mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #29772 - Loirooriol:sync, r=mrobinson
Backport several style changes from Gecko (2) <!-- Please describe your changes on the following line: --> This continues #29748. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
867326c46a
146 changed files with 2524 additions and 1683 deletions
|
@ -127,6 +127,7 @@ stroke-opacity
|
||||||
storage
|
storage
|
||||||
submit
|
submit
|
||||||
suspend
|
suspend
|
||||||
|
system-ui
|
||||||
tel
|
tel
|
||||||
text
|
text
|
||||||
time
|
time
|
||||||
|
|
|
@ -1387,6 +1387,8 @@ fn to_font_kit_family(font_family: &font::SingleFontFamily) -> FamilyName {
|
||||||
font::GenericFontFamily::Monospace => FamilyName::Monospace,
|
font::GenericFontFamily::Monospace => FamilyName::Monospace,
|
||||||
font::GenericFontFamily::Fantasy => FamilyName::Fantasy,
|
font::GenericFontFamily::Fantasy => FamilyName::Fantasy,
|
||||||
font::GenericFontFamily::Cursive => FamilyName::Cursive,
|
font::GenericFontFamily::Cursive => FamilyName::Cursive,
|
||||||
|
// TODO: There is no FontFamily::SystemUi.
|
||||||
|
font::GenericFontFamily::SystemUi => unreachable!("system-ui should be disabled"),
|
||||||
font::GenericFontFamily::None => unreachable!("Shouldn't appear in computed values"),
|
font::GenericFontFamily::None => unreachable!("Shouldn't appear in computed values"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -569,6 +569,7 @@ impl<'a> From<&'a SingleFontFamily> for FontFamilyName {
|
||||||
GenericFontFamily::Monospace => atom!("monospace"),
|
GenericFontFamily::Monospace => atom!("monospace"),
|
||||||
GenericFontFamily::Cursive => atom!("cursive"),
|
GenericFontFamily::Cursive => atom!("cursive"),
|
||||||
GenericFontFamily::Fantasy => atom!("fantasy"),
|
GenericFontFamily::Fantasy => atom!("fantasy"),
|
||||||
|
GenericFontFamily::SystemUi => atom!("system-ui"),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,9 @@ fn font_family(names: Vec<&str>) -> FontFamily {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
FontFamily {
|
FontFamily {
|
||||||
families: FontFamilyList::new(names.into_boxed_slice()),
|
families: FontFamilyList {
|
||||||
|
list: names.into_boxed_slice(),
|
||||||
|
},
|
||||||
is_system_font: false,
|
is_system_font: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -733,9 +733,9 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
|
||||||
hints.push(from_declaration(
|
hints.push(from_declaration(
|
||||||
shared_lock,
|
shared_lock,
|
||||||
PropertyDeclaration::FontFamily(font_family::SpecifiedValue::Values(
|
PropertyDeclaration::FontFamily(font_family::SpecifiedValue::Values(
|
||||||
computed::font::FontFamilyList::new(Box::new([
|
computed::font::FontFamilyList {
|
||||||
computed::font::SingleFontFamily::from_atom(font_family),
|
list: Box::new([computed::font::SingleFontFamily::from_atom(font_family)]),
|
||||||
])),
|
},
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ use crate::dom::node::{
|
||||||
use crate::dom::virtualmethods::VirtualMethods;
|
use crate::dom::virtualmethods::VirtualMethods;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use html5ever::{LocalName, Prefix};
|
use html5ever::{LocalName, Prefix};
|
||||||
use parking_lot::RwLock;
|
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
@ -112,15 +111,12 @@ impl HTMLMetaElement {
|
||||||
let shared_lock = document.style_shared_lock();
|
let shared_lock = document.style_shared_lock();
|
||||||
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
|
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
|
||||||
let sheet = Arc::new(Stylesheet {
|
let sheet = Arc::new(Stylesheet {
|
||||||
contents: StylesheetContents {
|
contents: StylesheetContents::from_shared_data(
|
||||||
rules: CssRules::new(vec![rule], shared_lock),
|
CssRules::new(vec![rule], shared_lock),
|
||||||
origin: Origin::Author,
|
Origin::Author,
|
||||||
namespaces: Default::default(),
|
window_from_node(self).get_url(),
|
||||||
quirks_mode: document.quirks_mode(),
|
document.quirks_mode(),
|
||||||
url_data: RwLock::new(window_from_node(self).get_url()),
|
),
|
||||||
source_map_url: RwLock::new(None),
|
|
||||||
source_url: RwLock::new(None),
|
|
||||||
},
|
|
||||||
media: Arc::new(shared_lock.wrap(MediaList::empty())),
|
media: Arc::new(shared_lock.wrap(MediaList::empty())),
|
||||||
shared_lock: shared_lock.clone(),
|
shared_lock: shared_lock.clone(),
|
||||||
disabled: AtomicBool::new(false),
|
disabled: AtomicBool::new(false),
|
||||||
|
|
|
@ -29,7 +29,6 @@ use net_traits::{
|
||||||
FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy,
|
FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy,
|
||||||
};
|
};
|
||||||
use net_traits::{ResourceFetchTiming, ResourceTimingType};
|
use net_traits::{ResourceFetchTiming, ResourceTimingType};
|
||||||
use parking_lot::RwLock;
|
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -41,9 +40,7 @@ use style::parser::ParserContext;
|
||||||
use style::shared_lock::{Locked, SharedRwLock};
|
use style::shared_lock::{Locked, SharedRwLock};
|
||||||
use style::stylesheets::import_rule::ImportSheet;
|
use style::stylesheets::import_rule::ImportSheet;
|
||||||
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||||
use style::stylesheets::{
|
use style::stylesheets::{CssRules, ImportRule, Origin, Stylesheet, StylesheetContents};
|
||||||
CssRules, ImportRule, Namespaces, Origin, Stylesheet, StylesheetContents,
|
|
||||||
};
|
|
||||||
use style::values::CssUrl;
|
use style::values::CssUrl;
|
||||||
|
|
||||||
pub trait StylesheetOwner {
|
pub trait StylesheetOwner {
|
||||||
|
@ -363,15 +360,12 @@ impl<'a> StyleStylesheetLoader for StylesheetLoader<'a> {
|
||||||
media: Arc<Locked<MediaList>>,
|
media: Arc<Locked<MediaList>>,
|
||||||
) -> Arc<Locked<ImportRule>> {
|
) -> Arc<Locked<ImportRule>> {
|
||||||
let sheet = Arc::new(Stylesheet {
|
let sheet = Arc::new(Stylesheet {
|
||||||
contents: StylesheetContents {
|
contents: StylesheetContents::from_shared_data(
|
||||||
rules: CssRules::new(Vec::new(), lock),
|
CssRules::new(Vec::new(), lock),
|
||||||
origin: context.stylesheet_origin,
|
context.stylesheet_origin,
|
||||||
url_data: RwLock::new(context.url_data.clone()),
|
context.url_data.clone(),
|
||||||
quirks_mode: context.quirks_mode,
|
context.quirks_mode,
|
||||||
namespaces: RwLock::new(Namespaces::default()),
|
),
|
||||||
source_map_url: RwLock::new(None),
|
|
||||||
source_url: RwLock::new(None),
|
|
||||||
},
|
|
||||||
media: media,
|
media: media,
|
||||||
shared_lock: lock.clone(),
|
shared_lock: lock.clone(),
|
||||||
disabled: AtomicBool::new(false),
|
disabled: AtomicBool::new(false),
|
||||||
|
|
|
@ -256,11 +256,6 @@ pub trait Parser<'i> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error recovery that selector lists inside :is() and :where() have.
|
|
||||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
|
||||||
ParseErrorRecovery::IgnoreInvalidSelector
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the given function name is an alias for the `:is()` function.
|
/// Whether the given function name is an alias for the `:is()` function.
|
||||||
fn is_is_alias(&self, _name: &str) -> bool {
|
fn is_is_alias(&self, _name: &str) -> bool {
|
||||||
false
|
false
|
||||||
|
@ -344,7 +339,7 @@ pub struct SelectorList<Impl: SelectorImpl>(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// How to treat invalid selectors in a selector list.
|
/// How to treat invalid selectors in a selector list.
|
||||||
pub enum ParseErrorRecovery {
|
enum ParseErrorRecovery {
|
||||||
/// Discard the entire selector list, this is the default behavior for
|
/// Discard the entire selector list, this is the default behavior for
|
||||||
/// almost all of CSS.
|
/// almost all of CSS.
|
||||||
DiscardList,
|
DiscardList,
|
||||||
|
@ -2277,7 +2272,7 @@ where
|
||||||
state |
|
state |
|
||||||
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||||
SelectorParsingState::DISALLOW_PSEUDOS,
|
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||||
parser.is_and_where_error_recovery(),
|
ParseErrorRecovery::IgnoreInvalidSelector,
|
||||||
)?;
|
)?;
|
||||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
@ -2686,10 +2681,6 @@ pub mod tests {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
|
||||||
ParseErrorRecovery::DiscardList
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_part(&self) -> bool {
|
fn parse_part(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -3294,9 +3285,9 @@ pub mod tests {
|
||||||
assert!(parse("::slotted(div)::before").is_ok());
|
assert!(parse("::slotted(div)::before").is_ok());
|
||||||
assert!(parse("slot::slotted(div,foo)").is_err());
|
assert!(parse("slot::slotted(div,foo)").is_err());
|
||||||
|
|
||||||
assert!(parse("foo:where()").is_err());
|
assert!(parse("foo:where()").is_ok());
|
||||||
assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
|
assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
|
||||||
assert!(parse("foo:where(::before)").is_err());
|
assert!(parse_expected("foo:where(::before)", Some("foo:where()")).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -248,6 +248,14 @@ impl<T> Arc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like from_raw, but returns an addrefed arc instead.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self {
|
||||||
|
let arc = Self::from_raw(ptr);
|
||||||
|
mem::forget(arc.clone());
|
||||||
|
arc
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new static Arc<T> (one that won't reference count the object)
|
/// Create a new static Arc<T> (one that won't reference count the object)
|
||||||
/// and place it in the allocation provided by the specified `alloc`
|
/// and place it in the allocation provided by the specified `alloc`
|
||||||
/// function.
|
/// function.
|
||||||
|
|
|
@ -27,7 +27,6 @@ servo-layout-2013 = []
|
||||||
servo-layout-2020 = []
|
servo-layout-2020 = []
|
||||||
gecko_debug = []
|
gecko_debug = []
|
||||||
gecko_refcount_logging = []
|
gecko_refcount_logging = []
|
||||||
gecko_profiler = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
|
|
|
@ -46,11 +46,15 @@ lazy_static! {
|
||||||
.join("layout/style/ServoBindings.toml");
|
.join("layout/style/ServoBindings.toml");
|
||||||
read_config(&path)
|
read_config(&path)
|
||||||
};
|
};
|
||||||
static ref BUILD_CONFIG: Table = {
|
static ref BINDGEN_FLAGS: Vec<String> = {
|
||||||
// Load build-specific config overrides.
|
// Load build-specific config overrides.
|
||||||
let path = PathBuf::from(env::var_os("MOZ_TOPOBJDIR").unwrap())
|
let path = PathBuf::from(env::var_os("MOZ_TOPOBJDIR").unwrap())
|
||||||
.join("layout/style/bindgen.toml");
|
.join("layout/style/extra-bindgen-flags");
|
||||||
read_config(&path)
|
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
||||||
|
fs::read_to_string(path).expect("Failed to read extra-bindgen-flags file")
|
||||||
|
.split_whitespace()
|
||||||
|
.map(std::borrow::ToOwned::to_owned)
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap();
|
static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap();
|
||||||
static ref DISTDIR_PATH: PathBuf = {
|
static ref DISTDIR_PATH: PathBuf = {
|
||||||
|
@ -159,12 +163,8 @@ impl BuilderExt for Builder {
|
||||||
builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1");
|
builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1");
|
||||||
}
|
}
|
||||||
|
|
||||||
let build_config = BUILD_CONFIG["build"]
|
for item in &*BINDGEN_FLAGS {
|
||||||
.as_table()
|
builder = builder.clang_arg(item);
|
||||||
.expect("Malformed config file");
|
|
||||||
let extra_bindgen_flags = build_config["args"].as_array().unwrap().as_slice();
|
|
||||||
for item in extra_bindgen_flags.iter() {
|
|
||||||
builder = builder.clang_arg(item.as_str().expect("Expect string in list"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
|
|
|
@ -55,7 +55,12 @@ pub fn parse_counter_style_name<'i, 't>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_name_definition(ident: &CustomIdent) -> bool {
|
fn is_valid_name_definition(ident: &CustomIdent) -> bool {
|
||||||
ident.0 != atom!("decimal") && ident.0 != atom!("disc")
|
ident.0 != atom!("decimal")
|
||||||
|
&& ident.0 != atom!("disc")
|
||||||
|
&& ident.0 != atom!("circle")
|
||||||
|
&& ident.0 != atom!("square")
|
||||||
|
&& ident.0 != atom!("disclosure-closed")
|
||||||
|
&& ident.0 != atom!("disclosure-open")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the prelude of an @counter-style rule
|
/// Parse the prelude of an @counter-style rule
|
||||||
|
|
|
@ -88,7 +88,7 @@ pub type Name = Atom;
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>
|
/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>
|
||||||
pub fn parse_name(s: &str) -> Result<&str, ()> {
|
pub fn parse_name(s: &str) -> Result<&str, ()> {
|
||||||
if s.starts_with("--") {
|
if s.starts_with("--") && s.len() > 2 {
|
||||||
Ok(&s[2..])
|
Ok(&s[2..])
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
|
@ -318,11 +318,6 @@ fn parse_declaration_value<'i, 't>(
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
// Need at least one token
|
|
||||||
let start = input.state();
|
|
||||||
input.next_including_whitespace()?;
|
|
||||||
input.reset(&start);
|
|
||||||
|
|
||||||
parse_declaration_value_block(input, references, missing_closing_characters)
|
parse_declaration_value_block(input, references, missing_closing_characters)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -334,6 +329,7 @@ fn parse_declaration_value_block<'i, 't>(
|
||||||
mut references: Option<&mut VarOrEnvReferences>,
|
mut references: Option<&mut VarOrEnvReferences>,
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
|
input.skip_whitespace();
|
||||||
let mut token_start = input.position();
|
let mut token_start = input.position();
|
||||||
let mut token = match input.next_including_whitespace_and_comments() {
|
let mut token = match input.next_including_whitespace_and_comments() {
|
||||||
Ok(token) => token,
|
Ok(token) => token,
|
||||||
|
@ -477,10 +473,8 @@ fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'
|
||||||
// Exclude `!` and `;` at the top level
|
// Exclude `!` and `;` at the top level
|
||||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
// At least one non-comment token.
|
|
||||||
input.next_including_whitespace()?;
|
|
||||||
// Skip until the end.
|
// Skip until the end.
|
||||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
while input.next_including_whitespace_and_comments().is_ok() {}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -996,12 +990,12 @@ fn substitute_block<'i>(
|
||||||
while input.next().is_ok() {}
|
while input.next().is_ok() {}
|
||||||
} else {
|
} else {
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
|
input.skip_whitespace();
|
||||||
let after_comma = input.state();
|
let after_comma = input.state();
|
||||||
let first_token_type = input
|
let first_token_type = input
|
||||||
.next_including_whitespace_and_comments()
|
.next_including_whitespace_and_comments()
|
||||||
// parse_var_function() ensures that .unwrap() will not fail.
|
.ok()
|
||||||
.unwrap()
|
.map_or_else(TokenSerializationType::nothing, |t| t.serialization_type());
|
||||||
.serialization_type();
|
|
||||||
input.reset(&after_comma);
|
input.reset(&after_comma);
|
||||||
let mut position = (after_comma.position(), first_token_type);
|
let mut position = (after_comma.position(), first_token_type);
|
||||||
last_token_type = substitute_block(
|
last_token_type = substitute_block(
|
||||||
|
|
|
@ -175,6 +175,25 @@ impl ElementStyles {
|
||||||
self.primary().get_box().clone_display().is_none()
|
self.primary().get_box().clone_display().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this element uses viewport units.
|
||||||
|
pub fn uses_viewport_units(&self) -> bool {
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
|
|
||||||
|
if self.primary().flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for pseudo_style in self.pseudos.as_array() {
|
||||||
|
if let Some(ref pseudo_style) = pseudo_style {
|
||||||
|
if pseudo_style.flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||||
// As the method name suggests, we don't measures the ComputedValues
|
// As the method name suggests, we don't measures the ComputedValues
|
||||||
|
|
|
@ -151,7 +151,7 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
|
||||||
/// Get this node's first child.
|
/// Get this node's first child.
|
||||||
fn first_child(&self) -> Option<Self>;
|
fn first_child(&self) -> Option<Self>;
|
||||||
|
|
||||||
/// Get this node's first child.
|
/// Get this node's last child.
|
||||||
fn last_child(&self) -> Option<Self>;
|
fn last_child(&self) -> Option<Self>;
|
||||||
|
|
||||||
/// Get this node's previous sibling.
|
/// Get this node's previous sibling.
|
||||||
|
@ -881,7 +881,7 @@ pub trait TElement:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Could be more granular.
|
// TODO: Could be more granular.
|
||||||
if !shadow.host().exports_any_part() {
|
if !inner_shadow_host.exports_any_part() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
inner_shadow = shadow;
|
inner_shadow = shadow;
|
||||||
|
|
|
@ -140,7 +140,8 @@ where
|
||||||
// ensures that we process all the elements at a given depth before
|
// ensures that we process all the elements at a given depth before
|
||||||
// proceeding to the next depth, which is important for style sharing.
|
// proceeding to the next depth, which is important for style sharing.
|
||||||
rayon::scope_fifo(|scope| {
|
rayon::scope_fifo(|scope| {
|
||||||
profiler_label!(Style);
|
#[cfg(feature = "gecko")]
|
||||||
|
gecko_profiler_label!(Layout, StyleComputation);
|
||||||
parallel::traverse_nodes(
|
parallel::traverse_nodes(
|
||||||
drain,
|
drain,
|
||||||
DispatchMode::TailCall,
|
DispatchMode::TailCall,
|
||||||
|
|
|
@ -56,10 +56,17 @@ impl GeckoStyleSheet {
|
||||||
/// already holds a strong reference.
|
/// already holds a strong reference.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self {
|
pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self {
|
||||||
debug_assert!(!s.is_null());
|
assert!(!s.is_null());
|
||||||
GeckoStyleSheet(s)
|
GeckoStyleSheet(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// HACK(emilio): This is so that we can avoid crashing release due to
|
||||||
|
/// bug 1719963 and can hopefully get a useful report from fuzzers.
|
||||||
|
#[inline]
|
||||||
|
pub fn hack_is_null(&self) -> bool {
|
||||||
|
self.0.is_null()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the raw `StyleSheet` that we're wrapping.
|
/// Get the raw `StyleSheet` that we're wrapping.
|
||||||
pub fn raw(&self) -> &DomStyleSheet {
|
pub fn raw(&self) -> &DomStyleSheet {
|
||||||
unsafe { &*self.0 }
|
unsafe { &*self.0 }
|
||||||
|
|
|
@ -590,6 +590,23 @@ fn eval_moz_os_version(
|
||||||
query_value.as_ptr() == os_version
|
query_value.as_ptr() == os_version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_moz_windows_non_native_menus(
|
||||||
|
device: &Device,
|
||||||
|
query_value: Option<bool>,
|
||||||
|
_: Option<RangeOrOperator>,
|
||||||
|
) -> bool {
|
||||||
|
let use_non_native_menus = match static_prefs::pref!("browser.display.windows.non_native_menus") {
|
||||||
|
0 => false,
|
||||||
|
1 => true,
|
||||||
|
_ => {
|
||||||
|
eval_moz_os_version(device, Some(atom!("windows-win10")), None) &&
|
||||||
|
get_lnf_int_as_bool(bindings::LookAndFeel_IntID::WindowsDefaultTheme as i32)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
query_value.map_or(use_non_native_menus, |v| v == use_non_native_menus)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_lnf_int(int_id: i32) -> i32 {
|
fn get_lnf_int(int_id: i32) -> i32 {
|
||||||
unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
|
unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
|
||||||
}
|
}
|
||||||
|
@ -663,7 +680,7 @@ macro_rules! bool_pref_feature {
|
||||||
/// to support new types in these entries and (2) ensuring that either
|
/// to support new types in these entries and (2) ensuring that either
|
||||||
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
|
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
|
||||||
/// would be returned by the evaluator function could change.
|
/// would be returned by the evaluator function could change.
|
||||||
pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
pub static MEDIA_FEATURES: [MediaFeatureDescription; 60] = [
|
||||||
feature!(
|
feature!(
|
||||||
atom!("width"),
|
atom!("width"),
|
||||||
AllowsRanges::Yes,
|
AllowsRanges::Yes,
|
||||||
|
@ -879,6 +896,12 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
||||||
keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme),
|
keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme),
|
||||||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||||
),
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("-moz-windows-non-native-menus"),
|
||||||
|
AllowsRanges::No,
|
||||||
|
Evaluator::BoolInteger(eval_moz_windows_non_native_menus),
|
||||||
|
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||||
|
),
|
||||||
|
|
||||||
lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward),
|
lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward),
|
||||||
lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward),
|
lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward),
|
||||||
|
@ -903,10 +926,6 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
||||||
lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
|
lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
|
||||||
lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement),
|
lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement),
|
||||||
lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
|
lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
|
||||||
|
|
||||||
bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"),
|
bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"),
|
||||||
bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"),
|
|
||||||
bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"),
|
|
||||||
bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"),
|
|
||||||
bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"),
|
bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"),
|
||||||
];
|
];
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl Device {
|
||||||
document,
|
document,
|
||||||
default_values: ComputedValues::default_values(doc),
|
default_values: ComputedValues::default_values(doc),
|
||||||
root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
|
root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
|
||||||
body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
|
body_text_color: AtomicUsize::new(prefs.mColors.mDefault as usize),
|
||||||
used_root_font_size: AtomicBool::new(false),
|
used_root_font_size: AtomicBool::new(false),
|
||||||
used_font_metrics: AtomicBool::new(false),
|
used_font_metrics: AtomicBool::new(false),
|
||||||
used_viewport_size: AtomicBool::new(false),
|
used_viewport_size: AtomicBool::new(false),
|
||||||
|
@ -387,12 +387,12 @@ impl Device {
|
||||||
|
|
||||||
/// Returns the default background color.
|
/// Returns the default background color.
|
||||||
pub fn default_background_color(&self) -> RGBA {
|
pub fn default_background_color(&self) -> RGBA {
|
||||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
|
convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefaultBackground)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the default foreground color.
|
/// Returns the default foreground color.
|
||||||
pub fn default_color(&self) -> RGBA {
|
pub fn default_color(&self) -> RGBA {
|
||||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor)
|
convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current effective text zoom.
|
/// Returns the current effective text zoom.
|
||||||
|
|
|
@ -13,8 +13,6 @@ pub mod conversions;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod media_features;
|
pub mod media_features;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
pub mod profiler;
|
|
||||||
pub mod pseudo_element;
|
pub mod pseudo_element;
|
||||||
pub mod restyle_damage;
|
pub mod restyle_damage;
|
||||||
pub mod selector_parser;
|
pub mod selector_parser;
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
//! Gecko profiler support.
|
|
||||||
//!
|
|
||||||
//! Use the `profiler_label!` macro from macros.rs.
|
|
||||||
|
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
|
|
||||||
/// A label describing a category of work that style threads can perform.
|
|
||||||
pub enum ProfilerLabel {
|
|
||||||
/// Style computation.
|
|
||||||
Style,
|
|
||||||
/// Style sheet parsing.
|
|
||||||
Parse,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RAII object that constructs and destroys a C++ AutoProfilerLabel object
|
|
||||||
/// pointed to be the specified reference.
|
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
pub struct AutoProfilerLabel<'a>(&'a mut structs::AutoProfilerLabel);
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
impl<'a> AutoProfilerLabel<'a> {
|
|
||||||
/// Creates a new AutoProfilerLabel with the specified label type.
|
|
||||||
///
|
|
||||||
/// unsafe since the caller must ensure that `label` is allocated on the
|
|
||||||
/// stack.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn new(
|
|
||||||
label: &mut std::mem::MaybeUninit<structs::AutoProfilerLabel>,
|
|
||||||
label_type: ProfilerLabel,
|
|
||||||
) -> AutoProfilerLabel {
|
|
||||||
let category_pair = match label_type {
|
|
||||||
ProfilerLabel::Style => structs::JS::ProfilingCategoryPair_LAYOUT_StyleComputation,
|
|
||||||
ProfilerLabel::Parse => structs::JS::ProfilingCategoryPair_LAYOUT_CSSParsing,
|
|
||||||
};
|
|
||||||
structs::Gecko_Construct_AutoProfilerLabel(label.as_mut_ptr(), category_pair);
|
|
||||||
AutoProfilerLabel(&mut *label.as_mut_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
impl<'a> Drop for AutoProfilerLabel<'a> {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
structs::Gecko_Destroy_AutoProfilerLabel(self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether the Gecko profiler is currently active.
|
|
||||||
///
|
|
||||||
/// This implementation must be kept in sync with
|
|
||||||
/// `mozilla::profiler::detail::RacyFeatures::IsActive`.
|
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
#[inline]
|
|
||||||
pub fn profiler_is_active() -> bool {
|
|
||||||
use self::structs::profiler::detail;
|
|
||||||
use std::mem;
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
|
|
||||||
let active_and_features: &AtomicU32 =
|
|
||||||
unsafe { mem::transmute(&detail::RacyFeatures_sActiveAndFeatures) };
|
|
||||||
(active_and_features.load(Ordering::Relaxed) & detail::RacyFeatures_Active) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Always false when the Gecko profiler is disabled.
|
|
||||||
#[cfg(not(feature = "gecko_profiler"))]
|
|
||||||
#[inline]
|
|
||||||
pub fn profiler_is_active() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||||
use crate::values::{AtomIdent, AtomString};
|
use crate::values::{AtomIdent, AtomString};
|
||||||
use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
|
use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
|
||||||
use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
|
use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
|
||||||
use selectors::parser::{ParseErrorRecovery, SelectorParseErrorKind};
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use selectors::SelectorList;
|
use selectors::SelectorList;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
||||||
|
@ -311,15 +311,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
|
||||||
if static_prefs::pref!("layout.css.is-and-where-better-error-recovery.enabled") {
|
|
||||||
ParseErrorRecovery::IgnoreInvalidSelector
|
|
||||||
} else {
|
|
||||||
ParseErrorRecovery::DiscardList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_part(&self) -> bool {
|
fn parse_part(&self) -> bool {
|
||||||
true
|
true
|
||||||
|
|
|
@ -409,9 +409,11 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn prev_sibling(&self) -> Option<Self> {
|
fn prev_sibling(&self) -> Option<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::Gecko_GetPreviousSibling(self.0)
|
let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);
|
||||||
.as_ref()
|
if prev_or_last.0.mNextSibling.raw::<nsIContent>().is_null() {
|
||||||
.map(GeckoNode)
|
return None;
|
||||||
|
}
|
||||||
|
Some(prev_or_last)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -308,11 +308,6 @@ impl_threadsafe_refcount!(
|
||||||
bindings::Gecko_AddRefnsIURIArbitraryThread,
|
bindings::Gecko_AddRefnsIURIArbitraryThread,
|
||||||
bindings::Gecko_ReleasensIURIArbitraryThread
|
bindings::Gecko_ReleasensIURIArbitraryThread
|
||||||
);
|
);
|
||||||
impl_threadsafe_refcount!(
|
|
||||||
structs::SharedFontList,
|
|
||||||
bindings::Gecko_AddRefSharedFontListArbitraryThread,
|
|
||||||
bindings::Gecko_ReleaseSharedFontListArbitraryThread
|
|
||||||
);
|
|
||||||
impl_threadsafe_refcount!(
|
impl_threadsafe_refcount!(
|
||||||
structs::SheetLoadDataHolder,
|
structs::SheetLoadDataHolder,
|
||||||
bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
|
bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
|
||||||
|
|
|
@ -10,6 +10,8 @@ use crate::gecko_bindings::bindings;
|
||||||
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
|
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
|
||||||
use crate::shared_lock::SharedRwLock;
|
use crate::shared_lock::SharedRwLock;
|
||||||
use crate::thread_state;
|
use crate::thread_state;
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
use gecko_profiler;
|
||||||
use parking_lot::{RwLock, RwLockReadGuard};
|
use parking_lot::{RwLock, RwLockReadGuard};
|
||||||
use rayon;
|
use rayon;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -49,20 +51,16 @@ fn thread_startup(_index: usize) {
|
||||||
thread_state::initialize_layout_worker_thread();
|
thread_state::initialize_layout_worker_thread();
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
bindings::Gecko_SetJemallocThreadLocalArena(true);
|
bindings::Gecko_SetJemallocThreadLocalArena(true);
|
||||||
let name = thread_name(_index);
|
let name = thread_name(_index);
|
||||||
let name = CString::new(name).unwrap();
|
gecko_profiler::register_thread(&name);
|
||||||
// Gecko_RegisterProfilerThread copies the passed name here.
|
|
||||||
bindings::Gecko_RegisterProfilerThread(name.as_ptr());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn thread_shutdown(_: usize) {
|
fn thread_shutdown(_: usize) {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::Gecko_UnregisterProfilerThread();
|
gecko_profiler::unregister_thread();
|
||||||
bindings::Gecko_SetJemallocThreadLocalArena(false);
|
bindings::Gecko_SetJemallocThreadLocalArena(false);
|
||||||
}
|
}
|
||||||
ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
|
ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
|
|
@ -60,7 +60,12 @@ impl RestyleHint {
|
||||||
/// Returns whether this hint invalidates the element and all its
|
/// Returns whether this hint invalidates the element and all its
|
||||||
/// descendants.
|
/// descendants.
|
||||||
pub fn contains_subtree(&self) -> bool {
|
pub fn contains_subtree(&self) -> bool {
|
||||||
self.contains(RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS)
|
self.contains(Self::restyle_subtree())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether we'll recascade all of the descendants.
|
||||||
|
pub fn will_recascade_subtree(&self) -> bool {
|
||||||
|
self.contains_subtree() || self.contains(Self::recascade_subtree())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether we need to restyle this element.
|
/// Returns whether we need to restyle this element.
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
pub mod element;
|
pub mod element;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
pub mod stylesheets;
|
pub mod stylesheets;
|
||||||
|
pub mod viewport_units;
|
||||||
|
|
57
components/style/invalidation/viewport_units.rs
Normal file
57
components/style/invalidation/viewport_units.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Invalidates style of all elements that depend on viewport units.
|
||||||
|
|
||||||
|
use crate::dom::{TElement, TNode};
|
||||||
|
use crate::invalidation::element::restyle_hints::RestyleHint;
|
||||||
|
|
||||||
|
/// Invalidates style of all elements that depend on viewport units.
|
||||||
|
///
|
||||||
|
/// Returns whether any element was invalidated.
|
||||||
|
pub fn invalidate<E>(root: E) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
debug!("invalidation::viewport_units::invalidate({:?})", root);
|
||||||
|
invalidate_recursively(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalidate_recursively<E>(element: E) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
let mut data = match element.mutate_data() {
|
||||||
|
Some(data) => data,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if data.hint.will_recascade_subtree() {
|
||||||
|
debug!("invalidate_recursively: {:?} was already invalid", element);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uses_viewport_units = data.styles.uses_viewport_units();
|
||||||
|
if uses_viewport_units {
|
||||||
|
debug!("invalidate_recursively: {:?} uses viewport units", element);
|
||||||
|
data.hint.insert(RestyleHint::RECASCADE_SELF);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut any_children_invalid = false;
|
||||||
|
for child in element.traversal_children() {
|
||||||
|
if let Some(child) = child.as_element() {
|
||||||
|
any_children_invalid |= invalidate_recursively(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if any_children_invalid {
|
||||||
|
debug!(
|
||||||
|
"invalidate_recursively: Children of {:?} changed, setting dirty descendants",
|
||||||
|
element
|
||||||
|
);
|
||||||
|
unsafe { element.set_dirty_descendants() }
|
||||||
|
}
|
||||||
|
|
||||||
|
uses_viewport_units || any_children_invalid
|
||||||
|
}
|
|
@ -33,6 +33,9 @@ extern crate cssparser;
|
||||||
extern crate debug_unreachable;
|
extern crate debug_unreachable;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate derive_more;
|
extern crate derive_more;
|
||||||
|
#[macro_use]
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
extern crate gecko_profiler;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod gecko_string_cache;
|
pub mod gecko_string_cache;
|
||||||
|
|
|
@ -105,35 +105,6 @@ macro_rules! define_keyword_type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Place a Gecko profiler label on the stack.
|
|
||||||
///
|
|
||||||
/// The `label_type` argument must be the name of a variant of `ProfilerLabel`.
|
|
||||||
#[cfg(feature = "gecko_profiler")]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! profiler_label {
|
|
||||||
($label_type:ident) => {
|
|
||||||
let mut _profiler_label =
|
|
||||||
::std::mem::MaybeUninit::<$crate::gecko_bindings::structs::AutoProfilerLabel>::uninit();
|
|
||||||
let _profiler_label = if $crate::gecko::profiler::profiler_is_active() {
|
|
||||||
unsafe {
|
|
||||||
Some($crate::gecko::profiler::AutoProfilerLabel::new(
|
|
||||||
&mut _profiler_label,
|
|
||||||
$crate::gecko::profiler::ProfilerLabel::$label_type,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// No-op when the Gecko profiler is not available.
|
|
||||||
#[cfg(not(feature = "gecko_profiler"))]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! profiler_label {
|
|
||||||
($label_type:ident) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
macro_rules! local_name {
|
macro_rules! local_name {
|
||||||
($s:tt) => {
|
($s:tt) => {
|
||||||
|
|
|
@ -274,7 +274,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>(
|
||||||
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
||||||
} else {
|
} else {
|
||||||
scope.spawn_fifo(move |scope| {
|
scope.spawn_fifo(move |scope| {
|
||||||
profiler_label!(Style);
|
#[cfg(feature = "gecko")]
|
||||||
|
gecko_profiler_label!(Layout, StyleComputation);
|
||||||
let work = work;
|
let work = work;
|
||||||
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
||||||
});
|
});
|
||||||
|
@ -284,7 +285,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>(
|
||||||
let nodes: WorkUnit<E::ConcreteNode> = chunk.collect();
|
let nodes: WorkUnit<E::ConcreteNode> = chunk.collect();
|
||||||
let traversal_data_copy = traversal_data.clone();
|
let traversal_data_copy = traversal_data.clone();
|
||||||
scope.spawn_fifo(move |scope| {
|
scope.spawn_fifo(move |scope| {
|
||||||
profiler_label!(Style);
|
#[cfg(feature = "gecko")]
|
||||||
|
gecko_profiler_label!(Layout, StyleComputation);
|
||||||
let n = nodes;
|
let n = nodes;
|
||||||
top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
|
top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
|
||||||
});
|
});
|
||||||
|
|
|
@ -464,7 +464,22 @@ fn tweak_when_ignoring_colors(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {
|
||||||
|
// We honor transparent and system colors more generally for all
|
||||||
|
// colors.
|
||||||
|
//
|
||||||
|
// NOTE(emilio): This doesn't handle caret-color and
|
||||||
|
// accent-color because those use a slightly different syntax
|
||||||
|
// (<color> | auto for example). That's probably fine though, as
|
||||||
|
// using a system color for caret-color doesn't make sense (using
|
||||||
|
// currentColor is fine), and we ignore accent-color in
|
||||||
|
// high-contrast-mode anyways.
|
||||||
|
if let Some(color) = declaration.color_value() {
|
||||||
|
if color.is_system() || alpha_channel(color, context) == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
*declaration.to_mut() =
|
*declaration.to_mut() =
|
||||||
|
@ -786,6 +801,35 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
|
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.author_specified
|
||||||
|
.contains(LonghandId::FontFamily)
|
||||||
|
{
|
||||||
|
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.author_specified
|
||||||
|
.contains(LonghandId::LetterSpacing)
|
||||||
|
{
|
||||||
|
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.author_specified
|
||||||
|
.contains(LonghandId::WordSpacing)
|
||||||
|
{
|
||||||
|
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
if self
|
||||||
|
.author_specified
|
||||||
|
.contains(LonghandId::FontSynthesis)
|
||||||
|
{
|
||||||
|
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
{
|
{
|
||||||
if let Some(font) = builder.get_font_if_mutated() {
|
if let Some(font) = builder.get_font_if_mutated() {
|
||||||
|
@ -849,18 +893,19 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
|
|
||||||
// System fonts are all right, and should have the default font type
|
// System fonts are all right, and should have the default font type
|
||||||
// set to none already, so bail out early.
|
// set to none already, so bail out early.
|
||||||
if font.mFont.systemFont {
|
if font.mFont.family.is_system_font {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
font.mFont.fontlist.mDefaultFontType,
|
font.mFont.family.families.fallback,
|
||||||
GenericFontFamily::None
|
GenericFontFamily::None
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let generic = font.mFont.family.families.single_generic().unwrap_or(GenericFontFamily::None);
|
||||||
let default_font_type = unsafe {
|
let default_font_type = unsafe {
|
||||||
bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
|
bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
|
||||||
builder.device.document(),
|
builder.device.document(),
|
||||||
font.mGenericID,
|
generic,
|
||||||
font.mLanguage.mRawPtr,
|
font.mLanguage.mRawPtr,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -870,15 +915,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
// cursive or fantasy, since they're ignored, see bug 789788), and
|
// cursive or fantasy, since they're ignored, see bug 789788), and
|
||||||
// we have a generic family to actually replace it with.
|
// we have a generic family to actually replace it with.
|
||||||
let prioritize_user_fonts = !use_document_fonts &&
|
let prioritize_user_fonts = !use_document_fonts &&
|
||||||
|
default_font_type != GenericFontFamily::None &&
|
||||||
matches!(
|
matches!(
|
||||||
font.mGenericID,
|
generic,
|
||||||
GenericFontFamily::None |
|
GenericFontFamily::None |
|
||||||
GenericFontFamily::Fantasy |
|
GenericFontFamily::Fantasy |
|
||||||
GenericFontFamily::Cursive
|
GenericFontFamily::Cursive
|
||||||
) &&
|
);
|
||||||
default_font_type != GenericFontFamily::None;
|
|
||||||
|
|
||||||
if !prioritize_user_fonts && default_font_type == font.mFont.fontlist.mDefaultFontType {
|
if !prioritize_user_fonts && default_font_type == font.mFont.family.families.fallback {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -886,9 +931,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let font = builder.mutate_font().gecko_mut();
|
let font = builder.mutate_font().gecko_mut();
|
||||||
font.mFont.fontlist.mDefaultFontType = default_font_type;
|
font.mFont.family.families.fallback = default_font_type;
|
||||||
if prioritize_user_fonts {
|
if prioritize_user_fonts {
|
||||||
unsafe { bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type) }
|
font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ bitflags! {
|
||||||
/// If we ever want to add some flags that shouldn't inherit for them,
|
/// If we ever want to add some flags that shouldn't inherit for them,
|
||||||
/// we might want to add a function to handle this.
|
/// we might want to add a function to handle this.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ComputedValueFlags: u16 {
|
pub struct ComputedValueFlags: u32 {
|
||||||
/// Whether the style or any of the ancestors has a text-decoration-line
|
/// Whether the style or any of the ancestors has a text-decoration-line
|
||||||
/// property that should get propagated to descendants.
|
/// property that should get propagated to descendants.
|
||||||
///
|
///
|
||||||
|
@ -89,6 +89,21 @@ bitflags! {
|
||||||
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
|
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
|
||||||
/// https://github.com/w3c/csswg-drafts/issues/4777
|
/// https://github.com/w3c/csswg-drafts/issues/4777
|
||||||
const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 15;
|
const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 15;
|
||||||
|
|
||||||
|
/// Whether there are author-specified rules for `font-family`.
|
||||||
|
const HAS_AUTHOR_SPECIFIED_FONT_FAMILY = 1 << 16;
|
||||||
|
|
||||||
|
/// Whether there are author-specified rules for `font-synthesis`.
|
||||||
|
const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS = 1 << 17;
|
||||||
|
|
||||||
|
/// Whether there are author-specified rules for `letter-spacing`.
|
||||||
|
const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 18;
|
||||||
|
|
||||||
|
/// Whether there are author-specified rules for `word-spacing`.
|
||||||
|
const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 19;
|
||||||
|
|
||||||
|
/// Whether the style depends on viewport units.
|
||||||
|
const USES_VIEWPORT_UNITS = 1 << 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
||||||
"-webkit-user-modify",
|
"-webkit-user-modify",
|
||||||
"-webkit-margin-before",
|
"-webkit-margin-before",
|
||||||
"-webkit-margin-after",
|
"-webkit-margin-after",
|
||||||
"tab-size",
|
|
||||||
"-webkit-margin-start",
|
"-webkit-margin-start",
|
||||||
"-webkit-column-break-inside",
|
"-webkit-column-break-inside",
|
||||||
"-webkit-padding-start",
|
"-webkit-padding-start",
|
||||||
|
@ -63,7 +62,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
||||||
"-webkit-text-combine",
|
"-webkit-text-combine",
|
||||||
"-webkit-text-emphasis-style",
|
"-webkit-text-emphasis-style",
|
||||||
"-webkit-text-emphasis",
|
"-webkit-text-emphasis",
|
||||||
"d",
|
|
||||||
"-webkit-mask-box-image-width",
|
"-webkit-mask-box-image-width",
|
||||||
"-webkit-mask-box-image-source",
|
"-webkit-mask-box-image-source",
|
||||||
"-webkit-mask-box-image-outset",
|
"-webkit-mask-box-image-outset",
|
||||||
|
@ -123,4 +121,5 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
||||||
"-webkit-columns",
|
"-webkit-columns",
|
||||||
"-webkit-column-rule-color",
|
"-webkit-column-rule-color",
|
||||||
"-webkit-shape-margin",
|
"-webkit-shape-margin",
|
||||||
|
"content-visibility",
|
||||||
]
|
]
|
||||||
|
|
|
@ -340,13 +340,14 @@ class Longhand(Property):
|
||||||
assert (
|
assert (
|
||||||
has_effect_on_gecko_scrollbars in [None, False, True]
|
has_effect_on_gecko_scrollbars in [None, False, True]
|
||||||
and not style_struct.inherited
|
and not style_struct.inherited
|
||||||
or (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None)
|
or (gecko_pref is None and enabled_in != "")
|
||||||
|
== (has_effect_on_gecko_scrollbars is None)
|
||||||
), (
|
), (
|
||||||
"Property "
|
"Property "
|
||||||
+ name
|
+ name
|
||||||
+ ": has_effect_on_gecko_scrollbars must be "
|
+ ": has_effect_on_gecko_scrollbars must be "
|
||||||
+ "specified, and must have a value of True or False, iff a "
|
+ "specified, and must have a value of True or False, iff a "
|
||||||
+ "property is inherited and is behind a Gecko pref"
|
+ "property is inherited and is behind a Gecko pref or internal"
|
||||||
)
|
)
|
||||||
self.need_index = need_index
|
self.need_index = need_index
|
||||||
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
||||||
|
@ -759,7 +760,7 @@ def _add_logical_props(data, props):
|
||||||
# These are probably Gecko bugs and should be supported per spec.
|
# These are probably Gecko bugs and should be supported per spec.
|
||||||
def _remove_common_first_line_and_first_letter_properties(props, engine):
|
def _remove_common_first_line_and_first_letter_properties(props, engine):
|
||||||
if engine == "gecko":
|
if engine == "gecko":
|
||||||
props.remove("-moz-tab-size")
|
props.remove("tab-size")
|
||||||
props.remove("hyphens")
|
props.remove("hyphens")
|
||||||
props.remove("line-break")
|
props.remove("line-break")
|
||||||
props.remove("text-align-last")
|
props.remove("text-align-last")
|
||||||
|
|
|
@ -385,7 +385,7 @@ impl PropertyDeclarationBlock {
|
||||||
// Step 1.2.3
|
// Step 1.2.3
|
||||||
// We don't print !important when serializing individual properties,
|
// We don't print !important when serializing individual properties,
|
||||||
// so we treat this as a normal-importance property
|
// so we treat this as a normal-importance property
|
||||||
match shorthand.get_shorthand_appendable_value(list.iter().cloned()) {
|
match shorthand.get_shorthand_appendable_value(&list) {
|
||||||
Some(appendable_value) => append_declaration_value(dest, appendable_value),
|
Some(appendable_value) => append_declaration_value(dest, appendable_value),
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
}
|
}
|
||||||
|
@ -911,9 +911,6 @@ impl PropertyDeclarationBlock {
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
|
/// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
|
||||||
pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
|
pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
|
||||||
use std::iter::Cloned;
|
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
let mut is_first_serialization = true; // trailing serializations should have a prepended space
|
let mut is_first_serialization = true; // trailing serializations should have a prepended space
|
||||||
|
|
||||||
// Step 1 -> dest = result list
|
// Step 1 -> dest = result list
|
||||||
|
@ -938,7 +935,7 @@ impl PropertyDeclarationBlock {
|
||||||
// properties in a declaration block, and that custom
|
// properties in a declaration block, and that custom
|
||||||
// properties can't be part of a shorthand, we can just care
|
// properties can't be part of a shorthand, we can just care
|
||||||
// about them here.
|
// about them here.
|
||||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
append_serialization(
|
||||||
dest,
|
dest,
|
||||||
&property,
|
&property,
|
||||||
AppendableValue::Declaration(declaration),
|
AppendableValue::Declaration(declaration),
|
||||||
|
@ -992,7 +989,7 @@ impl PropertyDeclarationBlock {
|
||||||
|
|
||||||
// Step 3.4.3:
|
// Step 3.4.3:
|
||||||
// Let current longhands be an empty array.
|
// Let current longhands be an empty array.
|
||||||
let mut current_longhands = SmallVec::<[_; 10]>::new();
|
let mut current_longhands = SmallVec::<[&_; 10]>::new();
|
||||||
let mut logical_groups = LogicalGroupSet::new();
|
let mut logical_groups = LogicalGroupSet::new();
|
||||||
let mut saw_one = false;
|
let mut saw_one = false;
|
||||||
let mut logical_mismatch = false;
|
let mut logical_mismatch = false;
|
||||||
|
@ -1066,7 +1063,7 @@ impl PropertyDeclarationBlock {
|
||||||
// Let value be the result of invoking serialize a CSS value
|
// Let value be the result of invoking serialize a CSS value
|
||||||
// of current longhands.
|
// of current longhands.
|
||||||
let appendable_value = match shorthand
|
let appendable_value = match shorthand
|
||||||
.get_shorthand_appendable_value(current_longhands.iter().cloned())
|
.get_shorthand_appendable_value(¤t_longhands)
|
||||||
{
|
{
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(appendable_value) => appendable_value,
|
Some(appendable_value) => appendable_value,
|
||||||
|
@ -1076,15 +1073,9 @@ impl PropertyDeclarationBlock {
|
||||||
// AppendableValue::Css.
|
// AppendableValue::Css.
|
||||||
let mut v = CssString::new();
|
let mut v = CssString::new();
|
||||||
let value = match appendable_value {
|
let value = match appendable_value {
|
||||||
AppendableValue::Css {
|
AppendableValue::Css(css) => {
|
||||||
css,
|
|
||||||
with_variables,
|
|
||||||
} => {
|
|
||||||
debug_assert!(!css.is_empty());
|
debug_assert!(!css.is_empty());
|
||||||
AppendableValue::Css {
|
appendable_value
|
||||||
css,
|
|
||||||
with_variables,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
other => {
|
other => {
|
||||||
append_declaration_value(&mut v, other)?;
|
append_declaration_value(&mut v, other)?;
|
||||||
|
@ -1096,14 +1087,13 @@ impl PropertyDeclarationBlock {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendableValue::Css {
|
AppendableValue::Css({
|
||||||
// Safety: serialization only generates valid utf-8.
|
// Safety: serialization only generates valid utf-8.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
css: unsafe { v.as_str_unchecked() },
|
unsafe { v.as_str_unchecked() }
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
css: &v,
|
&v
|
||||||
with_variables: false,
|
})
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1116,7 +1106,7 @@ impl PropertyDeclarationBlock {
|
||||||
//
|
//
|
||||||
// 3.4.10:
|
// 3.4.10:
|
||||||
// Append serialized declaration to list.
|
// Append serialized declaration to list.
|
||||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
append_serialization(
|
||||||
dest,
|
dest,
|
||||||
&shorthand,
|
&shorthand,
|
||||||
value,
|
value,
|
||||||
|
@ -1152,7 +1142,7 @@ impl PropertyDeclarationBlock {
|
||||||
// its important flag set.
|
// its important flag set.
|
||||||
//
|
//
|
||||||
// Append serialized declaration to list.
|
// Append serialized declaration to list.
|
||||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
append_serialization(
|
||||||
dest,
|
dest,
|
||||||
&property,
|
&property,
|
||||||
AppendableValue::Declaration(declaration),
|
AppendableValue::Declaration(declaration),
|
||||||
|
@ -1172,25 +1162,17 @@ impl PropertyDeclarationBlock {
|
||||||
|
|
||||||
/// A convenient enum to represent different kinds of stuff that can represent a
|
/// A convenient enum to represent different kinds of stuff that can represent a
|
||||||
/// _value_ in the serialization of a property declaration.
|
/// _value_ in the serialization of a property declaration.
|
||||||
pub enum AppendableValue<'a, I>
|
pub enum AppendableValue<'a, 'b: 'a> {
|
||||||
where
|
|
||||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
|
||||||
{
|
|
||||||
/// A given declaration, of which we'll serialize just the value.
|
/// A given declaration, of which we'll serialize just the value.
|
||||||
Declaration(&'a PropertyDeclaration),
|
Declaration(&'a PropertyDeclaration),
|
||||||
/// A set of declarations for a given shorthand.
|
/// A set of declarations for a given shorthand.
|
||||||
///
|
///
|
||||||
/// FIXME: This needs more docs, where are the shorthands expanded? We print
|
/// FIXME: This needs more docs, where are the shorthands expanded? We print
|
||||||
/// the property name before-hand, don't we?
|
/// the property name before-hand, don't we?
|
||||||
DeclarationsForShorthand(ShorthandId, I),
|
DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]),
|
||||||
/// A raw CSS string, coming for example from a property with CSS variables,
|
/// A raw CSS string, coming for example from a property with CSS variables,
|
||||||
/// or when storing a serialized shorthand value before appending directly.
|
/// or when storing a serialized shorthand value before appending directly.
|
||||||
Css {
|
Css(&'a str),
|
||||||
/// The raw CSS string.
|
|
||||||
css: &'a str,
|
|
||||||
/// Whether the original serialization contained variables or not.
|
|
||||||
with_variables: bool,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Potentially appends whitespace after the first (property: value;) pair.
|
/// Potentially appends whitespace after the first (property: value;) pair.
|
||||||
|
@ -1207,56 +1189,34 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a given kind of appendable value to a serialization.
|
/// Append a given kind of appendable value to a serialization.
|
||||||
pub fn append_declaration_value<'a, I>(
|
pub fn append_declaration_value<'a, 'b: 'a>(
|
||||||
dest: &mut CssStringWriter,
|
dest: &mut CssStringWriter,
|
||||||
appendable_value: AppendableValue<'a, I>,
|
appendable_value: AppendableValue<'a, 'b>,
|
||||||
) -> fmt::Result
|
) -> fmt::Result {
|
||||||
where
|
|
||||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
|
||||||
{
|
|
||||||
match appendable_value {
|
match appendable_value {
|
||||||
AppendableValue::Css { css, .. } => dest.write_str(css),
|
AppendableValue::Css(css) => dest.write_str(css),
|
||||||
AppendableValue::Declaration(decl) => decl.to_css(dest),
|
AppendableValue::Declaration(decl) => decl.to_css(dest),
|
||||||
AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
|
AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
|
||||||
shorthand.longhands_to_css(decls, &mut CssWriter::new(dest))
|
shorthand.longhands_to_css(decls, dest)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a given property and value pair to a serialization.
|
/// Append a given property and value pair to a serialization.
|
||||||
pub fn append_serialization<'a, I, N>(
|
pub fn append_serialization<'a, 'b: 'a, N>(
|
||||||
dest: &mut CssStringWriter,
|
dest: &mut CssStringWriter,
|
||||||
property_name: &N,
|
property_name: &N,
|
||||||
appendable_value: AppendableValue<'a, I>,
|
appendable_value: AppendableValue<'a, 'b>,
|
||||||
importance: Importance,
|
importance: Importance,
|
||||||
is_first_serialization: &mut bool,
|
is_first_serialization: &mut bool,
|
||||||
) -> fmt::Result
|
) -> fmt::Result
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
|
||||||
N: ToCss,
|
N: ToCss,
|
||||||
{
|
{
|
||||||
handle_first_serialization(dest, is_first_serialization)?;
|
handle_first_serialization(dest, is_first_serialization)?;
|
||||||
|
|
||||||
property_name.to_css(&mut CssWriter::new(dest))?;
|
property_name.to_css(&mut CssWriter::new(dest))?;
|
||||||
dest.write_char(':')?;
|
dest.write_str(": ")?;
|
||||||
|
|
||||||
// for normal parsed values, add a space between key: and value
|
|
||||||
match appendable_value {
|
|
||||||
AppendableValue::Declaration(decl) => {
|
|
||||||
if !decl.value_is_unparsed() {
|
|
||||||
// For normal parsed values, add a space between key: and value.
|
|
||||||
dest.write_str(" ")?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AppendableValue::Css { with_variables, .. } => {
|
|
||||||
if !with_variables {
|
|
||||||
dest.write_str(" ")?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Currently append_serialization is only called with a Css or
|
|
||||||
// a Declaration AppendableValue.
|
|
||||||
AppendableValue::DeclarationsForShorthand(..) => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
append_declaration_value(dest, appendable_value)?;
|
append_declaration_value(dest, appendable_value)?;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ff
|
||||||
use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
|
use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
|
||||||
% endfor
|
% endfor
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
|
use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
|
||||||
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
|
|
||||||
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
||||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
||||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
||||||
|
@ -799,11 +798,8 @@ fn static_assert() {
|
||||||
${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
|
${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
|
||||||
["-moz-outline-radius-{0}".format(x.replace("_", ""))
|
|
||||||
for x in CORNERS]) %>
|
|
||||||
<%self:impl_trait style_struct_name="Outline"
|
<%self:impl_trait style_struct_name="Outline"
|
||||||
skip_longhands="${skip_outline_longhands}">
|
skip_longhands="outline-style outline-width">
|
||||||
|
|
||||||
pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
|
pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
|
||||||
self.gecko.mOutlineStyle = v;
|
self.gecko.mOutlineStyle = v;
|
||||||
|
@ -831,12 +827,6 @@ fn static_assert() {
|
||||||
inherit_from="mOutlineWidth",
|
inherit_from="mOutlineWidth",
|
||||||
round_to_pixels=True) %>
|
round_to_pixels=True) %>
|
||||||
|
|
||||||
% for corner in CORNERS:
|
|
||||||
<% impl_corner_style_coord("_moz_outline_radius_%s" % corner.replace("_", ""),
|
|
||||||
"mOutlineRadius",
|
|
||||||
corner) %>
|
|
||||||
% endfor
|
|
||||||
|
|
||||||
pub fn outline_has_nonzero_width(&self) -> bool {
|
pub fn outline_has_nonzero_width(&self) -> bool {
|
||||||
self.gecko.mActualOutlineWidth != 0
|
self.gecko.mActualOutlineWidth != 0
|
||||||
}
|
}
|
||||||
|
@ -857,52 +847,6 @@ fn static_assert() {
|
||||||
<% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
|
<% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
|
||||||
<% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
|
<% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
|
||||||
|
|
||||||
pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
|
|
||||||
use crate::values::computed::font::GenericFontFamily;
|
|
||||||
|
|
||||||
let is_system_font = v.is_system_font;
|
|
||||||
self.gecko.mFont.systemFont = is_system_font;
|
|
||||||
self.gecko.mGenericID = if is_system_font {
|
|
||||||
GenericFontFamily::None
|
|
||||||
} else {
|
|
||||||
v.families.single_generic().unwrap_or(GenericFontFamily::None)
|
|
||||||
};
|
|
||||||
self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move(
|
|
||||||
v.families.shared_font_list().clone()
|
|
||||||
);
|
|
||||||
// Fixed-up if needed in Cascade::fixup_font_stuff.
|
|
||||||
self.gecko.mFont.fontlist.mDefaultFontType = GenericFontFamily::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_font_family_from(&mut self, other: &Self) {
|
|
||||||
unsafe { Gecko_CopyFontFamilyFrom(&mut self.gecko.mFont, &other.gecko.mFont); }
|
|
||||||
self.gecko.mGenericID = other.gecko.mGenericID;
|
|
||||||
self.gecko.mFont.systemFont = other.gecko.mFont.systemFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_font_family(&mut self, other: &Self) {
|
|
||||||
self.copy_font_family_from(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T {
|
|
||||||
use crate::values::computed::font::{FontFamily, SingleFontFamily, FontFamilyList};
|
|
||||||
|
|
||||||
let fontlist = &self.gecko.mFont.fontlist;
|
|
||||||
let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() };
|
|
||||||
|
|
||||||
let families = if shared_fontlist.mNames.is_empty() {
|
|
||||||
let default = SingleFontFamily::Generic(fontlist.mDefaultFontType);
|
|
||||||
FontFamilyList::new(Box::new([default]))
|
|
||||||
} else {
|
|
||||||
FontFamilyList::SharedFontList(shared_fontlist)
|
|
||||||
};
|
|
||||||
|
|
||||||
FontFamily {
|
|
||||||
families,
|
|
||||||
is_system_font: self.gecko.mFont.systemFont,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unzoom_fonts(&mut self, device: &Device) {
|
pub fn unzoom_fonts(&mut self, device: &Device) {
|
||||||
use crate::values::generics::NonNegative;
|
use crate::values::generics::NonNegative;
|
||||||
self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0));
|
self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0));
|
||||||
|
@ -1008,26 +952,9 @@ fn static_assert() {
|
||||||
|
|
||||||
${impl_simple("font_variant_alternates", "mFont.variantAlternates")}
|
${impl_simple("font_variant_alternates", "mFont.variantAlternates")}
|
||||||
|
|
||||||
pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) {
|
${impl_simple("font_size_adjust", "mFont.sizeAdjust")}
|
||||||
use crate::properties::longhands::font_size_adjust::computed_value::T;
|
|
||||||
match v {
|
|
||||||
T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32,
|
|
||||||
T::Number(n) => self.gecko.mFont.sizeAdjust = n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_font_size_adjust_from(&mut self, other: &Self) {
|
${impl_simple("font_family", "mFont.family")}
|
||||||
self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_font_size_adjust(&mut self, other: &Self) {
|
|
||||||
self.copy_font_size_adjust_from(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T {
|
|
||||||
use crate::properties::longhands::font_size_adjust::computed_value::T;
|
|
||||||
T::from_gecko_adjust(self.gecko.mFont.sizeAdjust)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
|
pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
|
||||||
|
|
|
@ -845,10 +845,7 @@
|
||||||
impl<'a> LonghandsToSerialize<'a> {
|
impl<'a> LonghandsToSerialize<'a> {
|
||||||
/// Tries to get a serializable set of longhands given a set of
|
/// Tries to get a serializable set of longhands given a set of
|
||||||
/// property declarations.
|
/// property declarations.
|
||||||
pub fn from_iter<I>(iter: I) -> Result<Self, ()>
|
pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {
|
||||||
where
|
|
||||||
I: Iterator<Item=&'a PropertyDeclaration>,
|
|
||||||
{
|
|
||||||
// Define all of the expected variables that correspond to the shorthand
|
// Define all of the expected variables that correspond to the shorthand
|
||||||
% for sub_property in shorthand.sub_properties:
|
% for sub_property in shorthand.sub_properties:
|
||||||
let mut ${sub_property.ident} =
|
let mut ${sub_property.ident} =
|
||||||
|
@ -856,8 +853,8 @@
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
// Attempt to assign the incoming declarations to the expected variables
|
// Attempt to assign the incoming declarations to the expected variables
|
||||||
for longhand in iter {
|
for declaration in iter {
|
||||||
match *longhand {
|
match *declaration {
|
||||||
% for sub_property in shorthand.sub_properties:
|
% for sub_property in shorthand.sub_properties:
|
||||||
PropertyDeclaration::${sub_property.camel_case}(ref value) => {
|
PropertyDeclaration::${sub_property.camel_case}(ref value) => {
|
||||||
${sub_property.ident} = Some(value)
|
${sub_property.ident} = Some(value)
|
||||||
|
@ -918,6 +915,14 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to serialize a given shorthand to a string.
|
||||||
|
pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result {
|
||||||
|
match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {
|
||||||
|
Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),
|
||||||
|
Err(_) => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
${caller.body()}
|
${caller.body()}
|
||||||
}
|
}
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -503,7 +503,6 @@ ${helpers.predefined_type(
|
||||||
"BreakWithin",
|
"BreakWithin",
|
||||||
"computed::BreakWithin::Auto",
|
"computed::BreakWithin::Auto",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
aliases="page-break-inside",
|
|
||||||
spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
|
spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -13,7 +13,6 @@ ${helpers.predefined_type(
|
||||||
engines="gecko servo-2013 servo-2020",
|
engines="gecko servo-2013 servo-2020",
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
servo_2020_pref="layout.2020.unimplemented",
|
||||||
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
|
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
animation_value_type="NonNegativeLengthOrAuto",
|
animation_value_type="NonNegativeLengthOrAuto",
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
|
||||||
|
@ -29,7 +28,6 @@ ${helpers.predefined_type(
|
||||||
initial_specified_value="specified::ColumnCount::auto()",
|
initial_specified_value="specified::ColumnCount::auto()",
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
animation_value_type="AnimatedColumnCount",
|
animation_value_type="AnimatedColumnCount",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
|
||||||
servo_restyle_damage="rebuild_and_reflow",
|
servo_restyle_damage="rebuild_and_reflow",
|
||||||
)}
|
)}
|
||||||
|
@ -38,7 +36,6 @@ ${helpers.single_keyword(
|
||||||
"column-fill",
|
"column-fill",
|
||||||
"balance auto",
|
"balance auto",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
gecko_enum_prefix="StyleColumnFill",
|
gecko_enum_prefix="StyleColumnFill",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
|
||||||
|
@ -53,7 +50,6 @@ ${helpers.predefined_type(
|
||||||
computed_type="crate::values::computed::NonNegativeLength",
|
computed_type="crate::values::computed::NonNegativeLength",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
|
||||||
animation_value_type="NonNegativeLength",
|
animation_value_type="NonNegativeLength",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-multicol-1/#crc
|
// https://drafts.csswg.org/css-multicol-1/#crc
|
||||||
|
@ -64,7 +60,6 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
initial_specified_value="specified::Color::currentcolor()",
|
initial_specified_value="specified::Color::currentcolor()",
|
||||||
animation_value_type="AnimatedColor",
|
animation_value_type="AnimatedColor",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
ignored_when_colors_disabled=True,
|
ignored_when_colors_disabled=True,
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
||||||
)}
|
)}
|
||||||
|
@ -76,7 +71,6 @@ ${helpers.single_keyword(
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
gecko_enum_prefix="StyleColumnSpan",
|
gecko_enum_prefix="StyleColumnSpan",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
@ -85,7 +79,6 @@ ${helpers.predefined_type(
|
||||||
"computed::BorderStyle::None",
|
"computed::BorderStyle::None",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
initial_specified_value="specified::BorderStyle::None",
|
initial_specified_value="specified::BorderStyle::None",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -213,6 +213,7 @@ ${helpers.predefined_type(
|
||||||
initial_value="computed::XLang::get_initial_value()",
|
initial_value="computed::XLang::get_initial_value()",
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
enabled_in="",
|
enabled_in="",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
spec="Internal (not web-exposed)",
|
spec="Internal (not web-exposed)",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -224,6 +225,7 @@ ${helpers.predefined_type(
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
gecko_ffi_name="mScriptSizeMultiplier",
|
gecko_ffi_name="mScriptSizeMultiplier",
|
||||||
enabled_in="",
|
enabled_in="",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
spec="Internal (not web-exposed)",
|
spec="Internal (not web-exposed)",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -263,6 +265,7 @@ ${helpers.single_keyword(
|
||||||
spec="Internal (not web-exposed)",
|
spec="Internal (not web-exposed)",
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
enabled_in="",
|
enabled_in="",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
needs_conversion=True,
|
needs_conversion=True,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -273,6 +276,7 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
enabled_in="",
|
enabled_in="",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
gecko_ffi_name="mScriptMinSize",
|
gecko_ffi_name="mScriptMinSize",
|
||||||
spec="Internal (not web-exposed)",
|
spec="Internal (not web-exposed)",
|
||||||
)}
|
)}
|
||||||
|
@ -284,6 +288,7 @@ ${helpers.predefined_type(
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
enabled_in="",
|
enabled_in="",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
spec="Internal (not web-exposed)",
|
spec="Internal (not web-exposed)",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -309,7 +314,6 @@ pub mod system_font {
|
||||||
//! variable reference. We may want to improve this behavior at some
|
//! variable reference. We may want to improve this behavior at some
|
||||||
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
|
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
|
||||||
|
|
||||||
use crate::values::computed::font::GenericFontFamily;
|
|
||||||
use crate::properties::longhands;
|
use crate::properties::longhands;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use crate::values::computed::{ToComputedValue, Context};
|
use crate::values::computed::{ToComputedValue, Context};
|
||||||
|
@ -351,7 +355,7 @@ pub mod system_font {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use crate::values::computed::Percentage;
|
use crate::values::computed::Percentage;
|
||||||
use crate::values::specified::font::KeywordInfo;
|
use crate::values::specified::font::KeywordInfo;
|
||||||
use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
|
use crate::values::computed::font::{FontSize, FontStretch, FontStyle};
|
||||||
use crate::values::generics::NonNegative;
|
use crate::values::generics::NonNegative;
|
||||||
|
|
||||||
let mut system = mem::MaybeUninit::<nsFont>::uninit();
|
let mut system = mem::MaybeUninit::<nsFont>::uninit();
|
||||||
|
@ -370,12 +374,7 @@ pub mod system_font {
|
||||||
})));
|
})));
|
||||||
let font_style = FontStyle::from_gecko(system.style);
|
let font_style = FontStyle::from_gecko(system.style);
|
||||||
let ret = ComputedSystemFont {
|
let ret = ComputedSystemFont {
|
||||||
font_family: FontFamily {
|
font_family: system.family.clone(),
|
||||||
families: FontFamilyList::SharedFontList(
|
|
||||||
unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
|
|
||||||
),
|
|
||||||
is_system_font: true,
|
|
||||||
},
|
|
||||||
font_size: FontSize {
|
font_size: FontSize {
|
||||||
size: NonNegative(cx.maybe_zoom_text(system.size.0)),
|
size: NonNegative(cx.maybe_zoom_text(system.size.0)),
|
||||||
keyword_info: KeywordInfo::none()
|
keyword_info: KeywordInfo::none()
|
||||||
|
@ -383,8 +382,7 @@ pub mod system_font {
|
||||||
font_weight,
|
font_weight,
|
||||||
font_stretch,
|
font_stretch,
|
||||||
font_style,
|
font_style,
|
||||||
font_size_adjust: longhands::font_size_adjust::computed_value
|
font_size_adjust: system.sizeAdjust,
|
||||||
::T::from_gecko_adjust(system.sizeAdjust),
|
|
||||||
% for kwprop in kw_font_props:
|
% for kwprop in kw_font_props:
|
||||||
${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
|
${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
|
||||||
system.${to_camel_case_lower(kwprop.replace('font_', ''))}
|
system.${to_camel_case_lower(kwprop.replace('font_', ''))}
|
||||||
|
@ -399,7 +397,6 @@ pub mod system_font {
|
||||||
font_variation_settings: longhands::font_variation_settings::get_initial_value(),
|
font_variation_settings: longhands::font_variation_settings::get_initial_value(),
|
||||||
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
|
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
|
||||||
system_font: *self,
|
system_font: *self,
|
||||||
default_font_type: system.fontlist.mDefaultFontType,
|
|
||||||
};
|
};
|
||||||
unsafe { bindings::Gecko_nsFont_Destroy(system); }
|
unsafe { bindings::Gecko_nsFont_Destroy(system); }
|
||||||
ret
|
ret
|
||||||
|
@ -431,7 +428,6 @@ pub mod system_font {
|
||||||
pub ${name}: longhands::${name}::computed_value::T,
|
pub ${name}: longhands::${name}::computed_value::T,
|
||||||
% endfor
|
% endfor
|
||||||
pub system_font: SystemFont,
|
pub system_font: SystemFont,
|
||||||
pub default_font_type: GenericFontFamily,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,9 @@ ${helpers.predefined_type(
|
||||||
"MozContextProperties",
|
"MozContextProperties",
|
||||||
"computed::MozContextProperties::default()",
|
"computed::MozContextProperties::default()",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
|
enabled_in="chrome",
|
||||||
|
gecko_pref="svg.context-properties.content.enabled",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
|
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -238,12 +238,13 @@ ${helpers.predefined_type(
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"-moz-tab-size",
|
"tab-size",
|
||||||
"NonNegativeLengthOrNumber",
|
"NonNegativeLengthOrNumber",
|
||||||
"generics::length::LengthOrNumber::Number(From::from(8.0))",
|
"generics::length::LengthOrNumber::Number(From::from(8.0))",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
animation_value_type="LengthOrNumber",
|
animation_value_type="LengthOrNumber",
|
||||||
spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
|
spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
|
||||||
|
aliases="-moz-tab-size",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
@ -336,6 +337,9 @@ ${helpers.predefined_type(
|
||||||
"text::MozControlCharacterVisibility",
|
"text::MozControlCharacterVisibility",
|
||||||
"Default::default()",
|
"Default::default()",
|
||||||
engines="gecko",
|
engines="gecko",
|
||||||
|
enabled_in="chrome",
|
||||||
|
gecko_pref="layout.css.moz-control-character-visibility.enabled",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
spec="Nonstandard"
|
spec="Nonstandard"
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -95,6 +95,17 @@ ${helpers.predefined_type(
|
||||||
has_effect_on_gecko_scrollbars=False,
|
has_effect_on_gecko_scrollbars=False,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
${helpers.predefined_type(
|
||||||
|
"color-scheme",
|
||||||
|
"ColorScheme",
|
||||||
|
"specified::color::ColorScheme::normal()",
|
||||||
|
engines="gecko",
|
||||||
|
spec="https://drafts.csswg.org/css-color-adjust/#color-scheme-prop",
|
||||||
|
gecko_pref="layout.css.color-scheme.enabled",
|
||||||
|
animation_value_type="discrete",
|
||||||
|
has_effect_on_gecko_scrollbars=False,
|
||||||
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"scrollbar-color",
|
"scrollbar-color",
|
||||||
"ui::ScrollbarColor",
|
"ui::ScrollbarColor",
|
||||||
|
|
|
@ -42,20 +42,6 @@ ${helpers.predefined_type(
|
||||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
|
spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// The -moz-outline-radius-* properties are non-standard and not on a standards track.
|
|
||||||
% for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
|
|
||||||
${helpers.predefined_type(
|
|
||||||
"-moz-outline-radius-" + corner,
|
|
||||||
"BorderCornerRadius",
|
|
||||||
"computed::BorderCornerRadius::zero()",
|
|
||||||
engines="gecko",
|
|
||||||
boxed=True,
|
|
||||||
animation_value_type="BorderCornerRadius",
|
|
||||||
gecko_pref="layout.css.moz-outline-radius.enabled",
|
|
||||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)",
|
|
||||||
)}
|
|
||||||
% endfor
|
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"outline-offset",
|
"outline-offset",
|
||||||
"Length",
|
"Length",
|
||||||
|
|
|
@ -434,7 +434,6 @@ ${helpers.predefined_type(
|
||||||
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
|
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013",
|
||||||
aliases="grid-column-gap" if engine == "gecko" else "",
|
aliases="grid-column-gap" if engine == "gecko" else "",
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
|
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
|
||||||
animation_value_type="NonNegativeLengthPercentageOrNormal",
|
animation_value_type="NonNegativeLengthPercentageOrNormal",
|
||||||
|
|
|
@ -246,3 +246,13 @@ ${helpers.predefined_type(
|
||||||
animation_value_type="LengthPercentage",
|
animation_value_type="LengthPercentage",
|
||||||
spec="https://svgwg.org/svg2-draft/geometry.html#R",
|
spec="https://svgwg.org/svg2-draft/geometry.html#R",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
${helpers.predefined_type(
|
||||||
|
"d",
|
||||||
|
"DProperty",
|
||||||
|
"specified::DProperty::none()",
|
||||||
|
engines="gecko",
|
||||||
|
animation_value_type="ComputedValue",
|
||||||
|
gecko_pref="layout.css.d-property.enabled",
|
||||||
|
spec="https://svgwg.org/svg2-draft/paths.html#TheDProperty",
|
||||||
|
)}
|
||||||
|
|
|
@ -232,7 +232,7 @@ pub mod shorthands {
|
||||||
// which don't exist in `LonghandId`.
|
// which don't exist in `LonghandId`.
|
||||||
|
|
||||||
<%
|
<%
|
||||||
extra = [
|
extra_variants = [
|
||||||
{
|
{
|
||||||
"name": "CSSWideKeyword",
|
"name": "CSSWideKeyword",
|
||||||
"type": "WideKeywordDeclaration",
|
"type": "WideKeywordDeclaration",
|
||||||
|
@ -252,7 +252,7 @@ pub mod shorthands {
|
||||||
"copy": False,
|
"copy": False,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
for v in extra:
|
for v in extra_variants:
|
||||||
variants.append(v)
|
variants.append(v)
|
||||||
groups[v["type"]] = [v]
|
groups[v["type"]] = [v]
|
||||||
%>
|
%>
|
||||||
|
@ -394,6 +394,17 @@ impl MallocSizeOf for PropertyDeclaration {
|
||||||
|
|
||||||
|
|
||||||
impl PropertyDeclaration {
|
impl PropertyDeclaration {
|
||||||
|
/// Returns whether this is a variant of the Longhand(Value) type, rather
|
||||||
|
/// than one of the special variants in extra_variants.
|
||||||
|
fn is_longhand_value(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
% for v in extra_variants:
|
||||||
|
PropertyDeclaration::${v["name"]}(..) => false,
|
||||||
|
% endfor
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Like the method on ToCss, but without the type parameter to avoid
|
/// Like the method on ToCss, but without the type parameter to avoid
|
||||||
/// accidentally monomorphizing this large function multiple times for
|
/// accidentally monomorphizing this large function multiple times for
|
||||||
/// different writers.
|
/// different writers.
|
||||||
|
@ -409,6 +420,24 @@ impl PropertyDeclaration {
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the color value of a given property, for high-contrast-mode
|
||||||
|
/// tweaks.
|
||||||
|
pub(crate) fn color_value(&self) -> Option<<&crate::values::specified::Color> {
|
||||||
|
${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")}
|
||||||
|
<%
|
||||||
|
# sanity check
|
||||||
|
assert data.longhands_by_name["background-color"].predefined_type == "Color"
|
||||||
|
|
||||||
|
color_specified_type = data.longhands_by_name["background-color"].specified_type()
|
||||||
|
%>
|
||||||
|
let id = self.id().as_longhand()?;
|
||||||
|
if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;
|
||||||
|
Some(unsafe { &(*repr).value })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A module with all the code related to animated properties.
|
/// A module with all the code related to animated properties.
|
||||||
|
@ -1366,6 +1395,9 @@ impl LonghandId {
|
||||||
|
|
||||||
// font-size depends on math-depth's computed value.
|
// font-size depends on math-depth's computed value.
|
||||||
LonghandId::MathDepth |
|
LonghandId::MathDepth |
|
||||||
|
|
||||||
|
// color-scheme affects how system colors resolve.
|
||||||
|
LonghandId::ColorScheme |
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
// Needed to compute the first available font, in order to
|
// Needed to compute the first available font, in order to
|
||||||
|
@ -1471,74 +1503,66 @@ impl ShorthandId {
|
||||||
///
|
///
|
||||||
/// Returns an error if writing to the stream fails, or if the declarations
|
/// Returns an error if writing to the stream fails, or if the declarations
|
||||||
/// do not map to a shorthand.
|
/// do not map to a shorthand.
|
||||||
pub fn longhands_to_css<'a, W, I>(
|
pub fn longhands_to_css(
|
||||||
&self,
|
&self,
|
||||||
declarations: I,
|
declarations: &[&PropertyDeclaration],
|
||||||
dest: &mut CssWriter<W>,
|
dest: &mut CssStringWriter,
|
||||||
) -> fmt::Result
|
) -> fmt::Result {
|
||||||
where
|
type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
|
||||||
W: Write,
|
fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
|
||||||
I: Iterator<Item=&'a PropertyDeclaration>,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
ShorthandId::All => {
|
|
||||||
// No need to try to serialize the declarations as the 'all'
|
// No need to try to serialize the declarations as the 'all'
|
||||||
// shorthand, since it only accepts CSS-wide keywords (and
|
// shorthand, since it only accepts CSS-wide keywords (and variable
|
||||||
// variable references), which will be handled in
|
// references), which will be handled in
|
||||||
// get_shorthand_appendable_value.
|
// get_shorthand_appendable_value.
|
||||||
Err(fmt::Error)
|
Ok(())
|
||||||
}
|
}
|
||||||
% for property in data.shorthands_except_all():
|
|
||||||
ShorthandId::${property.camel_case} => {
|
static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
|
||||||
match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) {
|
% for shorthand in data.shorthands:
|
||||||
Ok(longhands) => longhands.to_css(dest),
|
% if shorthand.ident == "all":
|
||||||
Err(_) => Err(fmt::Error)
|
all_to_css,
|
||||||
}
|
% else:
|
||||||
},
|
shorthands::${shorthand.ident}::to_css,
|
||||||
|
% endif
|
||||||
% endfor
|
% endfor
|
||||||
}
|
];
|
||||||
|
|
||||||
|
LONGHANDS_TO_CSS[*self as usize](declarations, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds and returns an appendable value for the given declarations.
|
/// Finds and returns an appendable value for the given declarations.
|
||||||
///
|
///
|
||||||
/// Returns the optional appendable value.
|
/// Returns the optional appendable value.
|
||||||
pub fn get_shorthand_appendable_value<'a, I>(
|
pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
|
||||||
self,
|
self,
|
||||||
declarations: I,
|
declarations: &'a [&'b PropertyDeclaration],
|
||||||
) -> Option<AppendableValue<'a, I::IntoIter>>
|
) -> Option<AppendableValue<'a, 'b>> {
|
||||||
where
|
let first_declaration = declarations.get(0)?;
|
||||||
I: IntoIterator<Item=&'a PropertyDeclaration>,
|
let rest = || declarations.iter().skip(1);
|
||||||
I::IntoIter: Clone,
|
|
||||||
{
|
|
||||||
let declarations = declarations.into_iter();
|
|
||||||
|
|
||||||
// Only cloning iterators (a few pointers each) not declarations.
|
|
||||||
let mut declarations2 = declarations.clone();
|
|
||||||
let mut declarations3 = declarations.clone();
|
|
||||||
|
|
||||||
let first_declaration = declarations2.next()?;
|
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
||||||
if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
|
if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
|
||||||
if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
||||||
return Some(AppendableValue::Css { css, with_variables: true });
|
return Some(AppendableValue::Css(css));
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether they are all the same CSS-wide keyword.
|
// Check whether they are all the same CSS-wide keyword.
|
||||||
if let Some(keyword) = first_declaration.get_css_wide_keyword() {
|
if let Some(keyword) = first_declaration.get_css_wide_keyword() {
|
||||||
if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
|
if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
|
||||||
return Some(AppendableValue::Css {
|
return Some(AppendableValue::Css(keyword.to_str()))
|
||||||
css: keyword.to_str(),
|
|
||||||
with_variables: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self == ShorthandId::All {
|
||||||
|
// 'all' only supports variables and CSS wide keywords.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether all declarations can be serialized as part of shorthand.
|
// Check whether all declarations can be serialized as part of shorthand.
|
||||||
if declarations3.all(|d| d.may_serialize_as_part_of_shorthand()) {
|
if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) {
|
||||||
return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
|
return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1596,23 +1620,20 @@ impl ShorthandId {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<(), ParseError<'i>>;
|
) -> Result<(), ParseError<'i>>;
|
||||||
|
|
||||||
fn unreachable<'i, 't>(
|
fn parse_all<'i, 't>(
|
||||||
_: &mut SourcePropertyDeclaration,
|
_: &mut SourcePropertyDeclaration,
|
||||||
_: &ParserContext,
|
_: &ParserContext,
|
||||||
_: &mut Parser<'i, 't>
|
input: &mut Parser<'i, 't>
|
||||||
) -> Result<(), ParseError<'i>> {
|
) -> Result<(), ParseError<'i>> {
|
||||||
unreachable!()
|
// 'all' accepts no value other than CSS-wide keywords
|
||||||
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'all' accepts no value other than CSS-wide keywords
|
|
||||||
if *self == ShorthandId::All {
|
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
|
||||||
}
|
|
||||||
|
|
||||||
static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
|
static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
|
||||||
% for shorthand in data.shorthands:
|
% for shorthand in data.shorthands:
|
||||||
% if shorthand.ident == "all":
|
% if shorthand.ident == "all":
|
||||||
unreachable,
|
parse_all,
|
||||||
% else:
|
% else:
|
||||||
shorthands::${shorthand.ident}::parse_into,
|
shorthands::${shorthand.ident}::parse_into,
|
||||||
% endif
|
% endif
|
||||||
|
@ -1720,7 +1741,8 @@ impl UnparsedValue {
|
||||||
|
|
||||||
let mut input = ParserInput::new(&css);
|
let mut input = ParserInput::new(&css);
|
||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
input.skip_whitespace();
|
||||||
|
|
||||||
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
||||||
return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
|
return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
|
||||||
}
|
}
|
||||||
|
@ -2436,12 +2458,11 @@ impl PropertyDeclaration {
|
||||||
debug_assert!(id.allowed_in(context), "{:?}", id);
|
debug_assert!(id.allowed_in(context), "{:?}", id);
|
||||||
|
|
||||||
let non_custom_id = id.non_custom_id();
|
let non_custom_id = id.non_custom_id();
|
||||||
|
input.skip_whitespace();
|
||||||
|
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
match id {
|
match id {
|
||||||
PropertyId::Custom(property_name) => {
|
PropertyId::Custom(property_name) => {
|
||||||
// FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774
|
|
||||||
// before adding skip_whitespace here.
|
|
||||||
// This probably affects some test results.
|
|
||||||
let value = match input.try_parse(CSSWideKeyword::parse) {
|
let value = match input.try_parse(CSSWideKeyword::parse) {
|
||||||
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
|
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
|
||||||
Err(()) => CustomDeclarationValue::Value(
|
Err(()) => CustomDeclarationValue::Value(
|
||||||
|
@ -2456,7 +2477,6 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
PropertyId::LonghandAlias(id, _) |
|
PropertyId::LonghandAlias(id, _) |
|
||||||
PropertyId::Longhand(id) => {
|
PropertyId::Longhand(id) => {
|
||||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
|
||||||
input.try_parse(CSSWideKeyword::parse).map(|keyword| {
|
input.try_parse(CSSWideKeyword::parse).map(|keyword| {
|
||||||
PropertyDeclaration::css_wide_keyword(id, keyword)
|
PropertyDeclaration::css_wide_keyword(id, keyword)
|
||||||
}).or_else(|()| {
|
}).or_else(|()| {
|
||||||
|
@ -2486,7 +2506,6 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
PropertyId::ShorthandAlias(id, _) |
|
PropertyId::ShorthandAlias(id, _) |
|
||||||
PropertyId::Shorthand(id) => {
|
PropertyId::Shorthand(id) => {
|
||||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
|
||||||
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
||||||
if id == ShorthandId::All {
|
if id == ShorthandId::All {
|
||||||
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
|
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
|
||||||
|
|
|
@ -316,15 +316,15 @@ ${helpers.two_properties_shorthand(
|
||||||
name="page-break-before"
|
name="page-break-before"
|
||||||
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||||
sub_properties="break-before"
|
sub_properties="break-before"
|
||||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before"
|
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||||
>
|
>
|
||||||
pub fn parse_value<'i>(
|
pub fn parse_value<'i>(
|
||||||
_: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, '_>,
|
input: &mut Parser<'i, '_>,
|
||||||
) -> Result<Longhands, ParseError<'i>> {
|
) -> Result<Longhands, ParseError<'i>> {
|
||||||
use crate::values::specified::box_::BreakBetween;
|
use crate::values::specified::box_::BreakBetween;
|
||||||
Ok(expanded! {
|
Ok(expanded! {
|
||||||
break_before: BreakBetween::parse_legacy(input)?,
|
break_before: BreakBetween::parse_legacy(context, input)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,15 +340,15 @@ ${helpers.two_properties_shorthand(
|
||||||
name="page-break-after"
|
name="page-break-after"
|
||||||
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||||
sub_properties="break-after"
|
sub_properties="break-after"
|
||||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after"
|
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||||
>
|
>
|
||||||
pub fn parse_value<'i>(
|
pub fn parse_value<'i>(
|
||||||
_: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, '_>,
|
input: &mut Parser<'i, '_>,
|
||||||
) -> Result<Longhands, ParseError<'i>> {
|
) -> Result<Longhands, ParseError<'i>> {
|
||||||
use crate::values::specified::box_::BreakBetween;
|
use crate::values::specified::box_::BreakBetween;
|
||||||
Ok(expanded! {
|
Ok(expanded! {
|
||||||
break_after: BreakBetween::parse_legacy(input)?,
|
break_after: BreakBetween::parse_legacy(context, input)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,6 +359,30 @@ ${helpers.two_properties_shorthand(
|
||||||
}
|
}
|
||||||
</%helpers:shorthand>
|
</%helpers:shorthand>
|
||||||
|
|
||||||
|
<%helpers:shorthand
|
||||||
|
engines="gecko"
|
||||||
|
name="page-break-inside"
|
||||||
|
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||||
|
sub_properties="break-inside"
|
||||||
|
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||||
|
>
|
||||||
|
pub fn parse_value<'i>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, '_>,
|
||||||
|
) -> Result<Longhands, ParseError<'i>> {
|
||||||
|
use crate::values::specified::box_::BreakWithin;
|
||||||
|
Ok(expanded! {
|
||||||
|
break_inside: BreakWithin::parse_legacy(context, input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||||
|
self.break_inside.to_css_legacy(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%helpers:shorthand>
|
||||||
|
|
||||||
<%helpers:shorthand name="offset"
|
<%helpers:shorthand name="offset"
|
||||||
engines="gecko"
|
engines="gecko"
|
||||||
sub_properties="offset-path offset-distance offset-rotate offset-anchor"
|
sub_properties="offset-path offset-distance offset-rotate offset-anchor"
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
sub_properties="column-width column-count"
|
sub_properties="column-width column-count"
|
||||||
servo_2013_pref="layout.columns.enabled",
|
servo_2013_pref="layout.columns.enabled",
|
||||||
derive_serialize="True"
|
derive_serialize="True"
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns"
|
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
||||||
use crate::properties::longhands::{column_count, column_width};
|
use crate::properties::longhands::{column_count, column_width};
|
||||||
|
|
||||||
|
@ -60,7 +59,6 @@
|
||||||
<%helpers:shorthand
|
<%helpers:shorthand
|
||||||
name="column-rule"
|
name="column-rule"
|
||||||
engines="gecko"
|
engines="gecko"
|
||||||
extra_prefixes="moz:layout.css.prefixes.columns"
|
|
||||||
sub_properties="column-rule-width column-rule-style column-rule-color"
|
sub_properties="column-rule-width column-rule-style column-rule-color"
|
||||||
derive_serialize="True"
|
derive_serialize="True"
|
||||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
|
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
|
||||||
|
|
|
@ -137,7 +137,7 @@
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
|
||||||
let family = FontFamily::parse_specified(input)?;
|
let family = FontFamily::parse(context, input)?;
|
||||||
Ok(expanded! {
|
Ok(expanded! {
|
||||||
% for name in "style weight stretch variant_caps".split():
|
% for name in "style weight stretch variant_caps".split():
|
||||||
font_${name}: unwrap_or_initial!(font_${name}, ${name}),
|
font_${name}: unwrap_or_initial!(font_${name}, ${name}),
|
||||||
|
|
|
@ -78,50 +78,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</%helpers:shorthand>
|
</%helpers:shorthand>
|
||||||
|
|
||||||
// The -moz-outline-radius shorthand is non-standard and not on a standards track.
|
|
||||||
<%helpers:shorthand
|
|
||||||
name="-moz-outline-radius"
|
|
||||||
engines="gecko"
|
|
||||||
gecko_pref="layout.css.moz-outline-radius.enabled"
|
|
||||||
sub_properties="${' '.join(
|
|
||||||
'-moz-outline-radius-%s' % corner
|
|
||||||
for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
|
|
||||||
)}"
|
|
||||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)"
|
|
||||||
>
|
|
||||||
use crate::values::generics::rect::Rect;
|
|
||||||
use crate::values::specified::border::BorderRadius;
|
|
||||||
use crate::parser::Parse;
|
|
||||||
|
|
||||||
pub fn parse_value<'i, 't>(
|
|
||||||
context: &ParserContext,
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
) -> Result<Longhands, ParseError<'i>> {
|
|
||||||
let radii = BorderRadius::parse(context, input)?;
|
|
||||||
Ok(expanded! {
|
|
||||||
_moz_outline_radius_topleft: radii.top_left,
|
|
||||||
_moz_outline_radius_topright: radii.top_right,
|
|
||||||
_moz_outline_radius_bottomright: radii.bottom_right,
|
|
||||||
_moz_outline_radius_bottomleft: radii.bottom_left,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
|
||||||
use crate::values::generics::border::BorderCornerRadius;
|
|
||||||
|
|
||||||
let LonghandsToSerialize {
|
|
||||||
_moz_outline_radius_topleft: &BorderCornerRadius(ref tl),
|
|
||||||
_moz_outline_radius_topright: &BorderCornerRadius(ref tr),
|
|
||||||
_moz_outline_radius_bottomright: &BorderCornerRadius(ref br),
|
|
||||||
_moz_outline_radius_bottomleft: &BorderCornerRadius(ref bl),
|
|
||||||
} = *self;
|
|
||||||
|
|
||||||
let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
|
|
||||||
let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
|
|
||||||
|
|
||||||
BorderRadius::serialize_rects(widths, heights, dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</%helpers:shorthand>
|
|
||||||
|
|
|
@ -232,9 +232,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
use crate::computed_values::list_style_position::T as ListStylePosition;
|
|
||||||
|
|
||||||
let mut blockify = false;
|
let mut blockify = false;
|
||||||
macro_rules! blockify_if {
|
macro_rules! blockify_if {
|
||||||
($if_what:expr) => {
|
($if_what:expr) => {
|
||||||
|
@ -254,16 +251,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
|
|
||||||
blockify_if!(self.style.is_floating());
|
blockify_if!(self.style.is_floating());
|
||||||
blockify_if!(self.style.is_absolutely_positioned());
|
blockify_if!(self.style.is_absolutely_positioned());
|
||||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
|
||||||
blockify_if!(
|
|
||||||
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
|
||||||
self.style.get_parent_list().clone_list_style_position() ==
|
|
||||||
ListStylePosition::Outside &&
|
|
||||||
!layout_parent_style
|
|
||||||
.get_box()
|
|
||||||
.clone_display()
|
|
||||||
.is_inline_flow()
|
|
||||||
);
|
|
||||||
|
|
||||||
if !blockify {
|
if !blockify {
|
||||||
return;
|
return;
|
||||||
|
@ -818,6 +805,46 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family'
|
||||||
|
/// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open'
|
||||||
|
/// is assigned 'font-family:-moz-bullet-font'. (This is for <ul><li> etc.)
|
||||||
|
/// We don't want synthesized italic/bold for this font, so turn that off too.
|
||||||
|
/// Likewise for 'letter/word-spacing' -- unless the author specified it then reset
|
||||||
|
/// them to their initial value because traditionally we never added such spacing
|
||||||
|
/// between a legacy bullet and the list item's content, so we keep that behavior
|
||||||
|
/// for web-compat reasons.
|
||||||
|
/// We intentionally don't check 'list-style-image' below since we want it to use
|
||||||
|
/// the same font as its fallback ('list-style-type') in case it fails to load.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn adjust_for_marker_pseudo(&mut self) {
|
||||||
|
use crate::values::computed::font::{FontFamily, FontSynthesis};
|
||||||
|
use crate::values::computed::text::{LetterSpacing, WordSpacing};
|
||||||
|
use crate::values::computed::counters::{Content};
|
||||||
|
|
||||||
|
let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
||||||
|
self.style.get_list().clone_list_style_type().is_bullet() &&
|
||||||
|
self.style.get_counters().clone_content() == Content::Normal;
|
||||||
|
if !is_legacy_marker {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
|
||||||
|
self.style.mutate_font().set_font_family(FontFamily::moz_bullet().clone());
|
||||||
|
|
||||||
|
// FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules.
|
||||||
|
// Then we can add it to the @font-face rule in html.css instead.
|
||||||
|
// https://github.com/w3c/csswg-drafts/issues/6081
|
||||||
|
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS) {
|
||||||
|
self.style.mutate_font().set_font_synthesis(FontSynthesis::none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
|
||||||
|
self.style.mutate_inherited_text().set_letter_spacing(LetterSpacing::normal());
|
||||||
|
}
|
||||||
|
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
|
||||||
|
self.style.mutate_inherited_text().set_word_spacing(WordSpacing::normal());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adjusts the style to account for various fixups that don't fit naturally
|
/// Adjusts the style to account for various fixups that don't fit naturally
|
||||||
/// into the cascade.
|
/// into the cascade.
|
||||||
///
|
///
|
||||||
|
@ -883,6 +910,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
{
|
{
|
||||||
self.adjust_for_appearance(element);
|
self.adjust_for_appearance(element);
|
||||||
self.adjust_for_inert();
|
self.adjust_for_inert();
|
||||||
|
self.adjust_for_marker_pseudo();
|
||||||
}
|
}
|
||||||
self.set_bits();
|
self.set_bits();
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,13 @@ impl ImportSheet {
|
||||||
/// exists.
|
/// exists.
|
||||||
pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
|
pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
|
||||||
match *self {
|
match *self {
|
||||||
ImportSheet::Sheet(ref s) => Some(s),
|
ImportSheet::Sheet(ref s) => {
|
||||||
|
debug_assert!(!s.hack_is_null());
|
||||||
|
if s.hack_is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(s)
|
||||||
|
},
|
||||||
ImportSheet::Pending(_) => None,
|
ImportSheet::Pending(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,10 @@ pub struct StylesheetContents {
|
||||||
pub source_map_url: RwLock<Option<String>>,
|
pub source_map_url: RwLock<Option<String>>,
|
||||||
/// This stylesheet's source URL.
|
/// This stylesheet's source URL.
|
||||||
pub source_url: RwLock<Option<String>>,
|
pub source_url: RwLock<Option<String>>,
|
||||||
|
|
||||||
|
/// We don't want to allow construction outside of this file, to guarantee
|
||||||
|
/// that all contents are created with Arc<>.
|
||||||
|
_forbid_construction: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StylesheetContents {
|
impl StylesheetContents {
|
||||||
|
@ -82,7 +86,7 @@ impl StylesheetContents {
|
||||||
use_counters: Option<&UseCounters>,
|
use_counters: Option<&UseCounters>,
|
||||||
allow_import_rules: AllowImportRules,
|
allow_import_rules: AllowImportRules,
|
||||||
sanitization_data: Option<&mut SanitizationData>,
|
sanitization_data: Option<&mut SanitizationData>,
|
||||||
) -> Self {
|
) -> Arc<Self> {
|
||||||
let namespaces = RwLock::new(Namespaces::default());
|
let namespaces = RwLock::new(Namespaces::default());
|
||||||
let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
|
let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
|
||||||
css,
|
css,
|
||||||
|
@ -99,7 +103,7 @@ impl StylesheetContents {
|
||||||
sanitization_data,
|
sanitization_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Arc::new(Self {
|
||||||
rules: CssRules::new(rules, &shared_lock),
|
rules: CssRules::new(rules, &shared_lock),
|
||||||
origin,
|
origin,
|
||||||
url_data: RwLock::new(url_data),
|
url_data: RwLock::new(url_data),
|
||||||
|
@ -107,7 +111,8 @@ impl StylesheetContents {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
source_map_url: RwLock::new(source_map_url),
|
source_map_url: RwLock::new(source_map_url),
|
||||||
source_url: RwLock::new(source_url),
|
source_url: RwLock::new(source_url),
|
||||||
}
|
_forbid_construction: (),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new StylesheetContents with the specified pre-parsed rules,
|
/// Creates a new StylesheetContents with the specified pre-parsed rules,
|
||||||
|
@ -126,9 +131,9 @@ impl StylesheetContents {
|
||||||
origin: Origin,
|
origin: Origin,
|
||||||
url_data: UrlExtraData,
|
url_data: UrlExtraData,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
) -> Self {
|
) -> Arc<Self> {
|
||||||
debug_assert!(rules.is_static());
|
debug_assert!(rules.is_static());
|
||||||
Self {
|
Arc::new(Self {
|
||||||
rules,
|
rules,
|
||||||
origin,
|
origin,
|
||||||
url_data: RwLock::new(url_data),
|
url_data: RwLock::new(url_data),
|
||||||
|
@ -136,7 +141,8 @@ impl StylesheetContents {
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
source_map_url: RwLock::new(None),
|
source_map_url: RwLock::new(None),
|
||||||
source_url: RwLock::new(None),
|
source_url: RwLock::new(None),
|
||||||
}
|
_forbid_construction: (),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the list of rules.
|
/// Returns a reference to the list of rules.
|
||||||
|
@ -178,6 +184,7 @@ impl DeepCloneWithLock for StylesheetContents {
|
||||||
namespaces: RwLock::new((*self.namespaces.read()).clone()),
|
namespaces: RwLock::new((*self.namespaces.read()).clone()),
|
||||||
source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
|
source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
|
||||||
source_url: RwLock::new((*self.source_url.read()).clone()),
|
source_url: RwLock::new((*self.source_url.read()).clone()),
|
||||||
|
_forbid_construction: (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +193,7 @@ impl DeepCloneWithLock for StylesheetContents {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stylesheet {
|
pub struct Stylesheet {
|
||||||
/// The contents of this stylesheet.
|
/// The contents of this stylesheet.
|
||||||
pub contents: StylesheetContents,
|
pub contents: Arc<StylesheetContents>,
|
||||||
/// The lock used for objects inside this stylesheet
|
/// The lock used for objects inside this stylesheet
|
||||||
pub shared_lock: SharedRwLock,
|
pub shared_lock: SharedRwLock,
|
||||||
/// List of media associated with the Stylesheet.
|
/// List of media associated with the Stylesheet.
|
||||||
|
@ -587,9 +594,9 @@ impl Clone for Stylesheet {
|
||||||
// Make a deep clone of the media, using the new lock.
|
// Make a deep clone of the media, using the new lock.
|
||||||
let media = self.media.read_with(&guard).clone();
|
let media = self.media.read_with(&guard).clone();
|
||||||
let media = Arc::new(lock.wrap(media));
|
let media = Arc::new(lock.wrap(media));
|
||||||
let contents = self
|
let contents = Arc::new(self
|
||||||
.contents
|
.contents
|
||||||
.deep_clone_with_lock(&lock, &guard, &DeepCloneParams);
|
.deep_clone_with_lock(&lock, &guard, &DeepCloneParams));
|
||||||
|
|
||||||
Stylesheet {
|
Stylesheet {
|
||||||
contents,
|
contents,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
|
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
|
||||||
use crate::invalidation::element::invalidation_map::InvalidationMap;
|
use crate::invalidation::element::invalidation_map::InvalidationMap;
|
||||||
use crate::invalidation::media_queries::EffectiveMediaQueryResults;
|
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, MediaListKey, ToMediaListKey};
|
||||||
use crate::invalidation::stylesheets::RuleChangeKind;
|
use crate::invalidation::stylesheets::RuleChangeKind;
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::properties::{self, CascadeMode, ComputedValues};
|
use crate::properties::{self, CascadeMode, ComputedValues};
|
||||||
|
@ -26,8 +26,7 @@ use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKin
|
||||||
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
|
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
|
||||||
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
|
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
|
||||||
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
|
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
|
||||||
use crate::stylesheets::StyleRule;
|
use crate::stylesheets::{StyleRule, StylesheetInDocument, StylesheetContents};
|
||||||
use crate::stylesheets::StylesheetInDocument;
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
|
use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
|
||||||
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
|
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
|
||||||
|
@ -50,7 +49,9 @@ use smallbitvec::SmallBitVec;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{mem, ops};
|
use std::{mem, ops};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
|
||||||
/// The type of the stylesheets that the stylist contains.
|
/// The type of the stylesheets that the stylist contains.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -60,6 +61,37 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
|
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct StylesheetContentsPtr(Arc<StylesheetContents>);
|
||||||
|
|
||||||
|
impl PartialEq for StylesheetContentsPtr {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Arc::ptr_eq(&self.0, &other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for StylesheetContentsPtr {}
|
||||||
|
|
||||||
|
impl Hash for StylesheetContentsPtr {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let contents: &StylesheetContents = &*self.0;
|
||||||
|
(contents as *const StylesheetContents).hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StyleSheetContentList = Vec<StylesheetContentsPtr>;
|
||||||
|
|
||||||
|
/// A key in the cascade data cache.
|
||||||
|
#[derive(Debug, Hash, Default, PartialEq, Eq)]
|
||||||
|
struct CascadeDataCacheKey {
|
||||||
|
media_query_results: Vec<MediaListKey>,
|
||||||
|
contents: StyleSheetContentList,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for CascadeDataCacheKey {}
|
||||||
|
unsafe impl Sync for CascadeDataCacheKey {}
|
||||||
|
|
||||||
trait CascadeDataCacheEntry : Sized {
|
trait CascadeDataCacheEntry : Sized {
|
||||||
/// Returns a reference to the cascade data.
|
/// Returns a reference to the cascade data.
|
||||||
fn cascade_data(&self) -> &CascadeData;
|
fn cascade_data(&self) -> &CascadeData;
|
||||||
|
@ -80,7 +112,7 @@ trait CascadeDataCacheEntry : Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CascadeDataCache<Entry> {
|
struct CascadeDataCache<Entry> {
|
||||||
entries: Vec<Arc<Entry>>,
|
entries: FxHashMap<CascadeDataCacheKey, Arc<Entry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Entry> CascadeDataCache<Entry>
|
impl<Entry> CascadeDataCache<Entry>
|
||||||
|
@ -88,7 +120,7 @@ where
|
||||||
Entry: CascadeDataCacheEntry,
|
Entry: CascadeDataCacheEntry,
|
||||||
{
|
{
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { entries: vec![] }
|
Self { entries: Default::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
|
@ -110,51 +142,66 @@ where
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
{
|
{
|
||||||
|
use std::collections::hash_map::Entry as HashMapEntry;
|
||||||
debug!("StyleSheetCache::lookup({})", self.len());
|
debug!("StyleSheetCache::lookup({})", self.len());
|
||||||
|
|
||||||
if !collection.dirty() {
|
if !collection.dirty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut key = EffectiveMediaQueryResults::new();
|
let mut key = CascadeDataCacheKey::default();
|
||||||
for sheet in collection.sheets() {
|
for sheet in collection.sheets() {
|
||||||
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
|
CascadeData::collect_applicable_media_query_results_into(
|
||||||
|
device,
|
||||||
|
sheet,
|
||||||
|
guard,
|
||||||
|
&mut key.media_query_results,
|
||||||
|
&mut key.contents,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for entry in &self.entries {
|
let new_entry;
|
||||||
if std::ptr::eq(&**entry, old_entry) {
|
match self.entries.entry(key) {
|
||||||
// Avoid reusing our old entry (this can happen if we get
|
HashMapEntry::Vacant(e) => {
|
||||||
// invalidated due to CSSOM mutations and our old stylesheet
|
debug!("> Picking the slow path (not in the cache)");
|
||||||
// contents were already unique, for example). This old entry
|
new_entry = Entry::rebuild(
|
||||||
// will be pruned from the cache with take_unused() afterwards.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if entry.cascade_data().effective_media_query_results != key {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if log_enabled!(log::Level::Debug) {
|
|
||||||
debug!("cache hit for:");
|
|
||||||
for sheet in collection.sheets() {
|
|
||||||
debug!(" > {:?}", sheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The line below ensures the "committed" bit is updated properly
|
|
||||||
// below.
|
|
||||||
collection.each(|_, _| true);
|
|
||||||
return Ok(Some(entry.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("> Picking the slow path");
|
|
||||||
|
|
||||||
let new_entry = Entry::rebuild(
|
|
||||||
device,
|
device,
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
collection,
|
collection,
|
||||||
guard,
|
guard,
|
||||||
old_entry,
|
old_entry,
|
||||||
)?;
|
)?;
|
||||||
|
e.insert(new_entry.clone());
|
||||||
|
}
|
||||||
|
HashMapEntry::Occupied(mut e) => {
|
||||||
|
// Avoid reusing our old entry (this can happen if we get
|
||||||
|
// invalidated due to CSSOM mutations and our old stylesheet
|
||||||
|
// contents were already unique, for example).
|
||||||
|
if !std::ptr::eq(&**e.get(), old_entry) {
|
||||||
|
if log_enabled!(log::Level::Debug) {
|
||||||
|
debug!("cache hit for:");
|
||||||
|
for sheet in collection.sheets() {
|
||||||
|
debug!(" > {:?}", sheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The line below ensures the "committed" bit is updated
|
||||||
|
// properly.
|
||||||
|
collection.each(|_, _| true);
|
||||||
|
return Ok(Some(e.get().clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("> Picking the slow path due to same entry as old");
|
||||||
|
new_entry = Entry::rebuild(
|
||||||
|
device,
|
||||||
|
quirks_mode,
|
||||||
|
collection,
|
||||||
|
guard,
|
||||||
|
old_entry,
|
||||||
|
)?;
|
||||||
|
e.insert(new_entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.entries.push(new_entry.clone());
|
|
||||||
Ok(Some(new_entry))
|
Ok(Some(new_entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,25 +214,27 @@ where
|
||||||
/// cache to not deadlock.
|
/// cache to not deadlock.
|
||||||
fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
|
fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
|
||||||
let mut unused = SmallVec::new();
|
let mut unused = SmallVec::new();
|
||||||
for i in (0..self.entries.len()).rev() {
|
self.entries.retain(|_key, value| {
|
||||||
// is_unique() returns false for static references, but we never
|
// is_unique() returns false for static references, but we never
|
||||||
// have static references to UserAgentCascadeDatas. If we did, it
|
// have static references to UserAgentCascadeDatas. If we did, it
|
||||||
// may not make sense to put them in the cache in the first place.
|
// may not make sense to put them in the cache in the first place.
|
||||||
if self.entries[i].is_unique() {
|
if !value.is_unique() {
|
||||||
unused.push(self.entries.remove(i));
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
unused.push(value.clone());
|
||||||
|
false
|
||||||
|
});
|
||||||
unused
|
unused
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_all(&mut self) -> Vec<Arc<Entry>> {
|
fn take_all(&mut self) -> FxHashMap<CascadeDataCacheKey, Arc<Entry>> {
|
||||||
mem::replace(&mut self.entries, Vec::new())
|
mem::take(&mut self.entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||||
sizes.mOther += self.entries.shallow_size_of(ops);
|
sizes.mOther += self.entries.shallow_size_of(ops);
|
||||||
for arc in self.entries.iter() {
|
for (_key, arc) in self.entries.iter() {
|
||||||
// These are primary Arc references that can be measured
|
// These are primary Arc references that can be measured
|
||||||
// unconditionally.
|
// unconditionally.
|
||||||
sizes.mOther += arc.unconditional_shallow_size_of(ops);
|
sizes.mOther += arc.unconditional_shallow_size_of(ops);
|
||||||
|
@ -2033,7 +2082,8 @@ impl CascadeData {
|
||||||
device: &Device,
|
device: &Device,
|
||||||
stylesheet: &S,
|
stylesheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
results: &mut EffectiveMediaQueryResults,
|
results: &mut Vec<MediaListKey>,
|
||||||
|
contents_list: &mut StyleSheetContentList,
|
||||||
) where
|
) where
|
||||||
S: StylesheetInDocument + 'static,
|
S: StylesheetInDocument + 'static,
|
||||||
{
|
{
|
||||||
|
@ -2042,19 +2092,25 @@ impl CascadeData {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(" + {:?}", stylesheet);
|
debug!(" + {:?}", stylesheet);
|
||||||
results.saw_effective(stylesheet.contents());
|
let contents = stylesheet.contents();
|
||||||
|
results.push(contents.to_media_list_key());
|
||||||
|
|
||||||
|
// Safety: StyleSheetContents are reference-counted with Arc.
|
||||||
|
contents_list.push(StylesheetContentsPtr(unsafe {
|
||||||
|
Arc::from_raw_addrefed(contents)
|
||||||
|
}));
|
||||||
|
|
||||||
for rule in stylesheet.effective_rules(device, guard) {
|
for rule in stylesheet.effective_rules(device, guard) {
|
||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Import(ref lock) => {
|
CssRule::Import(ref lock) => {
|
||||||
let import_rule = lock.read_with(guard);
|
let import_rule = lock.read_with(guard);
|
||||||
debug!(" + {:?}", import_rule.stylesheet.media(guard));
|
debug!(" + {:?}", import_rule.stylesheet.media(guard));
|
||||||
results.saw_effective(import_rule);
|
results.push(import_rule.to_media_list_key());
|
||||||
},
|
},
|
||||||
CssRule::Media(ref lock) => {
|
CssRule::Media(ref lock) => {
|
||||||
let media_rule = lock.read_with(guard);
|
let media_rule = lock.read_with(guard);
|
||||||
debug!(" + {:?}", media_rule.media_queries.read_with(guard));
|
debug!(" + {:?}", media_rule.media_queries.read_with(guard));
|
||||||
results.saw_effective(media_rule);
|
results.push(media_rule.to_media_list_key());
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
|
use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
|
||||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
use crate::values::generics::color::{Color as GenericColor, ComplexColorRatios};
|
use crate::values::generics::color::{Color as GenericColor, ComplexColorRatios};
|
||||||
|
use crate::values::specified::color::{ColorSpaceKind, HueAdjuster};
|
||||||
|
use euclid::default::{Transform3D, Vector3D};
|
||||||
|
|
||||||
/// An animated RGBA color.
|
/// An animated RGBA color.
|
||||||
///
|
///
|
||||||
|
@ -41,6 +43,26 @@ impl RGBA {
|
||||||
alpha,
|
alpha,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the colour is in gamut for sRGB.
|
||||||
|
pub fn in_gamut(&self) -> bool {
|
||||||
|
0. <= self.red &&
|
||||||
|
self.red <= 1. &&
|
||||||
|
0. <= self.green &&
|
||||||
|
self.green <= 1. &&
|
||||||
|
0. <= self.blue &&
|
||||||
|
self.blue <= 1.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the colour with coordinates clamped to the sRGB range.
|
||||||
|
pub fn clamp(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
red: self.red.max(0.).min(1.),
|
||||||
|
green: self.green.max(0.).min(1.),
|
||||||
|
blue: self.blue.max(0.).min(1.),
|
||||||
|
alpha: self.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Animate for RGBA {
|
impl Animate for RGBA {
|
||||||
|
@ -106,43 +128,69 @@ impl Color {
|
||||||
|
|
||||||
/// Mix two colors into one.
|
/// Mix two colors into one.
|
||||||
pub fn mix(
|
pub fn mix(
|
||||||
|
color_space: ColorSpaceKind,
|
||||||
left_color: &Color,
|
left_color: &Color,
|
||||||
left_weight: f32,
|
left_weight: f32,
|
||||||
right_color: &Color,
|
right_color: &Color,
|
||||||
right_weight: f32,
|
right_weight: f32,
|
||||||
|
hue_adjuster: HueAdjuster,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let left_bg = left_color.scaled_rgba();
|
match color_space {
|
||||||
let right_bg = right_color.scaled_rgba();
|
ColorSpaceKind::Srgb => Self::mix_in::<RGBA>(
|
||||||
let alpha = (left_bg.alpha * left_weight +
|
left_color,
|
||||||
right_bg.alpha * right_weight)
|
left_weight,
|
||||||
.min(1.);
|
right_color,
|
||||||
|
right_weight,
|
||||||
let mut fg = 0.;
|
hue_adjuster,
|
||||||
let mut red = 0.;
|
),
|
||||||
let mut green = 0.;
|
ColorSpaceKind::Xyz => Self::mix_in::<XYZA>(
|
||||||
let mut blue = 0.;
|
left_color,
|
||||||
|
left_weight,
|
||||||
let colors = [
|
right_color,
|
||||||
(left_color, &left_bg, left_weight),
|
right_weight,
|
||||||
(right_color, &right_bg, right_weight),
|
hue_adjuster,
|
||||||
];
|
),
|
||||||
|
ColorSpaceKind::Lab => Self::mix_in::<LABA>(
|
||||||
for &(color, bg, weight) in &colors {
|
left_color,
|
||||||
fg += color.ratios.fg * weight;
|
left_weight,
|
||||||
|
right_color,
|
||||||
red += bg.red * bg.alpha * weight;
|
right_weight,
|
||||||
green += bg.green * bg.alpha * weight;
|
hue_adjuster,
|
||||||
blue += bg.blue * bg.alpha * weight;
|
),
|
||||||
|
ColorSpaceKind::Lch => Self::mix_in::<LCHA>(
|
||||||
|
left_color,
|
||||||
|
left_weight,
|
||||||
|
right_color,
|
||||||
|
right_weight,
|
||||||
|
hue_adjuster,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = if alpha <= 0. {
|
fn mix_in<S>(
|
||||||
RGBA::transparent()
|
left_color: &Color,
|
||||||
|
left_weight: f32,
|
||||||
|
right_color: &Color,
|
||||||
|
right_weight: f32,
|
||||||
|
hue_adjuster: HueAdjuster,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
S: ModelledColor,
|
||||||
|
{
|
||||||
|
let left_bg = S::from(left_color.scaled_rgba());
|
||||||
|
let right_bg = S::from(right_color.scaled_rgba());
|
||||||
|
|
||||||
|
let color = S::lerp(left_bg, left_weight, right_bg, right_weight, hue_adjuster);
|
||||||
|
let rgba: RGBA = color.into();
|
||||||
|
let rgba = if !rgba.in_gamut() {
|
||||||
|
// TODO: Better gamut mapping.
|
||||||
|
rgba.clamp()
|
||||||
} else {
|
} else {
|
||||||
let inv = 1. / alpha;
|
rgba
|
||||||
RGBA::new(red * inv, green * inv, blue * inv, alpha)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::new(color, ComplexColorRatios { bg: 1., fg })
|
let fg = left_color.ratios.fg * left_weight + right_color.ratios.fg * right_weight;
|
||||||
|
Self::new(rgba, ComplexColorRatios { bg: 1., fg })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scaled_rgba(&self) -> RGBA {
|
fn scaled_rgba(&self) -> RGBA {
|
||||||
|
@ -309,3 +357,573 @@ impl ToAnimatedZero for Color {
|
||||||
Ok(RGBA::transparent().into())
|
Ok(RGBA::transparent().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A color modelled in a specific color space (such as sRGB or CIE XYZ).
|
||||||
|
///
|
||||||
|
/// For now, colors modelled in other spaces need to be convertible to and from
|
||||||
|
/// `RGBA` because we use sRGB for displaying colors.
|
||||||
|
trait ModelledColor: Clone + Copy + From<RGBA> + Into<RGBA> {
|
||||||
|
/// Linearly interpolate between the left and right colors.
|
||||||
|
///
|
||||||
|
/// The HueAdjuster parameter is only for color spaces where the hue is
|
||||||
|
/// represented as an angle (e.g., CIE LCH).
|
||||||
|
fn lerp(
|
||||||
|
left_bg: Self,
|
||||||
|
left_weight: f32,
|
||||||
|
right_bg: Self,
|
||||||
|
right_weight: f32,
|
||||||
|
hue_adjuster: HueAdjuster,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelledColor for RGBA {
|
||||||
|
fn lerp(
|
||||||
|
left_bg: Self,
|
||||||
|
left_weight: f32,
|
||||||
|
right_bg: Self,
|
||||||
|
right_weight: f32,
|
||||||
|
_: HueAdjuster,
|
||||||
|
) -> Self {
|
||||||
|
// Interpolation with alpha, as per
|
||||||
|
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||||
|
let mut red = 0.;
|
||||||
|
let mut green = 0.;
|
||||||
|
let mut blue = 0.;
|
||||||
|
|
||||||
|
// sRGB is a rectangular othogonal color space, so all component values
|
||||||
|
// are multiplied by the alpha value.
|
||||||
|
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||||
|
red += bg.red * bg.alpha * weight;
|
||||||
|
green += bg.green * bg.alpha * weight;
|
||||||
|
blue += bg.blue * bg.alpha * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||||
|
if alpha <= 0. {
|
||||||
|
RGBA::transparent()
|
||||||
|
} else {
|
||||||
|
let inv = 1. / alpha;
|
||||||
|
RGBA::new(red * inv, green * inv, blue * inv, alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An animated XYZA colour.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct XYZA {
|
||||||
|
/// The x component.
|
||||||
|
pub x: f32,
|
||||||
|
/// The y component.
|
||||||
|
pub y: f32,
|
||||||
|
/// The z component.
|
||||||
|
pub z: f32,
|
||||||
|
/// The alpha component.
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XYZA {
|
||||||
|
/// Returns a transparent color.
|
||||||
|
#[inline]
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Self {
|
||||||
|
x: 0.,
|
||||||
|
y: 0.,
|
||||||
|
z: 0.,
|
||||||
|
alpha: 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelledColor for XYZA {
|
||||||
|
fn lerp(
|
||||||
|
left_bg: Self,
|
||||||
|
left_weight: f32,
|
||||||
|
right_bg: Self,
|
||||||
|
right_weight: f32,
|
||||||
|
_: HueAdjuster,
|
||||||
|
) -> Self {
|
||||||
|
// Interpolation with alpha, as per
|
||||||
|
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||||
|
let mut x = 0.;
|
||||||
|
let mut y = 0.;
|
||||||
|
let mut z = 0.;
|
||||||
|
|
||||||
|
// CIE XYZ is a rectangular othogonal color space, so all component
|
||||||
|
// values are multiplied by the alpha value.
|
||||||
|
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||||
|
x += bg.x * bg.alpha * weight;
|
||||||
|
y += bg.y * bg.alpha * weight;
|
||||||
|
z += bg.z * bg.alpha * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||||
|
if alpha <= 0. {
|
||||||
|
Self::transparent()
|
||||||
|
} else {
|
||||||
|
let inv = 1. / alpha;
|
||||||
|
Self {
|
||||||
|
x: x * inv,
|
||||||
|
y: y * inv,
|
||||||
|
z: z * inv,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An animated LABA colour.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct LABA {
|
||||||
|
/// The lightness component.
|
||||||
|
pub lightness: f32,
|
||||||
|
/// The a component.
|
||||||
|
pub a: f32,
|
||||||
|
/// The b component.
|
||||||
|
pub b: f32,
|
||||||
|
/// The alpha component.
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LABA {
|
||||||
|
/// Returns a transparent color.
|
||||||
|
#[inline]
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Self {
|
||||||
|
lightness: 0.,
|
||||||
|
a: 0.,
|
||||||
|
b: 0.,
|
||||||
|
alpha: 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelledColor for LABA {
|
||||||
|
fn lerp(
|
||||||
|
left_bg: Self,
|
||||||
|
left_weight: f32,
|
||||||
|
right_bg: Self,
|
||||||
|
right_weight: f32,
|
||||||
|
_: HueAdjuster,
|
||||||
|
) -> Self {
|
||||||
|
// Interpolation with alpha, as per
|
||||||
|
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||||
|
let mut lightness = 0.;
|
||||||
|
let mut a = 0.;
|
||||||
|
let mut b = 0.;
|
||||||
|
|
||||||
|
// CIE LAB is a rectangular othogonal color space, so all component
|
||||||
|
// values are multiplied by the alpha value.
|
||||||
|
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||||
|
lightness += bg.lightness * bg.alpha * weight;
|
||||||
|
a += bg.a * bg.alpha * weight;
|
||||||
|
b += bg.b * bg.alpha * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||||
|
if alpha <= 0. {
|
||||||
|
Self::transparent()
|
||||||
|
} else {
|
||||||
|
let inv = 1. / alpha;
|
||||||
|
Self {
|
||||||
|
lightness: lightness * inv,
|
||||||
|
a: a * inv,
|
||||||
|
b: b * inv,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An animated LCHA colour.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct LCHA {
|
||||||
|
/// The lightness component.
|
||||||
|
pub lightness: f32,
|
||||||
|
/// The chroma component.
|
||||||
|
pub chroma: f32,
|
||||||
|
/// The hua component.
|
||||||
|
pub hue: f32,
|
||||||
|
/// The alpha component.
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LCHA {
|
||||||
|
/// Returns a transparent color.
|
||||||
|
#[inline]
|
||||||
|
pub fn transparent() -> Self {
|
||||||
|
Self {
|
||||||
|
lightness: 0.,
|
||||||
|
chroma: 0.,
|
||||||
|
hue: 0.,
|
||||||
|
alpha: 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LCHA {
|
||||||
|
fn adjust(left_bg: Self, right_bg: Self, hue_adjuster: HueAdjuster) -> (Self, Self) {
|
||||||
|
use std::f32::consts::{PI, TAU};
|
||||||
|
|
||||||
|
let mut left_bg = left_bg;
|
||||||
|
let mut right_bg = right_bg;
|
||||||
|
|
||||||
|
// Adjust the hue angle as per
|
||||||
|
// https://drafts.csswg.org/css-color/#hue-interpolation.
|
||||||
|
//
|
||||||
|
// If both hue angles are NAN, they should be set to 0. Otherwise, if a
|
||||||
|
// single hue angle is NAN, it should use the other hue angle.
|
||||||
|
if left_bg.hue.is_nan() || right_bg.hue.is_nan() {
|
||||||
|
if left_bg.hue.is_nan() && right_bg.hue.is_nan() {
|
||||||
|
left_bg.hue = 0.;
|
||||||
|
right_bg.hue = 0.;
|
||||||
|
} else if left_bg.hue.is_nan() {
|
||||||
|
left_bg.hue = right_bg.hue;
|
||||||
|
} else if right_bg.hue.is_nan() {
|
||||||
|
right_bg.hue = left_bg.hue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hue_adjuster != HueAdjuster::Specified {
|
||||||
|
// Normalize hue into [0, 2 * PI)
|
||||||
|
while left_bg.hue < 0. {
|
||||||
|
left_bg.hue += TAU;
|
||||||
|
}
|
||||||
|
while left_bg.hue > TAU {
|
||||||
|
left_bg.hue -= TAU;
|
||||||
|
}
|
||||||
|
|
||||||
|
while right_bg.hue < 0. {
|
||||||
|
right_bg.hue += TAU;
|
||||||
|
}
|
||||||
|
while right_bg.hue >= TAU {
|
||||||
|
right_bg.hue -= TAU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match hue_adjuster {
|
||||||
|
HueAdjuster::Shorter => {
|
||||||
|
let delta = right_bg.hue - left_bg.hue;
|
||||||
|
|
||||||
|
if delta > PI {
|
||||||
|
left_bg.hue += PI;
|
||||||
|
} else if delta < -1. * PI {
|
||||||
|
right_bg.hue += PI;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
HueAdjuster::Longer => {
|
||||||
|
let delta = right_bg.hue - left_bg.hue;
|
||||||
|
if 0. < delta && delta < PI {
|
||||||
|
left_bg.hue += TAU;
|
||||||
|
} else if -1. * PI < delta && delta < 0. {
|
||||||
|
right_bg.hue += TAU;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
HueAdjuster::Increasing => {
|
||||||
|
if right_bg.hue < left_bg.hue {
|
||||||
|
right_bg.hue += TAU;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
HueAdjuster::Decreasing => {
|
||||||
|
if left_bg.hue < right_bg.hue {
|
||||||
|
left_bg.hue += TAU;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
//Angles are not adjusted. They are interpolated like any other
|
||||||
|
//component.
|
||||||
|
HueAdjuster::Specified => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
(left_bg, right_bg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelledColor for LCHA {
|
||||||
|
fn lerp(
|
||||||
|
left_bg: Self,
|
||||||
|
left_weight: f32,
|
||||||
|
right_bg: Self,
|
||||||
|
right_weight: f32,
|
||||||
|
hue_adjuster: HueAdjuster,
|
||||||
|
) -> Self {
|
||||||
|
// Interpolation with alpha, as per
|
||||||
|
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||||
|
let (left_bg, right_bg) = Self::adjust(left_bg, right_bg, hue_adjuster);
|
||||||
|
|
||||||
|
let mut lightness = 0.;
|
||||||
|
let mut chroma = 0.;
|
||||||
|
let mut hue = 0.;
|
||||||
|
|
||||||
|
// CIE LCH is a cylindical polar color space, so all component values
|
||||||
|
// are multiplied by the alpha value.
|
||||||
|
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||||
|
lightness += bg.lightness * bg.alpha * weight;
|
||||||
|
chroma += bg.chroma * bg.alpha * weight;
|
||||||
|
// LCHA is a cylindrical color space so the hue coordinate is not
|
||||||
|
// pre-multipled by the alpha component when interpolating.
|
||||||
|
hue += bg.hue * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||||
|
if alpha <= 0. {
|
||||||
|
Self::transparent()
|
||||||
|
} else {
|
||||||
|
let inv = 1. / alpha;
|
||||||
|
Self {
|
||||||
|
lightness: lightness * inv,
|
||||||
|
chroma: chroma * inv,
|
||||||
|
hue,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RGBA> for XYZA {
|
||||||
|
/// Convert an RGBA colour to XYZ as specified in [1].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||||
|
fn from(rgba: RGBA) -> Self {
|
||||||
|
fn linearize(value: f32) -> f32 {
|
||||||
|
let sign = if value < 0. { -1. } else { 1. };
|
||||||
|
let abs = value.abs();
|
||||||
|
if abs < 0.04045 {
|
||||||
|
return value / 12.92;
|
||||||
|
}
|
||||||
|
|
||||||
|
sign * ((abs + 0.055) / 1.055).powf(2.4)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
const SRGB_TO_XYZ: Transform3D<f32> = Transform3D::new(
|
||||||
|
0.41239079926595934, 0.21263900587151027, 0.01933081871559182, 0.,
|
||||||
|
0.357584339383878, 0.715168678767756, 0.11919477979462598, 0.,
|
||||||
|
0.1804807884018343, 0.07219231536073371, 0.9505321522496607, 0.,
|
||||||
|
0., 0., 0., 1.,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
const BRADFORD: Transform3D<f32> = Transform3D::new(
|
||||||
|
1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0.,
|
||||||
|
0.022946793341019088, 0.990434484573249, 0.015055144896577895, 0.,
|
||||||
|
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
|
||||||
|
0., 0., 0., 1.,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1. Convert from sRGB to linear-light sRGB (undo gamma encoding).
|
||||||
|
let rgb = Vector3D::new(
|
||||||
|
linearize(rgba.red),
|
||||||
|
linearize(rgba.green),
|
||||||
|
linearize(rgba.blue),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Convert from linear sRGB to CIE XYZ.
|
||||||
|
// 3. Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in XYZ
|
||||||
|
// with the Bradford transform.
|
||||||
|
let xyz = SRGB_TO_XYZ.then(&BRADFORD).transform_vector3d(rgb);
|
||||||
|
|
||||||
|
XYZA {
|
||||||
|
x: xyz.x,
|
||||||
|
y: xyz.y,
|
||||||
|
z: xyz.z,
|
||||||
|
alpha: rgba.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<XYZA> for LABA {
|
||||||
|
/// Convert an XYZ colour to LAB as specified in [1] and [2].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||||
|
/// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||||
|
fn from(xyza: XYZA) -> Self {
|
||||||
|
const WHITE: [f32; 3] = [0.96422, 1., 0.82521];
|
||||||
|
|
||||||
|
fn compute_f(value: f32) -> f32 {
|
||||||
|
const EPSILON: f32 = 216. / 24389.;
|
||||||
|
const KAPPA: f32 = 24389. / 27.;
|
||||||
|
|
||||||
|
if value > EPSILON {
|
||||||
|
value.cbrt()
|
||||||
|
} else {
|
||||||
|
(KAPPA * value + 16.) / 116.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Convert D50-adapted XYZ to Lab.
|
||||||
|
let f = [
|
||||||
|
compute_f(xyza.x / WHITE[0]),
|
||||||
|
compute_f(xyza.y / WHITE[1]),
|
||||||
|
compute_f(xyza.z / WHITE[2]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let lightness = 116. * f[1] - 16.;
|
||||||
|
let a = 500. * (f[0] - f[1]);
|
||||||
|
let b = 200. * (f[1] - f[2]);
|
||||||
|
|
||||||
|
LABA {
|
||||||
|
lightness,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
alpha: xyza.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LABA> for LCHA {
|
||||||
|
/// Convert a LAB color to LCH as specified in [1].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||||
|
fn from(laba: LABA) -> Self {
|
||||||
|
let hue = laba.b.atan2(laba.a);
|
||||||
|
let chroma = (laba.a * laba.a + laba.b * laba.b).sqrt();
|
||||||
|
LCHA {
|
||||||
|
lightness: laba.lightness,
|
||||||
|
chroma,
|
||||||
|
hue,
|
||||||
|
alpha: laba.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LCHA> for LABA {
|
||||||
|
/// Convert a LCH color to LAB as specified in [1].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||||
|
fn from(lcha: LCHA) -> Self {
|
||||||
|
let a = lcha.chroma * lcha.hue.cos();
|
||||||
|
let b = lcha.chroma * lcha.hue.sin();
|
||||||
|
LABA {
|
||||||
|
lightness: lcha.lightness,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
alpha: lcha.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LABA> for XYZA {
|
||||||
|
/// Convert a CIELAB color to XYZ as specified in [1] and [2].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined
|
||||||
|
/// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||||
|
fn from(laba: LABA) -> Self {
|
||||||
|
// 1. Convert LAB to (D50-adapated) XYZ.
|
||||||
|
const KAPPA: f32 = 24389. / 27.;
|
||||||
|
const EPSILON: f32 = 216. / 24389.;
|
||||||
|
const WHITE: [f32; 3] = [0.96422, 1., 0.82521];
|
||||||
|
|
||||||
|
let f1 = (laba.lightness + 16f32) / 116f32;
|
||||||
|
let f0 = (laba.a / 500.) + f1;
|
||||||
|
let f2 = f1 - laba.b / 200.;
|
||||||
|
|
||||||
|
let x = if f0.powf(3.) > EPSILON {
|
||||||
|
f0.powf(3.)
|
||||||
|
} else {
|
||||||
|
(116. * f0 - 16.) / KAPPA
|
||||||
|
};
|
||||||
|
let y = if laba.lightness > KAPPA * EPSILON {
|
||||||
|
((laba.lightness + 16.) / 116.).powf(3.)
|
||||||
|
} else {
|
||||||
|
laba.lightness / KAPPA
|
||||||
|
};
|
||||||
|
let z = if f2.powf(3.) > EPSILON {
|
||||||
|
f2.powf(3.)
|
||||||
|
} else {
|
||||||
|
(116. * f2 - 16.) / KAPPA
|
||||||
|
};
|
||||||
|
|
||||||
|
XYZA {
|
||||||
|
x: x * WHITE[0],
|
||||||
|
y: y * WHITE[1],
|
||||||
|
z: z * WHITE[2],
|
||||||
|
alpha: laba.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<XYZA> for RGBA {
|
||||||
|
/// Convert an XYZ color to sRGB as specified in [1] and [2].
|
||||||
|
///
|
||||||
|
/// [1]: https://www.w3.org/TR/css-color-4/#lab-to-predefined
|
||||||
|
/// [2]: https://www.w3.org/TR/css-color-4/#color-conversion-code
|
||||||
|
fn from(xyza: XYZA) -> Self {
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
const BRADFORD_INVERSE: Transform3D<f32> = Transform3D::new(
|
||||||
|
0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.,
|
||||||
|
-0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.,
|
||||||
|
0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.,
|
||||||
|
0., 0., 0., 1.,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
const XYZ_TO_SRGB: Transform3D<f32> = Transform3D::new(
|
||||||
|
3.2409699419045226, -0.9692436362808796, 0.05563007969699366, 0.,
|
||||||
|
-1.537383177570094, 1.8759675015077202, -0.20397695888897652, 0.,
|
||||||
|
-0.4986107602930034, 0.04155505740717559, 1.0569715142428786, 0.,
|
||||||
|
0., 0., 0., 1.,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint
|
||||||
|
// used in sRGB, with the Bradford transform.
|
||||||
|
// 3. Convert from (D65-adapted) CIE XYZ to linear-light srgb
|
||||||
|
let xyz = Vector3D::new(xyza.x, xyza.y, xyza.z);
|
||||||
|
let linear_rgb = BRADFORD_INVERSE.then(&XYZ_TO_SRGB).transform_vector3d(xyz);
|
||||||
|
|
||||||
|
// 4. Convert from linear-light srgb to srgb (do gamma encoding).
|
||||||
|
fn delinearize(value: f32) -> f32 {
|
||||||
|
let sign = if value < 0. { -1. } else { 1. };
|
||||||
|
let abs = value.abs();
|
||||||
|
|
||||||
|
if abs > 0.0031308 {
|
||||||
|
sign * (1.055 * abs.powf(1. / 2.4) - 0.055)
|
||||||
|
} else {
|
||||||
|
12.92 * value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let red = delinearize(linear_rgb.x);
|
||||||
|
let green = delinearize(linear_rgb.y);
|
||||||
|
let blue = delinearize(linear_rgb.z);
|
||||||
|
|
||||||
|
RGBA {
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
alpha: xyza.alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RGBA> for LABA {
|
||||||
|
fn from(rgba: RGBA) -> Self {
|
||||||
|
let xyza: XYZA = rgba.into();
|
||||||
|
xyza.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LABA> for RGBA {
|
||||||
|
fn from(laba: LABA) -> Self {
|
||||||
|
let xyza: XYZA = laba.into();
|
||||||
|
xyza.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RGBA> for LCHA {
|
||||||
|
fn from(rgba: RGBA) -> Self {
|
||||||
|
let xyza: XYZA = rgba.into();
|
||||||
|
let laba: LABA = xyza.into();
|
||||||
|
laba.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LCHA> for RGBA {
|
||||||
|
fn from(lcha: LCHA) -> Self {
|
||||||
|
let laba: LABA = lcha.into();
|
||||||
|
let xyza: XYZA = laba.into();
|
||||||
|
xyza.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1105,21 +1105,27 @@ impl Animate for ComputedTransformOperation {
|
||||||
// interpolated as defined in section Interpolation of
|
// interpolated as defined in section Interpolation of
|
||||||
// Matrices afterwards.
|
// Matrices afterwards.
|
||||||
//
|
//
|
||||||
let from = create_perspective_matrix(fd.px());
|
let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
|
||||||
let to = create_perspective_matrix(td.px());
|
let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
|
||||||
|
|
||||||
let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
|
let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
|
||||||
|
|
||||||
let decomposed = decompose_3d_matrix(interpolated)?;
|
let decomposed = decompose_3d_matrix(interpolated)?;
|
||||||
let perspective_z = decomposed.perspective.2;
|
let perspective_z = decomposed.perspective.2;
|
||||||
let used_value = if perspective_z == 0. {
|
// Clamp results outside of the -1 to 0 range so that we get perspective
|
||||||
0.
|
// function values between 1 and infinity.
|
||||||
|
let used_value = if perspective_z >= 0. {
|
||||||
|
transform::PerspectiveFunction::None
|
||||||
|
} else {
|
||||||
|
transform::PerspectiveFunction::Length(CSSPixelLength::new(
|
||||||
|
if perspective_z <= -1. {
|
||||||
|
1.
|
||||||
} else {
|
} else {
|
||||||
-1. / perspective_z
|
-1. / perspective_z
|
||||||
|
}
|
||||||
|
))
|
||||||
};
|
};
|
||||||
Ok(TransformOperation::Perspective(CSSPixelLength::new(
|
Ok(TransformOperation::Perspective(used_value))
|
||||||
used_value,
|
|
||||||
)))
|
|
||||||
},
|
},
|
||||||
_ if self.is_translate() && other.is_translate() => self
|
_ if self.is_translate() && other.is_translate() => self
|
||||||
.to_translate_3d()
|
.to_translate_3d()
|
||||||
|
@ -1198,14 +1204,18 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
|
||||||
(
|
(
|
||||||
&TransformOperation::Perspective(ref fd),
|
&TransformOperation::Perspective(ref fd),
|
||||||
&TransformOperation::Perspective(ref td),
|
&TransformOperation::Perspective(ref td),
|
||||||
) => fd.compute_squared_distance(td),
|
) => {
|
||||||
|
fd.infinity_or(|l| l.px())
|
||||||
|
.compute_squared_distance(&td.infinity_or(|l| l.px()))
|
||||||
|
},
|
||||||
(&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) |
|
(&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) |
|
||||||
(&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
|
(&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
|
||||||
// FIXME(emilio): Is this right? Why interpolating this with
|
// FIXME(emilio): Is this right? Why interpolating this with
|
||||||
// Perspective but not with anything else?
|
// Perspective but not with anything else?
|
||||||
let mut p_matrix = Matrix3D::identity();
|
let mut p_matrix = Matrix3D::identity();
|
||||||
if p.px() > 0. {
|
let p = p.infinity_or(|p| p.px());
|
||||||
p_matrix.m34 = -1. / p.px();
|
if p >= 0. {
|
||||||
|
p_matrix.m34 = -1. / p.max(1.);
|
||||||
}
|
}
|
||||||
p_matrix.compute_squared_distance(&m)
|
p_matrix.compute_squared_distance(&m)
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,8 @@ use cssparser::{Color as CSSParserColor, RGBA};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::{CssWriter, ToCss};
|
use style_traits::{CssWriter, ToCss};
|
||||||
|
|
||||||
|
pub use crate::values::specified::color::ColorScheme;
|
||||||
|
|
||||||
/// The computed value of the `color` property.
|
/// The computed value of the `color` property.
|
||||||
pub type ColorPropertyValue = RGBA;
|
pub type ColorPropertyValue = RGBA;
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
|
|
||||||
//! Computed values for font properties
|
//! Computed values for font properties
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use crate::gecko_bindings::sugar::refptr::RefPtr;
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::gecko_bindings::{bindings, structs};
|
use crate::gecko_bindings::{bindings, structs};
|
||||||
use crate::values::animated::{ToAnimatedValue, ToAnimatedZero};
|
use crate::parser::{Parse, ParserContext};
|
||||||
|
use crate::values::animated::ToAnimatedValue;
|
||||||
use crate::values::computed::{
|
use crate::values::computed::{
|
||||||
Angle, Context, Integer, Length, NonNegativeLength, NonNegativePercentage,
|
Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage,
|
||||||
};
|
};
|
||||||
use crate::values::computed::{Number, Percentage, ToComputedValue};
|
use crate::values::computed::{Number, Percentage, ToComputedValue};
|
||||||
use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue};
|
use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue};
|
||||||
|
@ -26,13 +25,7 @@ use cssparser::{serialize_identifier, CssStringWriter, Parser};
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use std::mem::{self, ManuallyDrop};
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
use std::slice;
|
|
||||||
use style_traits::{CssWriter, ParseError, ToCss};
|
use style_traits::{CssWriter, ParseError, ToCss};
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use to_shmem::{self, SharedMemoryBuilder, ToShmem};
|
|
||||||
|
|
||||||
pub use crate::values::computed::Length as MozScriptMinSize;
|
pub use crate::values::computed::Length as MozScriptMinSize;
|
||||||
pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
|
pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
|
||||||
|
@ -183,6 +176,7 @@ impl ToAnimatedValue for FontSize {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
|
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
|
||||||
#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
|
#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
|
||||||
/// Specifies a prioritized list of font family names or generic family names.
|
/// Specifies a prioritized list of font family names or generic family names.
|
||||||
|
#[repr(C)]
|
||||||
pub struct FontFamily {
|
pub struct FontFamily {
|
||||||
/// The actual list of family names.
|
/// The actual list of family names.
|
||||||
pub families: FontFamilyList,
|
pub families: FontFamilyList,
|
||||||
|
@ -190,27 +184,105 @@ pub struct FontFamily {
|
||||||
pub is_system_font: bool,
|
pub is_system_font: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! static_font_family {
|
||||||
|
($ident:ident, $family:expr) => {
|
||||||
|
lazy_static! {
|
||||||
|
static ref $ident: FontFamily = FontFamily {
|
||||||
|
families: FontFamilyList {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
list: Box::new([$family]),
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fallback: GenericFontFamily::None,
|
||||||
|
},
|
||||||
|
is_system_font: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl FontFamily {
|
impl FontFamily {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Get default font family as `serif` which is a generic font-family
|
/// Get default font family as `serif` which is a generic font-family
|
||||||
pub fn serif() -> Self {
|
pub fn serif() -> Self {
|
||||||
FontFamily {
|
Self::generic(GenericFontFamily::Serif).clone()
|
||||||
families: FontFamilyList::new(Box::new([SingleFontFamily::Generic(
|
}
|
||||||
GenericFontFamily::Serif,
|
|
||||||
)])),
|
/// Returns the font family for `-moz-bullet-font`.
|
||||||
is_system_font: false,
|
#[cfg(feature = "gecko")]
|
||||||
|
pub(crate) fn moz_bullet() -> &'static Self {
|
||||||
|
static_font_family!(MOZ_BULLET, SingleFontFamily::FamilyName(FamilyName {
|
||||||
|
name: atom!("-moz-bullet-font"),
|
||||||
|
syntax: FontFamilyNameSyntax::Identifiers,
|
||||||
|
}));
|
||||||
|
|
||||||
|
&*MOZ_BULLET
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a font family for a single system font.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub fn for_system_font(name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
families: FontFamilyList {
|
||||||
|
list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(FamilyName {
|
||||||
|
name: Atom::from(name),
|
||||||
|
syntax: FontFamilyNameSyntax::Identifiers,
|
||||||
|
}))),
|
||||||
|
fallback: GenericFontFamily::None,
|
||||||
|
},
|
||||||
|
is_system_font: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a generic font family.
|
||||||
|
pub fn generic(generic: GenericFontFamily) -> &'static Self {
|
||||||
|
macro_rules! generic_font_family {
|
||||||
|
($ident:ident, $family:ident) => {
|
||||||
|
static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_font_family!(SERIF, Serif);
|
||||||
|
generic_font_family!(SANS_SERIF, SansSerif);
|
||||||
|
generic_font_family!(MONOSPACE, Monospace);
|
||||||
|
generic_font_family!(CURSIVE, Cursive);
|
||||||
|
generic_font_family!(FANTASY, Fantasy);
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
generic_font_family!(MOZ_EMOJI, MozEmoji);
|
||||||
|
generic_font_family!(SYSTEM_UI, SystemUi);
|
||||||
|
|
||||||
|
match generic {
|
||||||
|
GenericFontFamily::None => {
|
||||||
|
debug_assert!(false, "Bogus caller!");
|
||||||
|
&*SERIF
|
||||||
|
}
|
||||||
|
GenericFontFamily::Serif => &*SERIF,
|
||||||
|
GenericFontFamily::SansSerif => &*SANS_SERIF,
|
||||||
|
GenericFontFamily::Monospace => &*MONOSPACE,
|
||||||
|
GenericFontFamily::Cursive => &*CURSIVE,
|
||||||
|
GenericFontFamily::Fantasy => &*FANTASY,
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
|
||||||
|
GenericFontFamily::SystemUi => &*SYSTEM_UI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl MallocSizeOf for FontFamily {
|
impl MallocSizeOf for FontFamily {
|
||||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
// SharedFontList objects are generally shared from the pointer
|
use malloc_size_of::MallocUnconditionalSizeOf;
|
||||||
// stored in the specified value. So only count this if the
|
// SharedFontList objects are generally measured from the pointer stored
|
||||||
// SharedFontList is unshared.
|
// in the specified value. So only count this if the SharedFontList is
|
||||||
let shared_font_list = self.families.shared_font_list().get();
|
// unshared.
|
||||||
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(shared_font_list) }
|
let shared_font_list = &self.families.list;
|
||||||
|
if shared_font_list.is_unique() {
|
||||||
|
shared_font_list.unconditional_size_of(ops)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +292,15 @@ impl ToCss for FontFamily {
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
let mut iter = self.families.iter();
|
let mut iter = self.families.iter();
|
||||||
iter.next().unwrap().to_css(dest)?;
|
match iter.next() {
|
||||||
|
Some(f) => f.to_css(dest)?,
|
||||||
|
None => {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
return self.families.fallback.to_css(dest);
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
unreachable!();
|
||||||
|
},
|
||||||
|
}
|
||||||
for family in iter {
|
for family in iter {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
family.to_css(dest)?;
|
family.to_css(dest)?;
|
||||||
|
@ -229,15 +309,16 @@ impl ToCss for FontFamily {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The name of a font family of choice.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
/// The name of a font family of choice
|
#[repr(C)]
|
||||||
pub struct FamilyName {
|
pub struct FamilyName {
|
||||||
/// Name of the font family
|
/// Name of the font family.
|
||||||
pub name: Atom,
|
pub name: Atom,
|
||||||
/// Syntax of the font family
|
/// Syntax of the font family.
|
||||||
pub syntax: FontFamilyNameSyntax,
|
pub syntax: FontFamilyNameSyntax,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,11 +372,13 @@ pub enum FontFamilyNameSyntax {
|
||||||
Identifiers,
|
Identifiers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A set of faces that vary in weight, width or slope.
|
||||||
|
/// cbindgen:derive-mut-casts=true
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
|
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
|
||||||
/// A set of faces that vary in weight, width or slope.
|
#[repr(u8)]
|
||||||
pub enum SingleFontFamily {
|
pub enum SingleFontFamily {
|
||||||
/// The name of a font family of choice.
|
/// The name of a font family of choice.
|
||||||
FamilyName(FamilyName),
|
FamilyName(FamilyName),
|
||||||
|
@ -303,11 +386,22 @@ pub enum SingleFontFamily {
|
||||||
Generic(GenericFontFamily),
|
Generic(GenericFontFamily),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn system_ui_enabled(_: &ParserContext) -> bool {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
return static_prefs::pref!("layout.css.system-ui.enabled");
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// A generic font-family name.
|
/// A generic font-family name.
|
||||||
///
|
///
|
||||||
/// The order here is important, if you change it make sure that
|
/// The order here is important, if you change it make sure that
|
||||||
/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
|
/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
|
||||||
/// sSingleGenerics are updated as well.
|
/// sSingleGenerics are updated as well.
|
||||||
|
///
|
||||||
|
/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
|
||||||
|
/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
|
||||||
|
/// bug 1726515.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -323,7 +417,7 @@ pub enum SingleFontFamily {
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
#[repr(u8)]
|
#[repr(u32)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum GenericFontFamily {
|
pub enum GenericFontFamily {
|
||||||
/// No generic family specified, only for internal usage.
|
/// No generic family specified, only for internal usage.
|
||||||
|
@ -337,15 +431,17 @@ pub enum GenericFontFamily {
|
||||||
Monospace,
|
Monospace,
|
||||||
Cursive,
|
Cursive,
|
||||||
Fantasy,
|
Fantasy,
|
||||||
|
#[parse(condition = "system_ui_enabled")]
|
||||||
|
SystemUi,
|
||||||
/// An internal value for emoji font selection.
|
/// An internal value for emoji font selection.
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
MozEmoji,
|
MozEmoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SingleFontFamily {
|
impl Parse for SingleFontFamily {
|
||||||
/// Parse a font-family value.
|
/// Parse a font-family value.
|
||||||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||||
if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
|
if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
|
||||||
return Ok(SingleFontFamily::FamilyName(FamilyName {
|
return Ok(SingleFontFamily::FamilyName(FamilyName {
|
||||||
name: Atom::from(&*value),
|
name: Atom::from(&*value),
|
||||||
|
@ -353,11 +449,11 @@ impl SingleFontFamily {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_ident = input.expect_ident_cloned()?;
|
if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
|
||||||
if let Ok(generic) = GenericFontFamily::from_ident(&first_ident) {
|
|
||||||
return Ok(SingleFontFamily::Generic(generic));
|
return Ok(SingleFontFamily::Generic(generic));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let first_ident = input.expect_ident_cloned()?;
|
||||||
let reserved = match_ignore_ascii_case! { &first_ident,
|
let reserved = match_ignore_ascii_case! { &first_ident,
|
||||||
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
||||||
// "Font family names that happen to be the same as a keyword value
|
// "Font family names that happen to be the same as a keyword value
|
||||||
|
@ -400,8 +496,10 @@ impl SingleFontFamily {
|
||||||
syntax,
|
syntax,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
impl SingleFontFamily {
|
||||||
/// Get the corresponding font-family with Atom
|
/// Get the corresponding font-family with Atom
|
||||||
pub fn from_atom(input: Atom) -> SingleFontFamily {
|
pub fn from_atom(input: Atom) -> SingleFontFamily {
|
||||||
match input {
|
match input {
|
||||||
|
@ -410,6 +508,7 @@ impl SingleFontFamily {
|
||||||
atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
||||||
atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
||||||
atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
||||||
|
atom!("system-ui") => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +518,7 @@ impl SingleFontFamily {
|
||||||
"cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
"cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
||||||
"fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
"fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
||||||
"monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
"monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
||||||
|
"system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,22 +529,20 @@ impl SingleFontFamily {
|
||||||
syntax: FontFamilyNameSyntax::Quoted,
|
syntax: FontFamilyNameSyntax::Quoted,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of font families.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
/// Get the corresponding font-family with family name
|
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
|
||||||
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
|
#[repr(C)]
|
||||||
if family.mName.mRawPtr.is_null() {
|
pub struct FontFamilyList {
|
||||||
debug_assert_ne!(family.mGeneric, GenericFontFamily::None);
|
/// The actual list of font families specified.
|
||||||
return SingleFontFamily::Generic(family.mGeneric);
|
pub list: crate::ArcSlice<SingleFontFamily>,
|
||||||
}
|
/// A fallback font type (none, serif, or sans-serif, generally).
|
||||||
let name = unsafe { Atom::from_raw(family.mName.mRawPtr) };
|
pub fallback: GenericFontFamily,
|
||||||
SingleFontFamily::FamilyName(FamilyName {
|
|
||||||
name,
|
|
||||||
syntax: family.mSyntax,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of font families.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -459,115 +557,52 @@ impl SingleFontFamily {
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
/// A list of SingleFontFamily
|
pub struct FontFamilyList {
|
||||||
pub struct FontFamilyList(Box<[SingleFontFamily]>);
|
/// The actual list of font families specified.
|
||||||
|
pub list: Box<[SingleFontFamily]>,
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)]
|
|
||||||
/// A list of SingleFontFamily
|
|
||||||
pub enum FontFamilyList {
|
|
||||||
/// A strong reference to a Gecko SharedFontList object.
|
|
||||||
SharedFontList(
|
|
||||||
#[compute(no_field_bound)]
|
|
||||||
#[resolve(no_field_bound)]
|
|
||||||
RefPtr<structs::SharedFontList>,
|
|
||||||
),
|
|
||||||
/// A font-family generic ID.
|
|
||||||
Generic(GenericFontFamily),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl ToShmem for FontFamilyList {
|
|
||||||
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
|
|
||||||
// In practice, the only SharedFontList objects we create from shared
|
|
||||||
// style sheets are ones with a single generic entry.
|
|
||||||
Ok(ManuallyDrop::new(match *self {
|
|
||||||
FontFamilyList::SharedFontList(ref r) => {
|
|
||||||
if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) {
|
|
||||||
return Err(String::from(
|
|
||||||
"ToShmem failed for FontFamilyList: cannot handle non-generic families",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
FontFamilyList::Generic(r.mNames[0].mGeneric)
|
|
||||||
},
|
|
||||||
FontFamilyList::Generic(t) => FontFamilyList::Generic(t),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl PartialEq for FontFamilyList {
|
|
||||||
fn eq(&self, other: &FontFamilyList) -> bool {
|
|
||||||
let self_list = self.shared_font_list();
|
|
||||||
let other_list = other.shared_font_list();
|
|
||||||
|
|
||||||
if self_list.mNames.len() != other_list.mNames.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (a, b) in self_list.mNames.iter().zip(other_list.mNames.iter()) {
|
|
||||||
if a.mSyntax != b.mSyntax ||
|
|
||||||
a.mName.mRawPtr != b.mName.mRawPtr ||
|
|
||||||
a.mGeneric != b.mGeneric
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl Eq for FontFamilyList {}
|
|
||||||
|
|
||||||
impl FontFamilyList {
|
impl FontFamilyList {
|
||||||
/// Return FontFamilyList with a vector of SingleFontFamily
|
/// Return iterator of SingleFontFamily
|
||||||
#[cfg(feature = "servo")]
|
pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
|
||||||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
self.list.iter()
|
||||||
FontFamilyList(families)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return FontFamilyList with a vector of SingleFontFamily
|
/// Puts the fallback in the list if needed.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
pub fn normalize(&mut self) {
|
||||||
let fontlist;
|
if self.fallback == GenericFontFamily::None {
|
||||||
let names;
|
return;
|
||||||
unsafe {
|
}
|
||||||
fontlist = bindings::Gecko_SharedFontList_Create();
|
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
|
||||||
names = &mut (*fontlist).mNames;
|
new_list.push(SingleFontFamily::Generic(self.fallback));
|
||||||
names.ensure_capacity(families.len());
|
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If there's a generic font family on the list (which isn't cursive or
|
||||||
|
/// fantasy), then move it to the front of the list. Otherwise, prepend the
|
||||||
|
/// default generic.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub (crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
|
||||||
|
let index_of_first_generic = self.iter().position(|f| {
|
||||||
|
match *f {
|
||||||
|
SingleFontFamily::Generic(f) => f != GenericFontFamily::Cursive && f != GenericFontFamily::Fantasy,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(0) = index_of_first_generic {
|
||||||
|
return; // Already first
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
|
||||||
|
let element_to_prepend = match index_of_first_generic {
|
||||||
|
Some(i) => new_list.remove(i),
|
||||||
|
None => SingleFontFamily::Generic(generic),
|
||||||
};
|
};
|
||||||
|
|
||||||
for family in families.iter() {
|
new_list.insert(0, element_to_prepend);
|
||||||
match *family {
|
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
|
||||||
SingleFontFamily::FamilyName(ref f) => unsafe {
|
|
||||||
bindings::Gecko_nsTArray_FontFamilyName_AppendNamed(
|
|
||||||
names,
|
|
||||||
f.name.as_ptr(),
|
|
||||||
f.syntax,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
SingleFontFamily::Generic(family) => unsafe {
|
|
||||||
bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FontFamilyList::SharedFontList(unsafe { RefPtr::from_addrefed(fontlist) })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return iterator of SingleFontFamily
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
pub fn iter(&self) -> slice::Iter<SingleFontFamily> {
|
|
||||||
self.0.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return iterator of SingleFontFamily
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub fn iter(&self) -> FontFamilyNameIter {
|
|
||||||
FontFamilyNameIter {
|
|
||||||
names: &self.shared_font_list().mNames,
|
|
||||||
cur: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the generic ID if it is a single generic font
|
/// Return the generic ID if it is a single generic font
|
||||||
|
@ -580,67 +615,10 @@ impl FontFamilyList {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a reference to the Gecko SharedFontList.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub fn shared_font_list(&self) -> &RefPtr<structs::SharedFontList> {
|
|
||||||
match *self {
|
|
||||||
FontFamilyList::SharedFontList(ref r) => r,
|
|
||||||
FontFamilyList::Generic(t) => {
|
|
||||||
unsafe {
|
|
||||||
// TODO(heycam): Should really add StaticRefPtr sugar.
|
|
||||||
let index = t as usize;
|
|
||||||
mem::transmute::<
|
|
||||||
&structs::StaticRefPtr<structs::SharedFontList>,
|
|
||||||
&RefPtr<structs::SharedFontList>,
|
|
||||||
>(&structs::SharedFontList_sSingleGenerics[index])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator of FontFamily
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub struct FontFamilyNameIter<'a> {
|
|
||||||
names: &'a structs::nsTArray<structs::FontFamilyName>,
|
|
||||||
cur: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
impl<'a> Iterator for FontFamilyNameIter<'a> {
|
|
||||||
type Item = SingleFontFamily;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.cur < self.names.len() {
|
|
||||||
let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]);
|
|
||||||
self.cur += 1;
|
|
||||||
Some(item)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preserve the readability of text when font fallback occurs
|
/// Preserve the readability of text when font fallback occurs
|
||||||
#[derive(
|
pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
|
||||||
Animate,
|
|
||||||
Clone,
|
|
||||||
ComputeSquaredDistance,
|
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
MallocSizeOf,
|
|
||||||
PartialEq,
|
|
||||||
ToCss,
|
|
||||||
ToResolvedValue,
|
|
||||||
)]
|
|
||||||
pub enum FontSizeAdjust {
|
|
||||||
#[animation(error)]
|
|
||||||
/// None variant
|
|
||||||
None,
|
|
||||||
/// Number variant
|
|
||||||
Number(CSSFloat),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontSizeAdjust {
|
impl FontSizeAdjust {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -648,40 +626,6 @@ impl FontSizeAdjust {
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
FontSizeAdjust::None
|
FontSizeAdjust::None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get font-size-adjust with float number
|
|
||||||
pub fn from_gecko_adjust(gecko: f32) -> Self {
|
|
||||||
if gecko == -1.0 {
|
|
||||||
FontSizeAdjust::None
|
|
||||||
} else {
|
|
||||||
FontSizeAdjust::Number(gecko)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for FontSizeAdjust {
|
|
||||||
#[inline]
|
|
||||||
// FIXME(emilio): why?
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedValue for FontSizeAdjust {
|
|
||||||
type AnimatedValue = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
match animated {
|
|
||||||
FontSizeAdjust::Number(number) => FontSizeAdjust::Number(number.max(0.)),
|
|
||||||
_ => animated,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use VariantAlternatesList as computed type of FontVariantAlternates
|
/// Use VariantAlternatesList as computed type of FontVariantAlternates
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
//! `<length>` computed values, and related ones.
|
//! `<length>` computed values, and related ones.
|
||||||
|
|
||||||
use super::{Context, Number, ToComputedValue};
|
use super::{Context, Number, ToComputedValue};
|
||||||
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::values::animated::ToAnimatedValue;
|
use crate::values::animated::ToAnimatedValue;
|
||||||
use crate::values::computed::NonNegativeNumber;
|
use crate::values::computed::NonNegativeNumber;
|
||||||
use crate::values::generics::length as generics;
|
use crate::values::generics::length as generics;
|
||||||
|
@ -36,6 +37,7 @@ impl ToComputedValue for specified::NoCalcLength {
|
||||||
length.to_computed_value(context, FontBaseSize::CurrentStyle)
|
length.to_computed_value(context, FontBaseSize::CurrentStyle)
|
||||||
},
|
},
|
||||||
specified::NoCalcLength::ViewportPercentage(length) => {
|
specified::NoCalcLength::ViewportPercentage(length) => {
|
||||||
|
context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
|
||||||
length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
|
length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
|
||||||
},
|
},
|
||||||
specified::NoCalcLength::ServoCharacterWidth(length) => {
|
specified::NoCalcLength::ServoCharacterWidth(length) => {
|
||||||
|
@ -188,7 +190,8 @@ impl Size {
|
||||||
GenericSize::MinContent |
|
GenericSize::MinContent |
|
||||||
GenericSize::MaxContent |
|
GenericSize::MaxContent |
|
||||||
GenericSize::MozFitContent |
|
GenericSize::MozFitContent |
|
||||||
GenericSize::MozAvailable => false
|
GenericSize::MozAvailable |
|
||||||
|
GenericSize::FitContentFunction(_) => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -607,22 +607,11 @@ impl CalcLengthPercentageLeaf {
|
||||||
impl PartialOrd for CalcLengthPercentageLeaf {
|
impl PartialOrd for CalcLengthPercentageLeaf {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
use self::CalcLengthPercentageLeaf::*;
|
use self::CalcLengthPercentageLeaf::*;
|
||||||
|
// NOTE: Percentages can't be compared reasonably here because the
|
||||||
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
// percentage basis might be negative, see bug 1709018.
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
_ => None,
|
||||||
_ => {
|
|
||||||
match *self {
|
|
||||||
Length(..) | Percentage(..) => {},
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
debug_unreachable!("Forgot a branch?");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty};
|
||||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||||
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
||||||
pub use self::box_::{TouchAction, VerticalAlign, WillChange};
|
pub use self::box_::{TouchAction, VerticalAlign, WillChange};
|
||||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue};
|
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};
|
||||||
pub use self::column::ColumnCount;
|
pub use self::column::ColumnCount;
|
||||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
||||||
pub use self::easing::TimingFunction;
|
pub use self::easing::TimingFunction;
|
||||||
|
@ -82,7 +82,7 @@ pub use self::position::{
|
||||||
pub use self::ratio::Ratio;
|
pub use self::ratio::Ratio;
|
||||||
pub use self::rect::NonNegativeLengthOrNumberRect;
|
pub use self::rect::NonNegativeLengthOrNumberRect;
|
||||||
pub use self::resolution::Resolution;
|
pub use self::resolution::Resolution;
|
||||||
pub use self::svg::MozContextProperties;
|
pub use self::svg::{DProperty, MozContextProperties};
|
||||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
|
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
|
||||||
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
||||||
pub use self::text::TextUnderlinePosition;
|
pub use self::text::TextUnderlinePosition;
|
||||||
|
|
|
@ -5,10 +5,69 @@
|
||||||
//! Computed @page at-rule properties
|
//! Computed @page at-rule properties
|
||||||
|
|
||||||
use crate::values::computed::length::NonNegativeLength;
|
use crate::values::computed::length::NonNegativeLength;
|
||||||
|
use crate::values::computed::{Context, ToComputedValue};
|
||||||
use crate::values::generics;
|
use crate::values::generics;
|
||||||
use crate::values::generics::size::Size2D;
|
use crate::values::generics::size::Size2D;
|
||||||
|
|
||||||
|
use crate::values::specified::page as specified;
|
||||||
|
pub use generics::page::GenericPageSize;
|
||||||
pub use generics::page::Orientation;
|
pub use generics::page::Orientation;
|
||||||
pub use generics::page::PaperSize;
|
pub use generics::page::PaperSize;
|
||||||
|
|
||||||
/// Computed value of the @page size descriptor
|
/// Computed value of the @page size descriptor
|
||||||
pub type PageSize = generics::page::GenericPageSize<Size2D<NonNegativeLength>>;
|
///
|
||||||
|
/// The spec says that the computed value should be the same as the specified
|
||||||
|
/// value but with all absolute units, but it's not currently possibly observe
|
||||||
|
/// the computed value of page-size.
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
|
||||||
|
#[repr(C, u8)]
|
||||||
|
pub enum PageSize {
|
||||||
|
/// Specified size, paper size, or paper size and orientation.
|
||||||
|
Size(Size2D<NonNegativeLength>),
|
||||||
|
/// `landscape` or `portrait` value, no specified size.
|
||||||
|
Orientation(Orientation),
|
||||||
|
/// `auto` value
|
||||||
|
Auto,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToComputedValue for specified::PageSize {
|
||||||
|
type ComputedValue = PageSize;
|
||||||
|
|
||||||
|
fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
|
||||||
|
match &*self {
|
||||||
|
Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
|
||||||
|
Self::PaperSize(p, Orientation::Landscape) => PageSize::Size(Size2D {
|
||||||
|
width: p.long_edge().to_computed_value(ctx),
|
||||||
|
height: p.short_edge().to_computed_value(ctx),
|
||||||
|
}),
|
||||||
|
Self::PaperSize(p, Orientation::Portrait) => PageSize::Size(Size2D {
|
||||||
|
width: p.short_edge().to_computed_value(ctx),
|
||||||
|
height: p.long_edge().to_computed_value(ctx),
|
||||||
|
}),
|
||||||
|
Self::Orientation(o) => PageSize::Orientation(*o),
|
||||||
|
Self::Auto => PageSize::Auto,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
|
match *computed {
|
||||||
|
PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)),
|
||||||
|
PageSize::Orientation(o) => Self::Orientation(o),
|
||||||
|
PageSize::Auto => Self::Auto,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageSize {
|
||||||
|
/// `auto` value.
|
||||||
|
#[inline]
|
||||||
|
pub fn auto() -> Self {
|
||||||
|
PageSize::Auto
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this is the `auto` value.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_auto(&self) -> bool {
|
||||||
|
matches!(*self, PageSize::Auto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::values::generics::svg as generic;
|
||||||
use crate::values::RGBA;
|
use crate::values::RGBA;
|
||||||
use crate::Zero;
|
use crate::Zero;
|
||||||
|
|
||||||
pub use crate::values::specified::{MozContextProperties, SVGPaintOrder};
|
pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder};
|
||||||
|
|
||||||
/// Computed SVG Paint value
|
/// Computed SVG Paint value
|
||||||
pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;
|
pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;
|
||||||
|
|
|
@ -110,6 +110,14 @@ impl ToComputedValue for specified::WordSpacing {
|
||||||
/// A computed value for the `line-height` property.
|
/// A computed value for the `line-height` property.
|
||||||
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
|
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
|
||||||
|
|
||||||
|
impl WordSpacing {
|
||||||
|
/// Return the `normal` computed value, which is just zero.
|
||||||
|
#[inline]
|
||||||
|
pub fn normal() -> Self {
|
||||||
|
LengthPercentage::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
/// text-overflow.
|
/// text-overflow.
|
||||||
|
|
|
@ -24,6 +24,9 @@ pub type Transform = generic::GenericTransform<TransformOperation>;
|
||||||
pub type TransformOrigin =
|
pub type TransformOrigin =
|
||||||
generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
|
generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
|
||||||
|
|
||||||
|
/// The computed value of the `perspective()` transform function.
|
||||||
|
pub type PerspectiveFunction = generic::PerspectiveFunction<Length>;
|
||||||
|
|
||||||
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
|
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
|
||||||
pub type DirectionVector = Vector3D<CSSFloat>;
|
pub type DirectionVector = Vector3D<CSSFloat>;
|
||||||
|
|
||||||
|
@ -516,8 +519,8 @@ impl ToAnimatedZero for TransformOperation {
|
||||||
generic::TransformOperation::Rotate(_) => {
|
generic::TransformOperation::Rotate(_) => {
|
||||||
Ok(generic::TransformOperation::Rotate(Angle::zero()))
|
Ok(generic::TransformOperation::Rotate(Angle::zero()))
|
||||||
},
|
},
|
||||||
generic::TransformOperation::Perspective(ref l) => Ok(
|
generic::TransformOperation::Perspective(_) => Ok(
|
||||||
generic::TransformOperation::Perspective(l.to_animated_zero()?),
|
generic::TransformOperation::Perspective(generic::PerspectiveFunction::None)
|
||||||
),
|
),
|
||||||
generic::TransformOperation::AccumulateMatrix { .. } |
|
generic::TransformOperation::AccumulateMatrix { .. } |
|
||||||
generic::TransformOperation::InterpolateMatrix { .. } => {
|
generic::TransformOperation::InterpolateMatrix { .. } => {
|
||||||
|
|
|
@ -147,7 +147,7 @@ impl<L> BorderSpacing<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic value for `border-radius`, `outline-radius` and `inset()`.
|
/// A generic value for `border-radius` and `inset()`.
|
||||||
///
|
///
|
||||||
/// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>
|
/// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
|
@ -202,3 +202,60 @@ pub enum FontStyle<Angle> {
|
||||||
#[value_info(starts_with_keyword)]
|
#[value_info(starts_with_keyword)]
|
||||||
Oblique(Angle),
|
Oblique(Angle),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic value for the `font-size-adjust` property.
|
||||||
|
///
|
||||||
|
/// https://www.w3.org/TR/css-fonts-4/#font-size-adjust-prop
|
||||||
|
/// https://github.com/w3c/csswg-drafts/issues/6160
|
||||||
|
/// https://github.com/w3c/csswg-drafts/issues/6288
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(
|
||||||
|
Animate,
|
||||||
|
Clone,
|
||||||
|
ComputeSquaredDistance,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Hash,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToAnimatedValue,
|
||||||
|
ToAnimatedZero,
|
||||||
|
ToComputedValue,
|
||||||
|
ToResolvedValue,
|
||||||
|
ToShmem,
|
||||||
|
)]
|
||||||
|
pub enum GenericFontSizeAdjust<Number> {
|
||||||
|
#[animation(error)]
|
||||||
|
None,
|
||||||
|
// 'ex-height' is the implied basis, so the keyword can be omitted
|
||||||
|
ExHeight(Number),
|
||||||
|
#[value_info(starts_with_keyword)]
|
||||||
|
CapHeight(Number),
|
||||||
|
#[value_info(starts_with_keyword)]
|
||||||
|
ChWidth(Number),
|
||||||
|
#[value_info(starts_with_keyword)]
|
||||||
|
IcWidth(Number),
|
||||||
|
#[value_info(starts_with_keyword)]
|
||||||
|
IcHeight(Number),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Number: ToCss> ToCss for GenericFontSizeAdjust<Number> {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
let (prefix, value) = match self {
|
||||||
|
Self::None => return dest.write_str("none"),
|
||||||
|
Self::ExHeight(v) => ("", v),
|
||||||
|
Self::CapHeight(v) => ("cap-height ", v),
|
||||||
|
Self::ChWidth(v) => ("ch-width ", v),
|
||||||
|
Self::IcWidth(v) => ("ic-width ", v),
|
||||||
|
Self::IcHeight(v) => ("ic-height ", v),
|
||||||
|
};
|
||||||
|
|
||||||
|
dest.write_str(prefix)?;
|
||||||
|
value.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -161,6 +161,10 @@ pub enum GenericSize<LengthPercent> {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[animation(error)]
|
#[animation(error)]
|
||||||
MozAvailable,
|
MozAvailable,
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
#[animation(error)]
|
||||||
|
#[css(function = "fit-content")]
|
||||||
|
FitContentFunction(LengthPercent)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericSize as Size;
|
pub use self::GenericSize as Size;
|
||||||
|
@ -215,6 +219,10 @@ pub enum GenericMaxSize<LengthPercent> {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[animation(error)]
|
#[animation(error)]
|
||||||
MozAvailable,
|
MozAvailable,
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
#[animation(error)]
|
||||||
|
#[css(function = "fit-content")]
|
||||||
|
FitContentFunction(LengthPercent),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericMaxSize as MaxSize;
|
pub use self::GenericMaxSize as MaxSize;
|
||||||
|
@ -227,7 +235,7 @@ impl<LengthPercentage> MaxSize<LengthPercentage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic `<length>` | `<number>` value for the `-moz-tab-size` property.
|
/// A generic `<length>` | `<number>` value for the `tab-size` property.
|
||||||
#[derive(
|
#[derive(
|
||||||
Animate,
|
Animate,
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
@ -95,6 +95,21 @@ impl CounterStyle {
|
||||||
pub fn decimal() -> Self {
|
pub fn decimal() -> Self {
|
||||||
CounterStyle::Name(CustomIdent(atom!("decimal")))
|
CounterStyle::Name(CustomIdent(atom!("decimal")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
|
||||||
|
#[inline]
|
||||||
|
pub fn is_bullet(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
CounterStyle::Name(CustomIdent(ref name)) => {
|
||||||
|
name == &atom!("disc") ||
|
||||||
|
name == &atom!("circle") ||
|
||||||
|
name == &atom!("square") ||
|
||||||
|
name == &atom!("disclosure-closed") ||
|
||||||
|
name == &atom!("disclosure-open")
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for CounterStyle {
|
impl Parse for CounterStyle {
|
||||||
|
|
|
@ -4,22 +4,14 @@
|
||||||
|
|
||||||
//! @page at-rule properties
|
//! @page at-rule properties
|
||||||
|
|
||||||
|
use crate::values::generics::NonNegative;
|
||||||
|
use crate::values::specified::length::AbsoluteLength;
|
||||||
|
|
||||||
/// Page size names.
|
/// Page size names.
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size
|
/// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
Eq,
|
|
||||||
MallocSizeOf,
|
|
||||||
Parse,
|
|
||||||
PartialEq,
|
|
||||||
SpecifiedValueInfo,
|
|
||||||
ToComputedValue,
|
|
||||||
ToCss,
|
|
||||||
ToResolvedValue,
|
|
||||||
ToShmem,
|
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum PaperSize {
|
pub enum PaperSize {
|
||||||
|
@ -45,6 +37,39 @@ pub enum PaperSize {
|
||||||
Ledger,
|
Ledger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaperSize {
|
||||||
|
/// Gets the long edge length of the paper size
|
||||||
|
pub fn long_edge(&self) -> NonNegative<AbsoluteLength> {
|
||||||
|
NonNegative(match *self {
|
||||||
|
PaperSize::A5 => AbsoluteLength::Mm(210.0),
|
||||||
|
PaperSize::A4 => AbsoluteLength::Mm(297.0),
|
||||||
|
PaperSize::A3 => AbsoluteLength::Mm(420.0),
|
||||||
|
PaperSize::B5 => AbsoluteLength::Mm(250.0),
|
||||||
|
PaperSize::B4 => AbsoluteLength::Mm(353.0),
|
||||||
|
PaperSize::JisB5 => AbsoluteLength::Mm(257.0),
|
||||||
|
PaperSize::JisB4 => AbsoluteLength::Mm(364.0),
|
||||||
|
PaperSize::Letter => AbsoluteLength::In(11.0),
|
||||||
|
PaperSize::Legal => AbsoluteLength::In(14.0),
|
||||||
|
PaperSize::Ledger => AbsoluteLength::In(17.0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Gets the short edge length of the paper size
|
||||||
|
pub fn short_edge(&self) -> NonNegative<AbsoluteLength> {
|
||||||
|
NonNegative(match *self {
|
||||||
|
PaperSize::A5 => AbsoluteLength::Mm(148.0),
|
||||||
|
PaperSize::A4 => AbsoluteLength::Mm(210.0),
|
||||||
|
PaperSize::A3 => AbsoluteLength::Mm(297.0),
|
||||||
|
PaperSize::B5 => AbsoluteLength::Mm(176.0),
|
||||||
|
PaperSize::B4 => AbsoluteLength::Mm(250.0),
|
||||||
|
PaperSize::JisB5 => AbsoluteLength::Mm(182.0),
|
||||||
|
PaperSize::JisB4 => AbsoluteLength::Mm(257.0),
|
||||||
|
PaperSize::Letter => AbsoluteLength::In(8.5),
|
||||||
|
PaperSize::Legal => AbsoluteLength::In(8.5),
|
||||||
|
PaperSize::Ledger => AbsoluteLength::In(11.0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Paper orientation
|
/// Paper orientation
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
||||||
|
@ -57,7 +82,6 @@ pub enum PaperSize {
|
||||||
Parse,
|
Parse,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToComputedValue,
|
|
||||||
ToCss,
|
ToCss,
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
|
@ -70,33 +94,25 @@ pub enum Orientation {
|
||||||
Landscape,
|
Landscape,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_portrait(orientation: &Orientation) -> bool {
|
||||||
|
*orientation == Orientation::Portrait
|
||||||
|
}
|
||||||
|
|
||||||
/// Page size property
|
/// Page size property
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
||||||
#[derive(
|
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
MallocSizeOf,
|
|
||||||
PartialEq,
|
|
||||||
SpecifiedValueInfo,
|
|
||||||
ToComputedValue,
|
|
||||||
ToCss,
|
|
||||||
ToResolvedValue,
|
|
||||||
ToShmem,
|
|
||||||
)]
|
|
||||||
#[repr(C, u8)]
|
#[repr(C, u8)]
|
||||||
pub enum GenericPageSize<S> {
|
pub enum GenericPageSize<S> {
|
||||||
/// Page dimensions.
|
|
||||||
Size(S),
|
|
||||||
/// Paper size with no orientation.
|
|
||||||
PaperSize(PaperSize),
|
|
||||||
/// An orientation with no size.
|
|
||||||
Orientation(Orientation),
|
|
||||||
/// Paper size by name, with an orientation.
|
|
||||||
PaperSizeAndOrientation(PaperSize, Orientation),
|
|
||||||
/// `auto` value.
|
/// `auto` value.
|
||||||
Auto,
|
Auto,
|
||||||
|
/// Page dimensions.
|
||||||
|
Size(S),
|
||||||
|
/// An orientation with no size.
|
||||||
|
Orientation(Orientation),
|
||||||
|
/// Paper size by name
|
||||||
|
PaperSize(PaperSize, #[css(skip_if = "is_portrait")] Orientation),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericPageSize as PageSize;
|
pub use self::GenericPageSize as PageSize;
|
||||||
|
|
|
@ -141,6 +141,41 @@ fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
|
||||||
x == y
|
x == y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A value for the `perspective()` transform function, which is either a
|
||||||
|
/// non-negative `<length>` or `none`.
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Deserialize,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToComputedValue,
|
||||||
|
ToCss,
|
||||||
|
ToResolvedValue,
|
||||||
|
ToShmem,
|
||||||
|
)]
|
||||||
|
#[repr(C, u8)]
|
||||||
|
pub enum GenericPerspectiveFunction<L> {
|
||||||
|
/// `none`
|
||||||
|
None,
|
||||||
|
/// A `<length>`.
|
||||||
|
Length(L),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L> GenericPerspectiveFunction<L> {
|
||||||
|
/// Returns `f32::INFINITY` or the result of a function on the length value.
|
||||||
|
pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
|
||||||
|
match *self {
|
||||||
|
Self::None => std::f32::INFINITY,
|
||||||
|
Self::Length(ref l) => f(l),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::GenericPerspectiveFunction as PerspectiveFunction;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Debug,
|
Debug,
|
||||||
|
@ -240,7 +275,7 @@ where
|
||||||
///
|
///
|
||||||
/// The value must be greater than or equal to zero.
|
/// The value must be greater than or equal to zero.
|
||||||
#[css(function)]
|
#[css(function)]
|
||||||
Perspective(Length),
|
Perspective(GenericPerspectiveFunction<Length>),
|
||||||
/// A intermediate type for interpolation of mismatched transform lists.
|
/// A intermediate type for interpolation of mismatched transform lists.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[css(comma, function = "interpolatematrix")]
|
#[css(comma, function = "interpolatematrix")]
|
||||||
|
@ -443,36 +478,38 @@ where
|
||||||
use self::TransformOperation::*;
|
use self::TransformOperation::*;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
|
|
||||||
const TWO_PI: f64 = 2.0f64 * f64::consts::PI;
|
|
||||||
let reference_width = reference_box.map(|v| v.size.width);
|
let reference_width = reference_box.map(|v| v.size.width);
|
||||||
let reference_height = reference_box.map(|v| v.size.height);
|
let reference_height = reference_box.map(|v| v.size.height);
|
||||||
let matrix = match *self {
|
let matrix = match *self {
|
||||||
Rotate3D(ax, ay, az, theta) => {
|
Rotate3D(ax, ay, az, theta) => {
|
||||||
let theta = TWO_PI - theta.radians64();
|
let theta = theta.radians64();
|
||||||
let (ax, ay, az, theta) =
|
let (ax, ay, az, theta) =
|
||||||
get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
|
get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
|
||||||
Transform3D::rotation(
|
Transform3D::rotation(
|
||||||
ax as f64,
|
ax as f64,
|
||||||
ay as f64,
|
ay as f64,
|
||||||
az as f64,
|
az as f64,
|
||||||
-euclid::Angle::radians(theta),
|
euclid::Angle::radians(theta),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
RotateX(theta) => {
|
RotateX(theta) => {
|
||||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
let theta = euclid::Angle::radians(theta.radians64());
|
||||||
Transform3D::rotation(1., 0., 0., -theta)
|
Transform3D::rotation(1., 0., 0., theta)
|
||||||
},
|
},
|
||||||
RotateY(theta) => {
|
RotateY(theta) => {
|
||||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
let theta = euclid::Angle::radians(theta.radians64());
|
||||||
Transform3D::rotation(0., 1., 0., -theta)
|
Transform3D::rotation(0., 1., 0., theta)
|
||||||
},
|
},
|
||||||
RotateZ(theta) | Rotate(theta) => {
|
RotateZ(theta) | Rotate(theta) => {
|
||||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
let theta = euclid::Angle::radians(theta.radians64());
|
||||||
Transform3D::rotation(0., 0., 1., -theta)
|
Transform3D::rotation(0., 0., 1., theta)
|
||||||
},
|
},
|
||||||
Perspective(ref d) => {
|
Perspective(ref p) => {
|
||||||
let m = create_perspective_matrix(d.to_pixel_length(None)?);
|
let px = match p {
|
||||||
m.cast()
|
PerspectiveFunction::None => std::f32::INFINITY,
|
||||||
|
PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
|
||||||
|
};
|
||||||
|
create_perspective_matrix(px).cast()
|
||||||
},
|
},
|
||||||
Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
|
Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
|
||||||
Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
|
Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
|
||||||
|
@ -583,17 +620,10 @@ impl<T: ToMatrix> Transform<T> {
|
||||||
/// Return the transform matrix from a perspective length.
|
/// Return the transform matrix from a perspective length.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
|
pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
|
||||||
// TODO(gw): The transforms spec says that perspective length must
|
if d.is_finite() {
|
||||||
// be positive. However, there is some confusion between the spec
|
Transform3D::perspective(d.max(1.))
|
||||||
// and browser implementations as to handling the case of 0 for the
|
|
||||||
// perspective value. Until the spec bug is resolved, at least ensure
|
|
||||||
// that a provided perspective value of <= 0.0 doesn't cause panics
|
|
||||||
// and behaves as it does in other browsers.
|
|
||||||
// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details.
|
|
||||||
if d <= 0.0 {
|
|
||||||
Transform3D::identity()
|
|
||||||
} else {
|
} else {
|
||||||
Transform3D::perspective(d)
|
Transform3D::identity()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,14 @@
|
||||||
use super::{Context, ToResolvedValue};
|
use super::{Context, ToResolvedValue};
|
||||||
use crate::values::computed;
|
use crate::values::computed;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn allow_element_content_none() -> bool {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
return static_prefs::pref!("layout.css.element-content-none.enabled");
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-content/#content-property
|
/// https://drafts.csswg.org/css-content/#content-property
|
||||||
///
|
///
|
||||||
/// We implement this at resolved value time because otherwise it causes us to
|
/// We implement this at resolved value time because otherwise it causes us to
|
||||||
|
@ -23,19 +31,19 @@ impl ToResolvedValue for computed::Content {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_resolved_value(self, context: &Context) -> Self {
|
fn to_resolved_value(self, context: &Context) -> Self {
|
||||||
let is_before_or_after = context
|
let (is_pseudo, is_before_or_after, is_marker) =
|
||||||
.style
|
match context.style.pseudo() {
|
||||||
.pseudo()
|
Some(ref pseudo) => (true, pseudo.is_before_or_after(), pseudo.is_marker()),
|
||||||
.map_or(false, |p| p.is_before_or_after());
|
None => (false, false, false)
|
||||||
|
};
|
||||||
match self {
|
match self {
|
||||||
Self::Normal if is_before_or_after => Self::None,
|
Self::Normal if is_before_or_after => Self::None,
|
||||||
// For now, make `content: none` compute to `normal` on other
|
// For now, make `content: none` compute to `normal` for pseudos
|
||||||
// elements, as we don't respect it.
|
// other than ::before, ::after and ::marker, as we don't respect it.
|
||||||
//
|
// https://github.com/w3c/csswg-drafts/issues/6124
|
||||||
// FIXME(emilio, bug 1605473): for marker this should be preserved
|
// Ditto for non-pseudo elements if the pref is disabled.
|
||||||
// and respected, probably.
|
Self::None if (is_pseudo && !is_before_or_after && !is_marker) ||
|
||||||
Self::None if !is_before_or_after => Self::Normal,
|
(!is_pseudo && !allow_element_content_none()) => Self::Normal,
|
||||||
other => other,
|
other => other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,15 +55,6 @@ pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
|
||||||
/// The specified value of `Polygon`
|
/// The specified value of `Polygon`
|
||||||
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
|
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
fn is_clip_path_path_enabled(context: &ParserContext) -> bool {
|
|
||||||
context.chrome_rules_enabled() || static_prefs::pref!("layout.css.clip-path-path.enabled")
|
|
||||||
}
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
fn is_clip_path_path_enabled(_: &ParserContext) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper for both clip-path and shape-outside parsing of shapes.
|
/// A helper for both clip-path and shape-outside parsing of shapes.
|
||||||
fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
|
fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
|
@ -116,11 +107,9 @@ impl Parse for ClipPath {
|
||||||
return Ok(ClipPath::None);
|
return Ok(ClipPath::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_clip_path_path_enabled(context) {
|
|
||||||
if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) {
|
if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) {
|
||||||
return Ok(ClipPath::Path(p));
|
return Ok(ClipPath::Path(p));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
|
if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
|
||||||
return Ok(ClipPath::Url(url));
|
return Ok(ClipPath::Url(url));
|
||||||
|
|
|
@ -1838,8 +1838,6 @@ pub enum Appearance {
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
MozWindowTitlebarMaximized,
|
MozWindowTitlebarMaximized,
|
||||||
|
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
|
||||||
MozGtkInfoBar,
|
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
MozMacActiveSourceListSelection,
|
MozMacActiveSourceListSelection,
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
|
@ -1896,28 +1894,20 @@ pub enum BreakBetween {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BreakBetween {
|
impl BreakBetween {
|
||||||
/// Parse a legacy break-between value for `page-break-*`.
|
/// Parse a legacy break-between value for `page-break-{before,after}`.
|
||||||
///
|
///
|
||||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse_legacy<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let break_value = BreakBetween::parse(input)?;
|
||||||
let ident = input.expect_ident()?;
|
|
||||||
let break_value = match BreakBetween::from_ident(ident) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(()) => {
|
|
||||||
return Err(location
|
|
||||||
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
match break_value {
|
match break_value {
|
||||||
BreakBetween::Always => Ok(BreakBetween::Page),
|
BreakBetween::Always => Ok(BreakBetween::Page),
|
||||||
BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
|
BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
|
||||||
Ok(break_value)
|
Ok(break_value)
|
||||||
},
|
},
|
||||||
BreakBetween::Page => {
|
BreakBetween::Page => {
|
||||||
Err(location
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1925,7 +1915,8 @@ impl BreakBetween {
|
||||||
/// Serialize a legacy break-between value for `page-break-*`.
|
/// Serialize a legacy break-between value for `page-break-*`.
|
||||||
///
|
///
|
||||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||||
pub fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
#[cfg(feature = "gecko")]
|
||||||
|
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
|
@ -1962,6 +1953,39 @@ impl BreakBetween {
|
||||||
pub enum BreakWithin {
|
pub enum BreakWithin {
|
||||||
Auto,
|
Auto,
|
||||||
Avoid,
|
Avoid,
|
||||||
|
AvoidPage,
|
||||||
|
AvoidColumn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BreakWithin {
|
||||||
|
/// Parse a legacy break-between value for `page-break-inside`.
|
||||||
|
///
|
||||||
|
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
||||||
|
let break_value = BreakWithin::parse(input)?;
|
||||||
|
match break_value {
|
||||||
|
BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
|
||||||
|
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
|
||||||
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a legacy break-between value for `page-break-inside`.
|
||||||
|
///
|
||||||
|
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
|
||||||
|
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The value for the `overflow-x` / `overflow-y` properties.
|
/// The value for the `overflow-x` / `overflow-y` properties.
|
||||||
|
|
|
@ -107,8 +107,12 @@ impl PartialOrd for Leaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
// NOTE: Percentages can't be compared reasonably here because the
|
||||||
|
// percentage basis might be negative, see bug 1709018.
|
||||||
|
// Conveniently, we only use this for <length-percentage> (for raw
|
||||||
|
// percentages, we go through resolve()).
|
||||||
|
(&Percentage(..), &Percentage(..)) => None,
|
||||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
|
||||||
(&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
|
(&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
|
||||||
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
|
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
|
||||||
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
|
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
|
||||||
|
|
|
@ -9,9 +9,10 @@ use super::AllowQuirks;
|
||||||
use crate::gecko_bindings::structs::nscolor;
|
use crate::gecko_bindings::structs::nscolor;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
||||||
use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor};
|
use crate::values::generics::color::{GenericCaretColor, GenericColorOrAuto};
|
||||||
use crate::values::specified::calc::CalcNode;
|
use crate::values::specified::calc::CalcNode;
|
||||||
use crate::values::specified::Percentage;
|
use crate::values::specified::Percentage;
|
||||||
|
use crate::values::CustomIdent;
|
||||||
use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
|
use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
|
||||||
use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
|
use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
|
||||||
use itoa;
|
use itoa;
|
||||||
|
@ -20,29 +21,67 @@ use std::io::Write as IoWrite;
|
||||||
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
|
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
|
||||||
use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
|
use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
|
||||||
|
|
||||||
|
/// A color space as defined in [1].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color-5/#typedef-colorspace
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||||
|
pub enum ColorSpaceKind {
|
||||||
|
/// The sRGB color space.
|
||||||
|
Srgb,
|
||||||
|
/// The CIEXYZ color space.
|
||||||
|
Xyz,
|
||||||
|
/// The CIELAB color space.
|
||||||
|
Lab,
|
||||||
|
/// The CIELAB color space, expressed in cylindrical coordinates.
|
||||||
|
Lch,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hue adjuster as defined in [1].
|
||||||
|
///
|
||||||
|
/// [1]: https://drafts.csswg.org/css-color-5/#typedef-hue-adjuster
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||||
|
pub enum HueAdjuster {
|
||||||
|
/// The "shorter" angle adjustment.
|
||||||
|
Shorter,
|
||||||
|
/// The "longer" angle adjustment.
|
||||||
|
Longer,
|
||||||
|
/// The "increasing" angle adjustment.
|
||||||
|
Increasing,
|
||||||
|
/// The "decreasing" angle adjustment.
|
||||||
|
Decreasing,
|
||||||
|
/// The "specified" angle adjustment.
|
||||||
|
Specified,
|
||||||
|
}
|
||||||
|
|
||||||
/// A restricted version of the css `color-mix()` function, which only supports
|
/// A restricted version of the css `color-mix()` function, which only supports
|
||||||
/// percentages and sRGB color-space interpolation.
|
/// percentages.
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-color-5/#color-mix
|
/// https://drafts.csswg.org/css-color-5/#color-mix
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct ColorMix {
|
pub struct ColorMix {
|
||||||
|
pub color_space: ColorSpaceKind,
|
||||||
pub left: Color,
|
pub left: Color,
|
||||||
pub left_percentage: Percentage,
|
pub left_percentage: Percentage,
|
||||||
pub right: Color,
|
pub right: Color,
|
||||||
pub right_percentage: Percentage,
|
pub right_percentage: Percentage,
|
||||||
|
pub hue_adjuster: HueAdjuster,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn allow_color_mix() -> bool {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[inline]
|
return static_prefs::pref!("layout.css.color-mix.enabled");
|
||||||
fn allow_color_mix() -> bool {
|
#[cfg(feature = "servo")]
|
||||||
static_prefs::pref!("layout.css.color-mix.enabled")
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn allow_color_mix() -> bool {
|
fn allow_color_mix_color_spaces() -> bool {
|
||||||
false
|
#[cfg(feature = "gecko")]
|
||||||
|
return static_prefs::pref!("layout.css.color-mix.color-spaces.enabled");
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem
|
// NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem
|
||||||
|
@ -61,6 +100,9 @@ impl Parse for ColorMix {
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let color_spaces_enabled = context.chrome_rules_enabled() ||
|
||||||
|
allow_color_mix_color_spaces();
|
||||||
|
|
||||||
input.expect_function_matching("color-mix")?;
|
input.expect_function_matching("color-mix")?;
|
||||||
|
|
||||||
// NOTE(emilio): This implements the syntax described here for now,
|
// NOTE(emilio): This implements the syntax described here for now,
|
||||||
|
@ -69,12 +111,18 @@ impl Parse for ColorMix {
|
||||||
// https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765
|
// https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
input.expect_ident_matching("in")?;
|
input.expect_ident_matching("in")?;
|
||||||
// TODO: support multiple interpolation spaces.
|
let color_space = if color_spaces_enabled {
|
||||||
|
ColorSpaceKind::parse(input)?
|
||||||
|
} else {
|
||||||
input.expect_ident_matching("srgb")?;
|
input.expect_ident_matching("srgb")?;
|
||||||
|
ColorSpaceKind::Srgb
|
||||||
|
};
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
|
|
||||||
let left = Color::parse(context, input)?;
|
let left = Color::parse(context, input)?;
|
||||||
let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok();
|
let left_percentage = input
|
||||||
|
.try_parse(|input| Percentage::parse(context, input))
|
||||||
|
.ok();
|
||||||
|
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
|
|
||||||
|
@ -87,11 +135,18 @@ impl Parse for ColorMix {
|
||||||
|
|
||||||
let left_percentage =
|
let left_percentage =
|
||||||
left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get()));
|
left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get()));
|
||||||
|
|
||||||
|
let hue_adjuster = input
|
||||||
|
.try_parse(|input| HueAdjuster::parse(input))
|
||||||
|
.unwrap_or(HueAdjuster::Shorter);
|
||||||
|
|
||||||
Ok(ColorMix {
|
Ok(ColorMix {
|
||||||
|
color_space,
|
||||||
left,
|
left,
|
||||||
left_percentage,
|
left_percentage,
|
||||||
right,
|
right,
|
||||||
right_percentage,
|
right_percentage,
|
||||||
|
hue_adjuster,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -115,7 +170,9 @@ impl ToCss for ColorMix {
|
||||||
(1.0 - percent.get() - other.get()).abs() <= f32::EPSILON
|
(1.0 - percent.get() - other.get()).abs() <= f32::EPSILON
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.write_str("color-mix(in srgb, ")?;
|
dest.write_str("color-mix(in ")?;
|
||||||
|
self.color_space.to_css(dest)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
self.left.to_css(dest)?;
|
self.left.to_css(dest)?;
|
||||||
if !can_omit(&self.left_percentage, &self.right_percentage, true) {
|
if !can_omit(&self.left_percentage, &self.right_percentage, true) {
|
||||||
dest.write_str(" ")?;
|
dest.write_str(" ")?;
|
||||||
|
@ -127,10 +184,30 @@ impl ToCss for ColorMix {
|
||||||
dest.write_str(" ")?;
|
dest.write_str(" ")?;
|
||||||
self.right_percentage.to_css(dest)?;
|
self.right_percentage.to_css(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.hue_adjuster != HueAdjuster::Shorter {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
self.hue_adjuster.to_css(dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
dest.write_str(")")
|
dest.write_str(")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The color scheme for a specific system color.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum SystemColorScheme {
|
||||||
|
/// The default color-scheme for the document.
|
||||||
|
#[css(skip)]
|
||||||
|
Default,
|
||||||
|
/// A light color scheme.
|
||||||
|
Light,
|
||||||
|
/// A dark color scheme.
|
||||||
|
Dark,
|
||||||
|
}
|
||||||
|
|
||||||
/// Specified color value
|
/// Specified color value
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
|
@ -145,9 +222,10 @@ pub enum Color {
|
||||||
},
|
},
|
||||||
/// A complex color value from computed value
|
/// A complex color value from computed value
|
||||||
Complex(ComputedColor),
|
Complex(ComputedColor),
|
||||||
/// A system color
|
/// Either a system color, or a `-moz-system-color(<system-color>, light|dark)`
|
||||||
|
/// function which allows chrome code to choose between color schemes.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
System(SystemColor),
|
System(SystemColor, SystemColorScheme),
|
||||||
/// A color mix.
|
/// A color mix.
|
||||||
ColorMix(Box<ColorMix>),
|
ColorMix(Box<ColorMix>),
|
||||||
/// Quirksmode-only rule for inheriting color from the body
|
/// Quirksmode-only rule for inheriting color from the body
|
||||||
|
@ -181,13 +259,13 @@ pub enum SystemColor {
|
||||||
TextBackground,
|
TextBackground,
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
TextForeground,
|
TextForeground,
|
||||||
#[css(skip)]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
TextSelectBackground,
|
TextSelectBackground,
|
||||||
#[css(skip)]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
TextSelectForeground,
|
TextSelectForeground,
|
||||||
#[css(skip)]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
TextSelectBackgroundDisabled,
|
TextSelectBackgroundDisabled,
|
||||||
#[css(skip)]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
TextSelectBackgroundAttention,
|
TextSelectBackgroundAttention,
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
TextHighlightBackground,
|
TextHighlightBackground,
|
||||||
|
@ -358,6 +436,10 @@ pub enum SystemColor {
|
||||||
/// colors.
|
/// colors.
|
||||||
MozNativehyperlinktext,
|
MozNativehyperlinktext,
|
||||||
|
|
||||||
|
/// As above, but visited link color.
|
||||||
|
#[css(skip)]
|
||||||
|
MozNativevisitedhyperlinktext,
|
||||||
|
|
||||||
#[parse(aliases = "-moz-hyperlinktext")]
|
#[parse(aliases = "-moz-hyperlinktext")]
|
||||||
Linktext,
|
Linktext,
|
||||||
#[parse(aliases = "-moz-activehyperlinktext")]
|
#[parse(aliases = "-moz-activehyperlinktext")]
|
||||||
|
@ -369,14 +451,20 @@ pub enum SystemColor {
|
||||||
MozComboboxtext,
|
MozComboboxtext,
|
||||||
MozCombobox,
|
MozCombobox,
|
||||||
|
|
||||||
MozGtkInfoBarText,
|
|
||||||
|
|
||||||
/// Color of tree column headers
|
/// Color of tree column headers
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
MozColheadertext,
|
MozColheadertext,
|
||||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
MozColheaderhovertext,
|
MozColheaderhovertext,
|
||||||
|
|
||||||
|
/// Color of text in the (active) titlebar.
|
||||||
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
|
MozGtkTitlebarText,
|
||||||
|
|
||||||
|
/// Color of text in the (inactive) titlebar.
|
||||||
|
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||||
|
MozGtkTitlebarInactiveText,
|
||||||
|
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
End, // Just for array-indexing purposes.
|
End, // Just for array-indexing purposes.
|
||||||
}
|
}
|
||||||
|
@ -384,20 +472,29 @@ pub enum SystemColor {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl SystemColor {
|
impl SystemColor {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn compute(&self, cx: &Context) -> ComputedColor {
|
fn compute(&self, cx: &Context, scheme: SystemColorScheme) -> ComputedColor {
|
||||||
use crate::gecko_bindings::bindings;
|
use crate::gecko_bindings::bindings;
|
||||||
|
|
||||||
let prefs = cx.device().pref_sheet_prefs();
|
let colors = &cx.device().pref_sheet_prefs().mColors;
|
||||||
|
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
||||||
|
|
||||||
|
// TODO: At least Canvas / CanvasText should be color-scheme aware
|
||||||
|
// (probably the link colors too).
|
||||||
convert_nscolor_to_computedcolor(match *self {
|
convert_nscolor_to_computedcolor(match *self {
|
||||||
SystemColor::Canvastext => prefs.mDefaultColor,
|
SystemColor::Canvastext => colors.mDefault,
|
||||||
SystemColor::Canvas => prefs.mDefaultBackgroundColor,
|
SystemColor::Canvas => colors.mDefaultBackground,
|
||||||
SystemColor::Linktext => prefs.mLinkColor,
|
SystemColor::Linktext => colors.mLink,
|
||||||
SystemColor::Activetext => prefs.mActiveLinkColor,
|
SystemColor::Activetext => colors.mActiveLink,
|
||||||
SystemColor::Visitedtext => prefs.mVisitedLinkColor,
|
SystemColor::Visitedtext => colors.mVisitedLink,
|
||||||
|
|
||||||
_ => unsafe {
|
_ => {
|
||||||
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
|
let color = unsafe {
|
||||||
|
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document(), scheme, &style_color_scheme)
|
||||||
|
};
|
||||||
|
if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {
|
||||||
|
return ComputedColor::currentcolor();
|
||||||
|
}
|
||||||
|
color
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -473,6 +570,21 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn parse_moz_system_color<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<(SystemColor, SystemColorScheme), ParseError<'i>> {
|
||||||
|
debug_assert!(context.chrome_rules_enabled());
|
||||||
|
input.expect_function_matching("-moz-system-color")?;
|
||||||
|
input.parse_nested_block(|input| {
|
||||||
|
let color = SystemColor::parse(context, input)?;
|
||||||
|
input.expect_comma()?;
|
||||||
|
let scheme = SystemColorScheme::parse(input)?;
|
||||||
|
Ok((color, scheme))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for Color {
|
impl Parse for Color {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
|
@ -498,7 +610,15 @@ impl Parse for Color {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
{
|
{
|
||||||
if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {
|
if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {
|
||||||
return Ok(Color::System(system));
|
return Ok(Color::System(system, SystemColorScheme::Default));
|
||||||
|
}
|
||||||
|
|
||||||
|
if context.chrome_rules_enabled() {
|
||||||
|
if let Ok((color, scheme)) =
|
||||||
|
input.try_parse(|i| parse_moz_system_color(context, i))
|
||||||
|
{
|
||||||
|
return Ok(Color::System(color, scheme));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +657,17 @@ impl ToCss for Color {
|
||||||
Color::Complex(_) => Ok(()),
|
Color::Complex(_) => Ok(()),
|
||||||
Color::ColorMix(ref mix) => mix.to_css(dest),
|
Color::ColorMix(ref mix) => mix.to_css(dest),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(system) => system.to_css(dest),
|
Color::System(system, scheme) => {
|
||||||
|
if scheme == SystemColorScheme::Default {
|
||||||
|
system.to_css(dest)
|
||||||
|
} else {
|
||||||
|
dest.write_str("-moz-system-color(")?;
|
||||||
|
system.to_css(dest)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
scheme.to_css(dest)?;
|
||||||
|
dest.write_char(')')
|
||||||
|
}
|
||||||
|
},
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::InheritFromBodyQuirk => Ok(()),
|
Color::InheritFromBodyQuirk => Ok(()),
|
||||||
}
|
}
|
||||||
|
@ -696,14 +826,16 @@ impl Color {
|
||||||
let left = mix.left.to_computed_color(context)?.to_animated_value();
|
let left = mix.left.to_computed_color(context)?.to_animated_value();
|
||||||
let right = mix.right.to_computed_color(context)?.to_animated_value();
|
let right = mix.right.to_computed_color(context)?.to_animated_value();
|
||||||
ToAnimatedValue::from_animated_value(AnimatedColor::mix(
|
ToAnimatedValue::from_animated_value(AnimatedColor::mix(
|
||||||
|
mix.color_space,
|
||||||
&left,
|
&left,
|
||||||
mix.left_percentage.get(),
|
mix.left_percentage.get(),
|
||||||
&right,
|
&right,
|
||||||
mix.right_percentage.get(),
|
mix.right_percentage.get(),
|
||||||
|
mix.hue_adjuster,
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::System(system) => system.compute(context?),
|
Color::System(system, scheme) => system.compute(context?, scheme),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()),
|
Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()),
|
||||||
})
|
})
|
||||||
|
@ -818,3 +950,123 @@ impl Parse for CaretColor {
|
||||||
ColorOrAuto::parse(context, input).map(GenericCaretColor)
|
ColorOrAuto::parse(context, input).map(GenericCaretColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Various flags to represent the color-scheme property in an efficient
|
||||||
|
/// way.
|
||||||
|
#[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[value_info(other_values = "light,dark,only")]
|
||||||
|
pub struct ColorSchemeFlags: u8 {
|
||||||
|
/// Whether the author specified `light`.
|
||||||
|
const LIGHT = 1 << 0;
|
||||||
|
/// Whether the author specified `dark`.
|
||||||
|
const DARK = 1 << 1;
|
||||||
|
/// Whether the author specified `only`.
|
||||||
|
const ONLY = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/css-color-adjust/#color-scheme-prop>
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToComputedValue,
|
||||||
|
ToResolvedValue,
|
||||||
|
ToShmem,
|
||||||
|
)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[value_info(other_values = "normal")]
|
||||||
|
pub struct ColorScheme {
|
||||||
|
#[ignore_malloc_size_of = "Arc"]
|
||||||
|
idents: crate::ArcSlice<CustomIdent>,
|
||||||
|
bits: ColorSchemeFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorScheme {
|
||||||
|
/// Returns the `normal` value.
|
||||||
|
pub fn normal() -> Self {
|
||||||
|
Self {
|
||||||
|
idents: Default::default(),
|
||||||
|
bits: ColorSchemeFlags::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ColorScheme {
|
||||||
|
fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||||
|
let mut idents = vec![];
|
||||||
|
let mut bits = ColorSchemeFlags::empty();
|
||||||
|
|
||||||
|
let mut location = input.current_source_location();
|
||||||
|
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||||
|
let mut is_only = false;
|
||||||
|
match_ignore_ascii_case! { &ident,
|
||||||
|
"normal" => {
|
||||||
|
if idents.is_empty() && bits.is_empty() {
|
||||||
|
return Ok(Self::normal());
|
||||||
|
}
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
},
|
||||||
|
"light" => bits.insert(ColorSchemeFlags::LIGHT),
|
||||||
|
"dark" => bits.insert(ColorSchemeFlags::DARK),
|
||||||
|
"only" => {
|
||||||
|
if bits.intersects(ColorSchemeFlags::ONLY) {
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
|
bits.insert(ColorSchemeFlags::ONLY);
|
||||||
|
is_only = true;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_only {
|
||||||
|
if !idents.is_empty() {
|
||||||
|
// Only is allowed either at the beginning or at the end,
|
||||||
|
// but not in the middle.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idents.push(CustomIdent::from_ident(location, &ident, &[])?);
|
||||||
|
}
|
||||||
|
location = input.current_source_location();
|
||||||
|
}
|
||||||
|
|
||||||
|
if idents.is_empty() {
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
idents: crate::ArcSlice::from_iter(idents.into_iter()),
|
||||||
|
bits,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for ColorScheme {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
if self.idents.is_empty() {
|
||||||
|
debug_assert!(self.bits.is_empty());
|
||||||
|
return dest.write_str("normal");
|
||||||
|
}
|
||||||
|
let mut first = true;
|
||||||
|
for ident in self.idents.iter() {
|
||||||
|
if !first {
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
ident.to_css(dest)?;
|
||||||
|
}
|
||||||
|
if self.bits.intersects(ColorSchemeFlags::ONLY) {
|
||||||
|
dest.write_str(" only")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,15 +6,14 @@
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use crate::gecko_bindings::bindings;
|
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
|
use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
|
||||||
use crate::values::computed::{font as computed, Length, NonNegativeLength};
|
use crate::values::computed::{font as computed, Length, NonNegativeLength};
|
||||||
use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
|
use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
|
||||||
use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
|
use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
|
||||||
|
use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust;
|
||||||
use crate::values::generics::font::VariationValue;
|
use crate::values::generics::font::VariationValue;
|
||||||
use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag};
|
use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust};
|
||||||
use crate::values::generics::NonNegative;
|
use crate::values::generics::NonNegative;
|
||||||
use crate::values::specified::length::{FontBaseSize, PX_PER_PT};
|
use crate::values::specified::length::{FontBaseSize, PX_PER_PT};
|
||||||
use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
|
use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
|
||||||
|
@ -23,7 +22,7 @@ use crate::values::CustomIdent;
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use cssparser::{Parser, Token};
|
use cssparser::{Parser, Token};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::values::SequenceWriter;
|
use style_traits::values::SequenceWriter;
|
||||||
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
|
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
|
||||||
|
@ -682,14 +681,6 @@ pub enum FontFamily {
|
||||||
|
|
||||||
impl FontFamily {
|
impl FontFamily {
|
||||||
system_font_methods!(FontFamily, font_family);
|
system_font_methods!(FontFamily, font_family);
|
||||||
|
|
||||||
/// Parse a specified font-family value
|
|
||||||
pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
|
||||||
let values = input.parse_comma_separated(SingleFontFamily::parse)?;
|
|
||||||
Ok(FontFamily::Values(FontFamilyList::new(
|
|
||||||
values.into_boxed_slice(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for FontFamily {
|
impl ToComputedValue for FontFamily {
|
||||||
|
@ -697,8 +688,8 @@ impl ToComputedValue for FontFamily {
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
match *self {
|
match *self {
|
||||||
FontFamily::Values(ref v) => computed::FontFamily {
|
FontFamily::Values(ref list) => computed::FontFamily {
|
||||||
families: v.clone(),
|
families: list.clone(),
|
||||||
is_system_font: false,
|
is_system_font: false,
|
||||||
},
|
},
|
||||||
FontFamily::System(_) => self.compute_system(context),
|
FontFamily::System(_) => self.compute_system(context),
|
||||||
|
@ -712,18 +703,12 @@ impl ToComputedValue for FontFamily {
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl MallocSizeOf for FontFamily {
|
impl MallocSizeOf for FontFamily {
|
||||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
FontFamily::Values(ref v) => {
|
FontFamily::Values(ref v) => {
|
||||||
// Although a SharedFontList object is refcounted, we always
|
// Although the family list is refcounted, we always attribute
|
||||||
// attribute its size to the specified value, as long as it's
|
// its size to the specified value.
|
||||||
// not a value in SharedFontList::sSingleGenerics.
|
v.list.unconditional_size_of(ops)
|
||||||
if matches!(v, FontFamilyList::SharedFontList(_)) {
|
|
||||||
let ptr = v.shared_font_list().get();
|
|
||||||
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThis(ptr) }
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
FontFamily::System(_) => 0,
|
FontFamily::System(_) => 0,
|
||||||
}
|
}
|
||||||
|
@ -735,23 +720,31 @@ impl Parse for FontFamily {
|
||||||
/// <family-name> = <string> | [ <ident>+ ]
|
/// <family-name> = <string> | [ <ident>+ ]
|
||||||
/// TODO: <generic-family>
|
/// TODO: <generic-family>
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
_: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<FontFamily, ParseError<'i>> {
|
) -> Result<FontFamily, ParseError<'i>> {
|
||||||
FontFamily::parse_specified(input)
|
let values = input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
|
||||||
|
Ok(FontFamily::Values(FontFamilyList {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
list: crate::ArcSlice::from_iter(values.into_iter()),
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
list: values.into_boxed_slice(),
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fallback: computed::GenericFontFamily::None,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpecifiedValueInfo for FontFamily {}
|
impl SpecifiedValueInfo for FontFamily {}
|
||||||
|
|
||||||
/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around
|
/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
|
||||||
/// because we want the former to exclude generic family keywords.
|
/// way around because we want the former to exclude generic family keywords.
|
||||||
impl Parse for FamilyName {
|
impl Parse for FamilyName {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
_: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
match SingleFontFamily::parse(input) {
|
match SingleFontFamily::parse(context, input) {
|
||||||
Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
|
Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
|
||||||
Ok(SingleFontFamily::Generic(_)) => {
|
Ok(SingleFontFamily::Generic(_)) => {
|
||||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
|
@ -761,16 +754,13 @@ impl Parse for FamilyName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
|
||||||
)]
|
|
||||||
/// Preserve the readability of text when font fallback occurs
|
/// Preserve the readability of text when font fallback occurs
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||||
|
)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub enum FontSizeAdjust {
|
pub enum FontSizeAdjust {
|
||||||
/// None variant
|
Value(GenericFontSizeAdjust<NonNegativeNumber>),
|
||||||
None,
|
|
||||||
/// Number variant
|
|
||||||
Number(NonNegativeNumber),
|
|
||||||
/// system font
|
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
System(SystemFont),
|
System(SystemFont),
|
||||||
}
|
}
|
||||||
|
@ -779,34 +769,57 @@ impl FontSizeAdjust {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Default value of font-size-adjust
|
/// Default value of font-size-adjust
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
FontSizeAdjust::None
|
FontSizeAdjust::Value(GenericFontSizeAdjust::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
system_font_methods!(FontSizeAdjust, font_size_adjust);
|
system_font_methods!(FontSizeAdjust, font_size_adjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for FontSizeAdjust {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
let location = input.current_source_location();
|
||||||
|
if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled");
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
let basis_enabled = false;
|
||||||
|
let basis = match_ignore_ascii_case! { &ident,
|
||||||
|
"none" => return Ok(FontSizeAdjust::none()),
|
||||||
|
// Check for size adjustment basis keywords if enabled.
|
||||||
|
"ex-height" if basis_enabled => GenericFontSizeAdjust::ExHeight,
|
||||||
|
"cap-height" if basis_enabled => GenericFontSizeAdjust::CapHeight,
|
||||||
|
"ch-width" if basis_enabled => GenericFontSizeAdjust::ChWidth,
|
||||||
|
"ic-width" if basis_enabled => GenericFontSizeAdjust::IcWidth,
|
||||||
|
"ic-height" if basis_enabled => GenericFontSizeAdjust::IcHeight,
|
||||||
|
// Unknown (or disabled) keyword.
|
||||||
|
_ => return Err(location.new_custom_error(
|
||||||
|
::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident)
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
let value = NonNegativeNumber::parse(context, input)?;
|
||||||
|
return Ok(FontSizeAdjust::Value(basis(value)));
|
||||||
|
}
|
||||||
|
// Without a basis keyword, the number refers to the 'ex-height' metric.
|
||||||
|
let value = NonNegativeNumber::parse(context, input)?;
|
||||||
|
Ok(FontSizeAdjust::Value(GenericFontSizeAdjust::ExHeight(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToComputedValue for FontSizeAdjust {
|
impl ToComputedValue for FontSizeAdjust {
|
||||||
type ComputedValue = computed::FontSizeAdjust;
|
type ComputedValue = ComputedFontSizeAdjust;
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
match *self {
|
match *self {
|
||||||
FontSizeAdjust::None => computed::FontSizeAdjust::None,
|
FontSizeAdjust::Value(v) => v.to_computed_value(context),
|
||||||
FontSizeAdjust::Number(ref n) => {
|
|
||||||
// The computed version handles clamping of animated values
|
|
||||||
// itself.
|
|
||||||
computed::FontSizeAdjust::Number(n.to_computed_value(context).0)
|
|
||||||
},
|
|
||||||
FontSizeAdjust::System(_) => self.compute_system(context),
|
FontSizeAdjust::System(_) => self.compute_system(context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_computed_value(computed: &computed::FontSizeAdjust) -> Self {
|
fn from_computed_value(computed: &ComputedFontSizeAdjust) -> Self {
|
||||||
match *computed {
|
Self::Value(ToComputedValue::from_computed_value(computed))
|
||||||
computed::FontSizeAdjust::None => FontSizeAdjust::None,
|
|
||||||
computed::FontSizeAdjust::Number(v) => {
|
|
||||||
FontSizeAdjust::Number(NonNegativeNumber::from_computed_value(&v.into()))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1977,23 +1990,24 @@ impl Parse for FontFeatureSettings {
|
||||||
Debug,
|
Debug,
|
||||||
MallocSizeOf,
|
MallocSizeOf,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
SpecifiedValueInfo,
|
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToResolvedValue,
|
ToResolvedValue,
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
/// Whether user agents are allowed to synthesize bold or oblique font faces
|
/// Whether user agents are allowed to synthesize bold or oblique font faces
|
||||||
/// when a font family lacks bold or italic faces
|
/// when a font family lacks those faces, or a small-caps variant when this is
|
||||||
|
/// not supported by the face.
|
||||||
pub struct FontSynthesis {
|
pub struct FontSynthesis {
|
||||||
/// If a `font-weight` is requested that the font family does not contain,
|
/// If a `font-weight` is requested that the font family does not contain,
|
||||||
/// the user agent may synthesize the requested weight from the weights
|
/// the user agent may synthesize the requested weight from the weights
|
||||||
/// that do exist in the font family.
|
/// that do exist in the font family.
|
||||||
#[css(represents_keyword)]
|
|
||||||
pub weight: bool,
|
pub weight: bool,
|
||||||
/// If a font-style is requested that the font family does not contain,
|
/// If a font-style is requested that the font family does not contain,
|
||||||
/// the user agent may synthesize the requested style from the normal face in the font family.
|
/// the user agent may synthesize the requested style from the normal face in the font family.
|
||||||
#[css(represents_keyword)]
|
|
||||||
pub style: bool,
|
pub style: bool,
|
||||||
|
/// This bit controls whether the user agent is allowed to synthesize small caps variant
|
||||||
|
/// when a font face lacks it.
|
||||||
|
pub small_caps: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontSynthesis {
|
impl FontSynthesis {
|
||||||
|
@ -2003,8 +2017,31 @@ impl FontSynthesis {
|
||||||
FontSynthesis {
|
FontSynthesis {
|
||||||
weight: true,
|
weight: true,
|
||||||
style: true,
|
style: true,
|
||||||
|
small_caps: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
/// Get the 'none' value of font-synthesis
|
||||||
|
pub fn none() -> Self {
|
||||||
|
FontSynthesis {
|
||||||
|
weight: false,
|
||||||
|
style: false,
|
||||||
|
small_caps: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
/// Return true if this is the 'none' value
|
||||||
|
pub fn is_none(&self) -> bool {
|
||||||
|
*self == Self::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn allow_font_synthesis_small_caps() -> bool {
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
return static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled");
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for FontSynthesis {
|
impl Parse for FontSynthesis {
|
||||||
|
@ -2012,26 +2049,22 @@ impl Parse for FontSynthesis {
|
||||||
_: &ParserContext,
|
_: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<FontSynthesis, ParseError<'i>> {
|
) -> Result<FontSynthesis, ParseError<'i>> {
|
||||||
let mut result = FontSynthesis {
|
use crate::values::SelectorParseErrorKind;
|
||||||
weight: false,
|
let mut result = Self::none();
|
||||||
style: false,
|
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||||
};
|
match_ignore_ascii_case! { &ident,
|
||||||
try_match_ident_ignore_ascii_case! { input,
|
"none" if result.is_none() => return Ok(result),
|
||||||
"none" => Ok(result),
|
"weight" if !result.weight => result.weight = true,
|
||||||
"weight" => {
|
"style" if !result.style => result.style = true,
|
||||||
result.weight = true;
|
"small-caps" if !result.small_caps && allow_font_synthesis_small_caps()
|
||||||
if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
|
=> result.small_caps = true,
|
||||||
result.style = true;
|
_ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))),
|
||||||
}
|
}
|
||||||
Ok(result)
|
|
||||||
},
|
|
||||||
"style" => {
|
|
||||||
result.style = true;
|
|
||||||
if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
|
|
||||||
result.weight = true;
|
|
||||||
}
|
}
|
||||||
|
if !result.is_none() {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
},
|
} else {
|
||||||
|
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2041,14 +2074,41 @@ impl ToCss for FontSynthesis {
|
||||||
where
|
where
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
if self.weight && self.style {
|
if self.is_none() {
|
||||||
dest.write_str("weight style")
|
return dest.write_str("none");
|
||||||
} else if self.style {
|
}
|
||||||
dest.write_str("style")
|
|
||||||
} else if self.weight {
|
let mut need_space = false;
|
||||||
dest.write_str("weight")
|
if self.weight {
|
||||||
} else {
|
dest.write_str("weight")?;
|
||||||
dest.write_str("none")
|
need_space = true;
|
||||||
|
}
|
||||||
|
if self.style {
|
||||||
|
if need_space {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
}
|
||||||
|
dest.write_str("style")?;
|
||||||
|
need_space = true;
|
||||||
|
}
|
||||||
|
if self.small_caps {
|
||||||
|
if need_space {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
}
|
||||||
|
dest.write_str("small-caps")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecifiedValueInfo for FontSynthesis {
|
||||||
|
fn collect_completion_keywords(f: KeywordsCollectFn) {
|
||||||
|
f(&[
|
||||||
|
"none",
|
||||||
|
"weight",
|
||||||
|
"style",
|
||||||
|
]);
|
||||||
|
if allow_font_synthesis_small_caps() {
|
||||||
|
f(&["small-caps"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2061,6 +2121,7 @@ impl From<u8> for FontSynthesis {
|
||||||
FontSynthesis {
|
FontSynthesis {
|
||||||
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
|
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
|
||||||
style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
|
style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
|
||||||
|
small_caps: bits & structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8 != 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2077,6 +2138,9 @@ impl From<FontSynthesis> for u8 {
|
||||||
if v.style {
|
if v.style {
|
||||||
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
|
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
|
||||||
}
|
}
|
||||||
|
if v.small_caps {
|
||||||
|
bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8;
|
||||||
|
}
|
||||||
bits
|
bits
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ fn cross_fade_enabled() -> bool {
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn image_set_enabled() -> bool {
|
fn image_set_enabled() -> bool {
|
||||||
static_prefs::pref!("layout.css.image-set.enabled")
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -365,7 +365,14 @@ impl ImageSet {
|
||||||
cors_mode: CorsMode,
|
cors_mode: CorsMode,
|
||||||
only_url: bool,
|
only_url: bool,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_function_matching("image-set")?;
|
let function = input.expect_function()?;
|
||||||
|
match_ignore_ascii_case! { &function,
|
||||||
|
"-webkit-image-set" | "image-set" => {},
|
||||||
|
_ => {
|
||||||
|
let func = function.clone();
|
||||||
|
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
|
||||||
|
}
|
||||||
|
}
|
||||||
let items = input.parse_nested_block(|input| {
|
let items = input.parse_nested_block(|input| {
|
||||||
input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url))
|
input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url))
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -1247,6 +1247,27 @@ macro_rules! parse_size_non_length {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn is_fit_content_function_enabled() -> bool {
|
||||||
|
static_prefs::pref!("layout.css.fit-content-function.enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
macro_rules! parse_fit_content_function {
|
||||||
|
($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
|
||||||
|
if is_fit_content_function_enabled() {
|
||||||
|
if let Ok(length) = $input.try_parse(|input| {
|
||||||
|
input.expect_function_matching("fit-content")?;
|
||||||
|
input.parse_nested_block(|i| {
|
||||||
|
NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
return Ok($size::FitContentFunction(length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Size {
|
impl Size {
|
||||||
/// Parses, with quirks.
|
/// Parses, with quirks.
|
||||||
pub fn parse_quirky<'i, 't>(
|
pub fn parse_quirky<'i, 't>(
|
||||||
|
@ -1255,6 +1276,8 @@ impl Size {
|
||||||
allow_quirks: AllowQuirks,
|
allow_quirks: AllowQuirks,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
parse_size_non_length!(Size, input, "auto" => Auto);
|
parse_size_non_length!(Size, input, "auto" => Auto);
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
parse_fit_content_function!(Size, input, context, allow_quirks);
|
||||||
|
|
||||||
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
||||||
Ok(GenericSize::LengthPercentage(length))
|
Ok(GenericSize::LengthPercentage(length))
|
||||||
|
@ -1287,6 +1310,8 @@ impl MaxSize {
|
||||||
allow_quirks: AllowQuirks,
|
allow_quirks: AllowQuirks,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
parse_size_non_length!(MaxSize, input, "none" => None);
|
parse_size_non_length!(MaxSize, input, "none" => None);
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
parse_fit_content_function!(MaxSize, input, context, allow_quirks);
|
||||||
|
|
||||||
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
||||||
Ok(GenericMaxSize::LengthPercentage(length))
|
Ok(GenericMaxSize::LengthPercentage(length))
|
||||||
|
|
|
@ -67,6 +67,15 @@ impl ListStyleType {
|
||||||
_ => unreachable!("Unknown counter style keyword value"),
|
_ => unreachable!("Unknown counter style keyword value"),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
|
||||||
|
#[inline]
|
||||||
|
pub fn is_bullet(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ListStyleType::CounterStyle(ref style) => style.is_bullet(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub use self::box_::{Clear, Float, Overflow, OverflowAnchor};
|
||||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||||
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
||||||
pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
||||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue};
|
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};
|
||||||
pub use self::column::ColumnCount;
|
pub use self::column::ColumnCount;
|
||||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
||||||
pub use self::easing::TimingFunction;
|
pub use self::easing::TimingFunction;
|
||||||
|
@ -80,7 +80,7 @@ pub use self::position::{PositionComponent, ZIndex};
|
||||||
pub use self::ratio::Ratio;
|
pub use self::ratio::Ratio;
|
||||||
pub use self::rect::NonNegativeLengthOrNumberRect;
|
pub use self::rect::NonNegativeLengthOrNumberRect;
|
||||||
pub use self::resolution::Resolution;
|
pub use self::resolution::Resolution;
|
||||||
pub use self::svg::MozContextProperties;
|
pub use self::svg::{DProperty, MozContextProperties};
|
||||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
|
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
|
||||||
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
||||||
pub use self::svg_path::SVGPathData;
|
pub use self::svg_path::SVGPathData;
|
||||||
|
|
|
@ -23,15 +23,15 @@ impl Parse for PageSize {
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
// Try to parse as <page-size> [ <orientation> ]
|
// Try to parse as <page-size> [ <orientation> ]
|
||||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||||
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
let orientation = input
|
||||||
return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation));
|
.try_parse(Orientation::parse)
|
||||||
}
|
.unwrap_or(Orientation::Portrait);
|
||||||
return Ok(PageSize::PaperSize(paper_size));
|
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||||
}
|
}
|
||||||
// Try to parse as <orientation> [ <page-size> ]
|
// Try to parse as <orientation> [ <page-size> ]
|
||||||
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
||||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||||
return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation));
|
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||||
}
|
}
|
||||||
return Ok(PageSize::Orientation(orientation));
|
return Ok(PageSize::Orientation(orientation));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::values::specified::color::Color;
|
||||||
use crate::values::specified::url::SpecifiedUrl;
|
use crate::values::specified::url::SpecifiedUrl;
|
||||||
use crate::values::specified::AllowQuirks;
|
use crate::values::specified::AllowQuirks;
|
||||||
use crate::values::specified::LengthPercentage;
|
use crate::values::specified::LengthPercentage;
|
||||||
|
use crate::values::specified::SVGPathData;
|
||||||
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
|
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
|
||||||
use crate::values::CustomIdent;
|
use crate::values::CustomIdent;
|
||||||
use cssparser::{Parser, Token};
|
use cssparser::{Parser, Token};
|
||||||
|
@ -334,3 +335,57 @@ impl Parse for MozContextProperties {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The svg d property type.
|
||||||
|
///
|
||||||
|
/// https://svgwg.org/svg2-draft/paths.html#TheDProperty
|
||||||
|
#[derive(
|
||||||
|
Animate,
|
||||||
|
Clone,
|
||||||
|
ComputeSquaredDistance,
|
||||||
|
Debug,
|
||||||
|
Deserialize,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToAnimatedZero,
|
||||||
|
ToComputedValue,
|
||||||
|
ToCss,
|
||||||
|
ToResolvedValue,
|
||||||
|
ToShmem,
|
||||||
|
)]
|
||||||
|
#[repr(C, u8)]
|
||||||
|
pub enum DProperty {
|
||||||
|
/// Path value for path(<string>) or just a <string>.
|
||||||
|
#[css(function)]
|
||||||
|
Path(SVGPathData),
|
||||||
|
/// None value.
|
||||||
|
#[animation(error)]
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DProperty {
|
||||||
|
/// return none.
|
||||||
|
#[inline]
|
||||||
|
pub fn none() -> Self {
|
||||||
|
DProperty::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for DProperty {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
// Parse none.
|
||||||
|
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
|
||||||
|
return Ok(DProperty::none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse possible functions.
|
||||||
|
input.expect_function_matching("path")?;
|
||||||
|
let path_data = input.parse_nested_block(|i| SVGPathData::parse(context, i))?;
|
||||||
|
Ok(DProperty::Path(path_data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,111 @@ impl SVGPathData {
|
||||||
|
|
||||||
SVGPathData(crate::ArcSlice::from_iter(result.into_iter()))
|
SVGPathData(crate::ArcSlice::from_iter(result.into_iter()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Bug 1714238, we may drop this once we use the same data structure for both SVG and
|
||||||
|
// CSS.
|
||||||
|
/// Decode the svg path raw data from Gecko.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
|
||||||
|
use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
|
||||||
|
|
||||||
|
let mut result: Vec<PathCommand> = Vec::new();
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < path.len() {
|
||||||
|
// See EncodeType() and DecodeType() in SVGPathSegUtils.h.
|
||||||
|
// We are using reinterpret_cast<> to encode and decode between u32 and f32, so here we
|
||||||
|
// use to_bits() to decode the type.
|
||||||
|
let seg_type = path[i].to_bits() as u16;
|
||||||
|
i = i + 1;
|
||||||
|
match seg_type {
|
||||||
|
PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
|
||||||
|
PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
|
||||||
|
debug_assert!(i + 1 < path.len());
|
||||||
|
result.push(PathCommand::MoveTo {
|
||||||
|
point: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
|
||||||
|
});
|
||||||
|
i = i + 2;
|
||||||
|
}
|
||||||
|
PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
|
||||||
|
debug_assert!(i + 1 < path.len());
|
||||||
|
result.push(PathCommand::LineTo {
|
||||||
|
point: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
|
||||||
|
});
|
||||||
|
i = i + 2;
|
||||||
|
}
|
||||||
|
PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
|
||||||
|
debug_assert!(i + 5 < path.len());
|
||||||
|
result.push(PathCommand::CurveTo {
|
||||||
|
control1: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
control2: CoordPair::new(path[i + 2], path[i + 3]),
|
||||||
|
point: CoordPair::new(path[i + 4], path[i + 5]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
|
||||||
|
});
|
||||||
|
i = i + 6;
|
||||||
|
}
|
||||||
|
PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
|
||||||
|
debug_assert!(i + 3 < path.len());
|
||||||
|
result.push(PathCommand::QuadBezierCurveTo {
|
||||||
|
control1: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
point: CoordPair::new(path[i + 2], path[i + 3]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
|
||||||
|
});
|
||||||
|
i = i + 4;
|
||||||
|
}
|
||||||
|
PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
|
||||||
|
debug_assert!(i + 6 < path.len());
|
||||||
|
result.push(PathCommand::EllipticalArc {
|
||||||
|
rx: path[i],
|
||||||
|
ry: path[i + 1],
|
||||||
|
angle: path[i + 2],
|
||||||
|
large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
|
||||||
|
sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
|
||||||
|
point: CoordPair::new(path[i + 5], path[i + 6]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
|
||||||
|
});
|
||||||
|
i = i + 7;
|
||||||
|
}
|
||||||
|
PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
|
||||||
|
debug_assert!(i < path.len());
|
||||||
|
result.push(PathCommand::HorizontalLineTo {
|
||||||
|
x: path[i],
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
|
||||||
|
});
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
|
||||||
|
debug_assert!(i < path.len());
|
||||||
|
result.push(PathCommand::VerticalLineTo {
|
||||||
|
y: path[i],
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
|
||||||
|
});
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
|
||||||
|
debug_assert!(i + 3 < path.len());
|
||||||
|
result.push(PathCommand::SmoothCurveTo {
|
||||||
|
control2: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
point: CoordPair::new(path[i + 2], path[i + 3]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
|
||||||
|
});
|
||||||
|
i = i + 4;
|
||||||
|
}
|
||||||
|
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
|
||||||
|
debug_assert!(i + 1 < path.len());
|
||||||
|
result.push(PathCommand::SmoothQuadBezierCurveTo {
|
||||||
|
point: CoordPair::new(path[i], path[i + 1]),
|
||||||
|
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
|
||||||
|
});
|
||||||
|
i = i + 2;
|
||||||
|
}
|
||||||
|
PATHSEG_UNKNOWN | _ => return Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for SVGPathData {
|
impl ToCss for SVGPathData {
|
||||||
|
@ -506,6 +611,17 @@ impl IsAbsolute {
|
||||||
pub fn is_yes(&self) -> bool {
|
pub fn is_yes(&self) -> bool {
|
||||||
*self == IsAbsolute::Yes
|
*self == IsAbsolute::Yes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return Yes if value is true. Otherwise, return No.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn new(value: bool) -> Self {
|
||||||
|
if value {
|
||||||
|
IsAbsolute::Yes
|
||||||
|
} else {
|
||||||
|
IsAbsolute::No
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The path coord type.
|
/// The path coord type.
|
||||||
|
|
|
@ -238,8 +238,14 @@ impl Transform {
|
||||||
Ok(generic::TransformOperation::SkewY(theta))
|
Ok(generic::TransformOperation::SkewY(theta))
|
||||||
},
|
},
|
||||||
"perspective" => {
|
"perspective" => {
|
||||||
let d = specified::Length::parse_non_negative(context, input)?;
|
let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) {
|
||||||
Ok(generic::TransformOperation::Perspective(d))
|
Ok(p) => generic::PerspectiveFunction::Length(p),
|
||||||
|
Err(..) => {
|
||||||
|
input.expect_ident_matching("none")?;
|
||||||
|
generic::PerspectiveFunction::None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(generic::TransformOperation::Perspective(p))
|
||||||
},
|
},
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,8 @@ use std::ptr::NonNull;
|
||||||
use std::{iter, mem};
|
use std::{iter, mem};
|
||||||
use to_shmem_derive::ToShmem;
|
use to_shmem_derive::ToShmem;
|
||||||
|
|
||||||
|
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
|
||||||
|
|
||||||
/// A canary that we stash in ArcSlices.
|
/// A canary that we stash in ArcSlices.
|
||||||
///
|
///
|
||||||
/// Given we cannot use a zero-sized-type for the header, since well, C++
|
/// Given we cannot use a zero-sized-type for the header, since well, C++
|
||||||
|
@ -137,6 +139,22 @@ impl<T> ArcSlice<T> {
|
||||||
std::mem::forget(empty);
|
std::mem::forget(empty);
|
||||||
ptr as *mut _
|
ptr as *mut _
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether there's only one reference to this ArcSlice.
|
||||||
|
pub fn is_unique(&self) -> bool {
|
||||||
|
self.0.with_arc(|arc| arc.is_unique())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||||
|
let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };
|
||||||
|
for el in self.iter() {
|
||||||
|
size += el.size_of(ops);
|
||||||
|
}
|
||||||
|
size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
|
/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
|
||||||
|
|
|
@ -81,10 +81,14 @@ packages = [
|
||||||
files = [
|
files = [
|
||||||
"./components/net/tests/parsable_mime/text",
|
"./components/net/tests/parsable_mime/text",
|
||||||
# These are ignored to avoid diverging from Gecko
|
# These are ignored to avoid diverging from Gecko
|
||||||
|
"./components/style/counter_style/mod.rs",
|
||||||
"./components/style/properties/helpers.mako.rs",
|
"./components/style/properties/helpers.mako.rs",
|
||||||
"./components/style/stylesheets/rule_parser.rs",
|
"./components/style/stylesheets/rule_parser.rs",
|
||||||
"./components/style/stylist.rs",
|
"./components/style/stylist.rs",
|
||||||
|
"./components/style/values/computed/font.rs",
|
||||||
"./components/style/values/computed/image.rs",
|
"./components/style/values/computed/image.rs",
|
||||||
|
"./components/style/values/specified/color.rs",
|
||||||
|
"./components/style/values/specified/transform.rs",
|
||||||
# Mako does not lend itself easily to splitting long lines
|
# Mako does not lend itself easily to splitting long lines
|
||||||
"./components/style/properties/helpers/animated_properties.mako.rs",
|
"./components/style/properties/helpers/animated_properties.mako.rs",
|
||||||
"./components/style/properties/shorthands/text.mako.rs",
|
"./components/style/properties/shorthands/text.mako.rs",
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-position-calc-minmax-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-position-negative-percentage-comparison.html]
|
|
||||||
expected: FAIL
|
|
|
@ -926,51 +926,6 @@
|
||||||
[Web Animations: property <transform> from [scaleZ(2)\] to [scaleZ(2) perspective(500px)\] at (2) should be [scaleZ(2) perspective(250px)\]]
|
[Web Animations: property <transform> from [scaleZ(2)\] to [scaleZ(2) perspective(500px)\] at (2) should be [scaleZ(2) perspective(250px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
[Web Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -203,39 +203,12 @@
|
||||||
[Web Animations: property <transform> from [matrix(1,0,0,1,0,0) rotate(0deg)\] to [matrix(2,0,0,2,0,0) rotate(360deg)\] at (0.5) should be [matrix(1.5,0,0,1.5,0,0) rotate(180deg)\]]
|
[Web Animations: property <transform> from [matrix(1,0,0,1,0,0) rotate(0deg)\] to [matrix(2,0,0,2,0,0) rotate(360deg)\] at (0.5) should be [matrix(1.5,0,0,1.5,0,0) rotate(180deg)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
[Web Animations: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -248,9 +221,6 @@
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
[CSS Transitions with transition: all: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
[Web Animations: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -260,21 +230,9 @@
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
[CSS Transitions with transition: all: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
[Web Animations: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
[Web Animations: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -290,14 +248,5 @@
|
||||||
[Web Animations: property <transform> from [perspective(10px) translateZ(0.5px)\] to [perspective(1px) translateZ(0.5px)\] at (-1) should be [translateZ(0.5px)\]]
|
[Web Animations: property <transform> from [perspective(10px) translateZ(0.5px)\] to [perspective(1px) translateZ(0.5px)\] at (-1) should be [translateZ(0.5px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Web Animations: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
[Web Animations: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
[transform-valid.html]
|
[transform-valid.html]
|
||||||
[e.style['transform'\] = "perspective(none)" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['transform'\] = "translate(1px, 0%)" should set the property value]
|
[e.style['transform'\] = "translate(1px, 0%)" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[perspective-zero-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[perspective-zero-3.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[perspective-zero.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform3d-perspective-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,36 +1,3 @@
|
||||||
[minmax-percentage-serialize.html]
|
[minmax-percentage-serialize.html]
|
||||||
['min(1%, 2%, 3%)' as a specified value should serialize as 'min(1%, 2%, 3%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['min(1%, 2%, 3%)' as a computed value should serialize as 'min(1%, 2%, 3%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['min(3%, 2%, 1%)' as a specified value should serialize as 'min(3%, 2%, 1%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['min(3%, 2%, 1%)' as a computed value should serialize as 'min(3%, 2%, 1%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['max(1%, 2%, 3%)' as a specified value should serialize as 'max(1%, 2%, 3%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['max(1%, 2%, 3%)' as a computed value should serialize as 'max(1%, 2%, 3%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['max(3%, 2%, 1%)' as a specified value should serialize as 'max(3%, 2%, 1%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['max(3%, 2%, 1%)' as a computed value should serialize as 'max(3%, 2%, 1%)'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['min(1%, 2%, 3%) 0px' as a specified value should serialize as 'min(1%, 2%, 3%) 0px'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['min(1%, 2%, 3%) 0px' as a computed value should serialize as 'min(1%, 2%, 3%) 0px'.]
|
['min(1%, 2%, 3%) 0px' as a computed value should serialize as 'min(1%, 2%, 3%) 0px'.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a specified value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a computed value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -7,9 +7,3 @@
|
||||||
|
|
||||||
[target6]
|
[target6]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[target9]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[target11]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue