Auto merge of #24978 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

See individual commits for details.
This commit is contained in:
bors-servo 2019-11-30 16:11:15 -05:00 committed by GitHub
commit 1dd6ad7063
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 1328 additions and 1043 deletions

8
Cargo.lock generated
View file

@ -5333,7 +5333,7 @@ name = "style"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"app_units", "app_units",
"arrayvec 0.4.6", "arrayvec 0.5.1",
"atomic_refcell", "atomic_refcell",
"bindgen", "bindgen",
"bitflags", "bitflags",
@ -5941,11 +5941,11 @@ checksum = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
[[package]] [[package]]
name = "uluru" name = "uluru"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2606e9192f308ddc4f0b3c5d1bf3400e28a70fff956e9d9f46d23b094746d9f" checksum = "6d7b39d0c32eba57d52d334e4bdd150df6e755264eefaa1ae2e7cd125f35e1ca"
dependencies = [ dependencies = [
"arrayvec 0.4.6", "arrayvec 0.5.1",
] ]
[[package]] [[package]]

View file

@ -22,7 +22,7 @@ bitflags = "1.0"
byteorder = "1" byteorder = "1"
canvas_traits = {path = "../canvas_traits"} canvas_traits = {path = "../canvas_traits"}
crossbeam-channel = "0.3" crossbeam-channel = "0.3"
cssparser = "0.27.1" cssparser = "0.27"
embedder_traits = {path = "../embedder_traits"} embedder_traits = {path = "../embedder_traits"}
euclid = "0.20" euclid = "0.20"
fnv = "1.0" fnv = "1.0"

View file

@ -14,7 +14,7 @@ path = "lib.rs"
webgl_backtrace = [] webgl_backtrace = []
[dependencies] [dependencies]
cssparser = "0.27.1" cssparser = "0.27"
euclid = "0.20" euclid = "0.20"
ipc-channel = "0.12" ipc-channel = "0.12"
lazy_static = "1" lazy_static = "1"

View file

@ -514,6 +514,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
false false
} }
fn exports_any_part(&self) -> bool {
false
}
fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> { fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
unsafe { unsafe {
(*self.element.style_attribute()) (*self.element.style_attribute())
@ -999,6 +1003,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
None
}
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
unsafe { self.element.has_class_for_layout(name, case_sensitivity) } unsafe { self.element.has_class_for_layout(name, case_sensitivity) }
@ -1533,6 +1545,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::exported_part called");
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::imported_part called");
None
}
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called"); debug!("ServoThreadSafeLayoutElement::has_class called");
false false

View file

@ -521,6 +521,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
false false
} }
fn exports_any_part(&self) -> bool {
false
}
fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> { fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
unsafe { unsafe {
(*self.element.style_attribute()) (*self.element.style_attribute())
@ -1006,6 +1010,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
None
}
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
unsafe { self.element.has_class_for_layout(name, case_sensitivity) } unsafe { self.element.has_class_for_layout(name, case_sensitivity) }
@ -1540,6 +1552,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::exported_part called");
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::imported_part called");
None
}
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called"); debug!("ServoThreadSafeLayoutElement::has_class called");
false false

View file

@ -28,7 +28,7 @@ servo = [
app_units = "0.7" app_units = "0.7"
content-security-policy = {version = "0.3.0", features = ["serde"], optional = true} content-security-policy = {version = "0.3.0", features = ["serde"], optional = true}
crossbeam-channel = { version = "0.3", optional = true } crossbeam-channel = { version = "0.3", optional = true }
cssparser = "0.27.1" cssparser = "0.27"
euclid = "0.20" euclid = "0.20"
hashglobe = { path = "../hashglobe" } hashglobe = { path = "../hashglobe" }
hyper = { version = "0.12", optional = true } hyper = { version = "0.12", optional = true }

View file

@ -43,7 +43,7 @@ content-security-policy = {version = "0.3.0", features = ["serde"]}
cookie = "0.11" cookie = "0.11"
chrono = "0.4" chrono = "0.4"
crossbeam-channel = "0.3" crossbeam-channel = "0.3"
cssparser = "0.27.1" cssparser = "0.27"
deny_public_fields = {path = "../deny_public_fields"} deny_public_fields = {path = "../deny_public_fields"}
devtools_traits = {path = "../devtools_traits"} devtools_traits = {path = "../devtools_traits"}
dom_struct = {path = "../dom_struct"} dom_struct = {path = "../dom_struct"}

View file

@ -3073,6 +3073,14 @@ impl<'a> SelectorsElement for DomRoot<Element> {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
Element::has_class(&**self, name, case_sensitivity) Element::has_class(&**self, name, case_sensitivity)
} }

View file

@ -22,7 +22,7 @@ bench = []
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
matches = "0.1" matches = "0.1"
cssparser = "0.27.1" cssparser = "0.27"
derive_more = "0.13" derive_more = "0.13"
log = "0.4" log = "0.4"
fxhash = "0.2" fxhash = "0.2"

View file

@ -8,6 +8,7 @@ use crate::nth_index_cache::NthIndexCacheInner;
use crate::parser::{AncestorHashes, Combinator, Component, LocalName}; use crate::parser::{AncestorHashes, Combinator, Component, LocalName};
use crate::parser::{NonTSPseudoClass, Selector, SelectorImpl, SelectorIter, SelectorList}; use crate::parser::{NonTSPseudoClass, Selector, SelectorImpl, SelectorIter, SelectorList};
use crate::tree::Element; use crate::tree::Element;
use smallvec::SmallVec;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::iter; use std::iter;
@ -667,7 +668,41 @@ where
match *selector { match *selector {
Component::Combinator(_) => unreachable!(), Component::Combinator(_) => unreachable!(),
Component::Part(ref parts) => parts.iter().all(|part| element.is_part(part)), Component::Part(ref parts) => {
let mut hosts = SmallVec::<[E; 4]>::new();
let mut host = match element.containing_shadow_host() {
Some(h) => h,
None => return false,
};
loop {
let outer_host = host.containing_shadow_host();
if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host {
break;
}
let outer_host = match outer_host {
Some(h) => h,
None => return false,
};
// TODO(emilio): if worth it, we could early return if
// host doesn't have the exportparts attribute.
hosts.push(host);
host = outer_host;
}
// Translate the part into the right scope.
parts.iter().all(|part| {
let mut part = part.clone();
for host in hosts.iter().rev() {
part = match host.imported_part(&part) {
Some(p) => p,
None => return false,
};
}
element.is_part(&part)
})
},
Component::Slotted(ref selector) => { Component::Slotted(ref selector) => {
// <slots> are never flattened tree slottables. // <slots> are never flattened tree slottables.
!element.is_html_slot_element() && !element.is_html_slot_element() &&

View file

@ -117,6 +117,20 @@ pub trait Element: Sized + Clone + Debug {
case_sensitivity: CaseSensitivity, case_sensitivity: CaseSensitivity,
) -> bool; ) -> bool;
/// Returns the mapping from the `exportparts` attribute in the regular
/// direction, that is, inner-tree -> outer-tree.
fn exported_part(
&self,
name: &<Self::Impl as SelectorImpl>::PartName,
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
/// Returns the mapping from the `exportparts` attribute in the reverse
/// direction, that is, in an outer-tree -> inner-tree direction.
fn imported_part(
&self,
name: &<Self::Impl as SelectorImpl>::PartName,
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool; fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool;
/// Returns whether this element matches `:empty`. /// Returns whether this element matches `:empty`.

View file

@ -485,6 +485,14 @@ impl<T: ?Sized> Arc<T> {
} }
} }
/// Whether or not the `Arc` is a static reference.
#[inline]
pub fn is_static(&self) -> bool {
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values.
self.inner().count.load(Relaxed) == STATIC_REFCOUNT
}
/// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not /// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not
/// a static reference. /// a static reference.
#[inline] #[inline]
@ -501,10 +509,7 @@ impl<T: ?Sized> Drop for Arc<T> {
fn drop(&mut self) { fn drop(&mut self) {
// NOTE(emilio): If you change anything here, make sure that the // NOTE(emilio): If you change anything here, make sure that the
// implementation in layout/style/ServoStyleConstsInlines.h matches! // implementation in layout/style/ServoStyleConstsInlines.h matches!
// if self.is_static() {
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
return; return;
} }

View file

@ -18,8 +18,9 @@ doctest = false
[features] [features]
gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"] gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"]
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever", servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url",
"servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"] "string_cache", "crossbeam-channel", "to_shmem/servo",
"servo_arc/servo"]
servo-layout-2013 = [] servo-layout-2013 = []
servo-layout-2020 = [] servo-layout-2020 = []
gecko_debug = [] gecko_debug = []
@ -28,11 +29,11 @@ gecko_profiler = []
[dependencies] [dependencies]
app_units = "0.7" app_units = "0.7"
arrayvec = "0.4.6" arrayvec = "0.5"
atomic_refcell = "0.1" atomic_refcell = "0.1"
bitflags = "1.0" bitflags = "1.0"
byteorder = "1.0" byteorder = "1.0"
cssparser = "0.27.1" cssparser = "0.27"
crossbeam-channel = { version = "0.3", optional = true } crossbeam-channel = { version = "0.3", optional = true }
derive_more = "0.13" derive_more = "0.13"
new_debug_unreachable = "1.0" new_debug_unreachable = "1.0"
@ -74,7 +75,7 @@ thin-slice = "0.1.0"
to_shmem = {path = "../to_shmem"} to_shmem = {path = "../to_shmem"}
to_shmem_derive = {path = "../to_shmem_derive"} to_shmem_derive = {path = "../to_shmem_derive"}
time = "0.1" time = "0.1"
uluru = "0.3" uluru = "0.4"
unicode-bidi = "0.3" unicode-bidi = "0.3"
unicode-segmentation = "1.0" unicode-segmentation = "1.0"
void = "1.0.2" void = "1.0.2"

View file

@ -16,8 +16,8 @@ use crate::properties::animated_properties::AnimatedProperty;
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId}; use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
use crate::rule_tree::CascadeLevel;
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
use crate::stylesheets::Origin;
use crate::timer::Timer; use crate::timer::Timer;
use crate::values::computed::box_::TransitionProperty; use crate::values::computed::box_::TransitionProperty;
use crate::values::computed::Time; use crate::values::computed::Time;
@ -491,7 +491,7 @@ where
guard guard
.normal_declaration_iter() .normal_declaration_iter()
.filter(|declaration| declaration.is_animatable()) .filter(|declaration| declaration.is_animatable())
.map(|decl| (decl, CascadeLevel::Animations)) .map(|decl| (decl, Origin::Author))
}; };
// This currently ignores visited styles, which seems acceptable, // This currently ignores visited styles, which seems acceptable,

View file

@ -5,11 +5,10 @@
//! Applicable declarations management. //! Applicable declarations management.
use crate::properties::PropertyDeclarationBlock; use crate::properties::PropertyDeclarationBlock;
use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder, StyleSource}; use crate::rule_tree::{CascadeLevel, StyleSource};
use crate::shared_lock::Locked; use crate::shared_lock::Locked;
use servo_arc::Arc; use servo_arc::Arc;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::{self, Debug};
/// List of applicable declarations. This is a transient structure that shuttles /// List of applicable declarations. This is a transient structure that shuttles
/// declarations between selector matching and inserting into the rule tree, and /// declarations between selector matching and inserting into the rule tree, and
@ -31,64 +30,29 @@ const SOURCE_ORDER_BITS: usize = 24;
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1; const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT; const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;
/// We store up-to-15 shadow order levels. /// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy
/// /// for the different trade-offs there.
/// You'd need an element slotted across 16 components with ::slotted rules to const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS;
/// trigger this as of this writing, which looks... Unlikely.
const SHADOW_CASCADE_ORDER_SHIFT: usize = SOURCE_ORDER_BITS;
const SHADOW_CASCADE_ORDER_BITS: usize = 4;
const SHADOW_CASCADE_ORDER_MAX: u8 = (1 << SHADOW_CASCADE_ORDER_BITS) - 1;
const SHADOW_CASCADE_ORDER_MASK: u32 =
(SHADOW_CASCADE_ORDER_MAX as u32) << SHADOW_CASCADE_ORDER_SHIFT;
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS + SHADOW_CASCADE_ORDER_BITS;
const CASCADE_LEVEL_BITS: usize = 4;
const CASCADE_LEVEL_MAX: u8 = (1 << CASCADE_LEVEL_BITS) - 1;
const CASCADE_LEVEL_MASK: u32 = (CASCADE_LEVEL_MAX as u32) << CASCADE_LEVEL_SHIFT;
/// Stores the source order of a block, the cascade level it belongs to, and the /// Stores the source order of a block, the cascade level it belongs to, and the
/// counter needed to handle Shadow DOM cascade order properly. /// counter needed to handle Shadow DOM cascade order properly.
#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
struct ApplicableDeclarationBits(u32); struct ApplicableDeclarationBits(u32);
impl ApplicableDeclarationBits { impl ApplicableDeclarationBits {
fn new( fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
source_order: u32, Self(
cascade_level: CascadeLevel, (source_order & SOURCE_ORDER_MASK) |
shadow_cascade_order: ShadowCascadeOrder, ((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT),
) -> Self { )
debug_assert!(
cascade_level as u8 <= CASCADE_LEVEL_MAX,
"Gotta find more bits!"
);
let mut bits = ::std::cmp::min(source_order, SOURCE_ORDER_MAX);
bits |= ((shadow_cascade_order & SHADOW_CASCADE_ORDER_MAX) as u32) <<
SHADOW_CASCADE_ORDER_SHIFT;
bits |= (cascade_level as u8 as u32) << CASCADE_LEVEL_SHIFT;
ApplicableDeclarationBits(bits)
} }
fn source_order(&self) -> u32 { fn source_order(&self) -> u32 {
(self.0 & SOURCE_ORDER_MASK) >> SOURCE_ORDER_SHIFT self.0 & SOURCE_ORDER_MASK
}
fn shadow_cascade_order(&self) -> ShadowCascadeOrder {
((self.0 & SHADOW_CASCADE_ORDER_MASK) >> SHADOW_CASCADE_ORDER_SHIFT) as ShadowCascadeOrder
} }
fn level(&self) -> CascadeLevel { fn level(&self) -> CascadeLevel {
let byte = ((self.0 & CASCADE_LEVEL_MASK) >> CASCADE_LEVEL_SHIFT) as u8; CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8)
unsafe { CascadeLevel::from_byte(byte) }
}
}
impl Debug for ApplicableDeclarationBits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ApplicableDeclarationBits")
.field("source_order", &self.source_order())
.field("shadow_cascade_order", &self.shadow_cascade_order())
.field("level", &self.level())
.finish()
} }
} }
@ -119,23 +83,17 @@ impl ApplicableDeclarationBlock {
) -> Self { ) -> Self {
ApplicableDeclarationBlock { ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations), source: StyleSource::from_declarations(declarations),
bits: ApplicableDeclarationBits::new(0, level, 0), bits: ApplicableDeclarationBits::new(0, level),
specificity: 0, specificity: 0,
} }
} }
/// Constructs an applicable declaration block from the given components /// Constructs an applicable declaration block from the given components
#[inline] #[inline]
pub fn new( pub fn new(source: StyleSource, order: u32, level: CascadeLevel, specificity: u32) -> Self {
source: StyleSource,
order: u32,
level: CascadeLevel,
specificity: u32,
shadow_cascade_order: ShadowCascadeOrder,
) -> Self {
ApplicableDeclarationBlock { ApplicableDeclarationBlock {
source, source,
bits: ApplicableDeclarationBits::new(order, level, shadow_cascade_order), bits: ApplicableDeclarationBits::new(order, level),
specificity, specificity,
} }
} }
@ -155,9 +113,8 @@ impl ApplicableDeclarationBlock {
/// Convenience method to consume self and return the right thing for the /// Convenience method to consume self and return the right thing for the
/// rule tree to iterate over. /// rule tree to iterate over.
#[inline] #[inline]
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel, ShadowCascadeOrder) { pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) {
let level = self.level(); let level = self.level();
let cascade_order = self.bits.shadow_cascade_order(); (self.source, level)
(self.source, level, cascade_order)
} }
} }

View file

@ -306,8 +306,7 @@ fn parse_declaration_value_block<'i, 't>(
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { ) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
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() {
// FIXME: remove clone() when borrows are non-lexical Ok(token) => token,
Ok(token) => token.clone(),
Err(_) => { Err(_) => {
return Ok(( return Ok((
TokenSerializationType::nothing(), TokenSerializationType::nothing(),
@ -335,8 +334,9 @@ fn parse_declaration_value_block<'i, 't>(
} }
}; };
} }
let last_token_type = match token { let last_token_type = match *token {
Token::Comment(_) => { Token::Comment(_) => {
let serialization_type = token.serialization_type();
let token_slice = input.slice_from(token_start); let token_slice = input.slice_from(token_start);
if !token_slice.ends_with("*/") { if !token_slice.ends_with("*/") {
missing_closing_characters.push_str(if token_slice.ends_with('*') { missing_closing_characters.push_str(if token_slice.ends_with('*') {
@ -345,14 +345,14 @@ fn parse_declaration_value_block<'i, 't>(
"*/" "*/"
}) })
} }
token.serialization_type() serialization_type
}, },
Token::BadUrl(u) => { Token::BadUrl(ref u) => {
let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u); let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone());
return Err(input.new_custom_error(e)); return Err(input.new_custom_error(e));
}, },
Token::BadString(s) => { Token::BadString(ref s) => {
let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s); let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone());
return Err(input.new_custom_error(e)); return Err(input.new_custom_error(e));
}, },
Token::CloseParenthesis => { Token::CloseParenthesis => {
@ -401,13 +401,14 @@ fn parse_declaration_value_block<'i, 't>(
Token::CloseSquareBracket.serialization_type() Token::CloseSquareBracket.serialization_type()
}, },
Token::QuotedString(_) => { Token::QuotedString(_) => {
let serialization_type = token.serialization_type();
let token_slice = input.slice_from(token_start); let token_slice = input.slice_from(token_start);
let quote = &token_slice[..1]; let quote = &token_slice[..1];
debug_assert!(matches!(quote, "\"" | "'")); debug_assert!(matches!(quote, "\"" | "'"));
if !(token_slice.ends_with(quote) && token_slice.len() > 1) { if !(token_slice.ends_with(quote) && token_slice.len() > 1) {
missing_closing_characters.push_str(quote) missing_closing_characters.push_str(quote)
} }
token.serialization_type() serialization_type
}, },
Token::Ident(ref value) | Token::Ident(ref value) |
Token::AtKeyword(ref value) | Token::AtKeyword(ref value) |
@ -417,6 +418,8 @@ fn parse_declaration_value_block<'i, 't>(
Token::Dimension { Token::Dimension {
unit: ref value, .. unit: ref value, ..
} => { } => {
let serialization_type = token.serialization_type();
let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));
if value.ends_with("<EFBFBD>") && input.slice_from(token_start).ends_with("\\") { if value.ends_with("<EFBFBD>") && input.slice_from(token_start).ends_with("\\") {
// Unescaped backslash at EOF in these contexts is interpreted as U+FFFD // Unescaped backslash at EOF in these contexts is interpreted as U+FFFD
// Check the value in case the final backslash was itself escaped. // Check the value in case the final backslash was itself escaped.
@ -424,18 +427,17 @@ fn parse_declaration_value_block<'i, 't>(
// (Unescaped U+FFFD would also work, but removing the backslash is annoying.) // (Unescaped U+FFFD would also work, but removing the backslash is annoying.)
missing_closing_characters.push_str("<EFBFBD>") missing_closing_characters.push_str("<EFBFBD>")
} }
if matches!(token, Token::UnquotedUrl(_)) { if is_unquoted_url {
check_closed!(")"); check_closed!(")");
} }
token.serialization_type() serialization_type
}, },
_ => token.serialization_type(), _ => token.serialization_type(),
}; };
token_start = input.position(); token_start = input.position();
token = match input.next_including_whitespace_and_comments() { token = match input.next_including_whitespace_and_comments() {
// FIXME: remove clone() when borrows are non-lexical Ok(token) => token,
Ok(token) => token.clone(),
Err(..) => return Ok((first_token_type, last_token_type)), Err(..) => return Ok((first_token_type, last_token_type)),
}; };
} }
@ -888,15 +890,12 @@ fn substitute_block<'i>(
let mut set_position_at_next_iteration = false; let mut set_position_at_next_iteration = false;
loop { loop {
let before_this_token = input.position(); let before_this_token = input.position();
// FIXME: remove clone() when borrows are non-lexical let next = input.next_including_whitespace_and_comments();
let next = input
.next_including_whitespace_and_comments()
.map(|t| t.clone());
if set_position_at_next_iteration { if set_position_at_next_iteration {
*position = ( *position = (
before_this_token, before_this_token,
match next { match next {
Ok(ref token) => token.serialization_type(), Ok(token) => token.serialization_type(),
Err(_) => TokenSerializationType::nothing(), Err(_) => TokenSerializationType::nothing(),
}, },
); );

View file

@ -331,7 +331,7 @@ where
} }
/// The ShadowRoot trait. /// The ShadowRoot trait.
pub trait TShadowRoot: Sized + Copy + Clone + PartialEq { pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
/// The concrete node type. /// The concrete node type.
type ConcreteNode: TNode<ConcreteShadowRoot = Self>; type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
@ -523,6 +523,9 @@ pub trait TElement:
/// Returns whether this element has a `part` attribute. /// Returns whether this element has a `part` attribute.
fn has_part_attr(&self) -> bool; fn has_part_attr(&self) -> bool;
/// Returns whether this element exports any part from its shadow tree.
fn exports_any_part(&self) -> bool;
/// The ID for this element. /// The ID for this element.
fn id(&self) -> Option<&WeakAtom>; fn id(&self) -> Option<&WeakAtom>;

View file

@ -172,7 +172,11 @@ where
}; };
for selector in self.selector_list.0.iter() { for selector in self.selector_list.0.iter() {
target_vector.push(Invalidation::new(selector, 0)) target_vector.push(Invalidation::new(
selector,
self.matching_context.current_host.clone(),
0,
))
} }
false false

View file

@ -237,7 +237,13 @@ impl Device {
/// used for viewport unit resolution. /// used for viewport unit resolution.
pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> { pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
self.used_viewport_size.store(true, Ordering::Relaxed); self.used_viewport_size.store(true, Ordering::Relaxed);
self.au_viewport_size()
let pc = match self.pres_context() {
Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)),
};
let size = &pc.mSizeForViewportUnits;
Size2D::new(Au(size.width), Au(size.height))
} }
/// Returns whether we ever looked up the viewport size of the Device. /// Returns whether we ever looked up the viewport size of the Device.
@ -268,13 +274,7 @@ impl Device {
if doc.mIsBeingUsedAsImage() { if doc.mIsBeingUsedAsImage() {
return true; return true;
} }
let document_color_use = static_prefs::pref!("browser.display.document_color_use"); self.pref_sheet_prefs().mUseDocumentColors
let prefs = self.pref_sheet_prefs();
match document_color_use {
1 => true,
2 => prefs.mIsChrome,
_ => !prefs.mUseAccessibilityTheme,
}
} }
/// Returns the default background color. /// Returns the default background color.

View file

@ -140,6 +140,12 @@ impl PseudoElement {
*self == PseudoElement::FieldsetContent *self == PseudoElement::FieldsetContent
} }
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
#[inline]
pub fn is_color_swatch(&self) -> bool {
*self == PseudoElement::MozColorSwatch
}
/// Whether this pseudo-element is lazily-cascaded. /// Whether this pseudo-element is lazily-cascaded.
#[inline] #[inline]
pub fn is_lazy(&self) -> bool { pub fn is_lazy(&self) -> bool {

View file

@ -132,7 +132,7 @@ PRELUDE = '''
RULE_TEMPLATE = ''' RULE_TEMPLATE = '''
("{atom}") => {{{{ ("{atom}") => {{{{
#[allow(unsafe_code)] #[allow(unused_unsafe)] #[allow(unsafe_code)] #[allow(unused_unsafe)]
unsafe {{ $crate::string_cache::Atom::from_index({index}) }} unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}
}}}}; }}}};
'''[1:] '''[1:]

View file

@ -193,6 +193,16 @@ impl ElementSnapshot for GeckoElementSnapshot {
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
} }
#[inline]
fn exported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::exported_part(&*self.mAttrs, name)
}
#[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::imported_part(&*self.mAttrs, name)
}
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_any(Flags::MaybeClass) { if !self.has_any(Flags::MaybeClass) {

View file

@ -82,6 +82,32 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) }) Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) })
} }
#[inline(always)]
pub(super) fn exported_part(
attrs: &[structs::AttrArray_InternalAttr],
name: &Atom,
) -> Option<Atom> {
let attr = find_attr(attrs, &atom!("exportparts"))?;
let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) };
if atom.is_null() {
return None;
}
Some(unsafe { Atom::from_raw(atom) })
}
#[inline(always)]
pub(super) fn imported_part(
attrs: &[structs::AttrArray_InternalAttr],
name: &Atom,
) -> Option<Atom> {
let attr = find_attr(attrs, &atom!("exportparts"))?;
let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };
if atom.is_null() {
return None;
}
Some(unsafe { Atom::from_raw(atom) })
}
/// Given a class or part name, a case sensitivity, and an array of attributes, /// Given a class or part name, a case sensitivity, and an array of attributes,
/// returns whether the attribute has that name. /// returns whether the attribute has that name.
#[inline(always)] #[inline(always)]

View file

@ -144,6 +144,13 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot); pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO(emilio): Maybe print the host or something?
write!(f, "<shadow-root> ({:#x})", self.as_node().opaque().0)
}
}
impl<'lr> PartialEq for GeckoShadowRoot<'lr> { impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
#[inline] #[inline]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
@ -232,8 +239,8 @@ impl<'ln> fmt::Debug for GeckoNode<'ln> {
return write!(f, "<document> ({:#x})", self.opaque().0); return write!(f, "<document> ({:#x})", self.opaque().0);
} }
if self.is_shadow_root() { if let Some(sr) = self.as_shadow_root() {
return write!(f, "<shadow-root> ({:#x})", self.opaque().0); return sr.fmt(f);
} }
write!(f, "<non-text node> ({:#x})", self.opaque().0) write!(f, "<non-text node> ({:#x})", self.opaque().0)
@ -300,7 +307,10 @@ impl<'ln> GeckoNode<'ln> {
fn flattened_tree_parent_is_parent(&self) -> bool { fn flattened_tree_parent_is_parent(&self) -> bool {
use crate::gecko_bindings::structs::*; use crate::gecko_bindings::structs::*;
let flags = self.flags(); let flags = self.flags();
if flags & (NODE_MAY_BE_IN_BINDING_MNGR as u32 | NODE_IS_IN_SHADOW_TREE as u32) != 0 {
// FIXME(emilio): The shadow tree condition seems it shouldn't be needed
// anymore, if we check for slots.
if self.is_in_shadow_tree() {
return false; return false;
} }
@ -758,13 +768,6 @@ impl<'le> GeckoElement<'le> {
data.damage |= damage; data.damage |= damage;
} }
/// This logic is duplicated in Gecko's nsIContent::IsRootOfAnonymousSubtree.
#[inline]
fn is_root_of_anonymous_subtree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_ANONYMOUS_ROOT;
self.flags() & (NODE_IS_ANONYMOUS_ROOT as u32) != 0
}
/// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree. /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
#[inline] #[inline]
fn is_root_of_native_anonymous_subtree(&self) -> bool { fn is_root_of_native_anonymous_subtree(&self) -> bool {
@ -772,11 +775,6 @@ impl<'le> GeckoElement<'le> {
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0; return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
} }
#[inline]
fn is_in_anonymous_subtree(&self) -> bool {
unsafe { bindings::Gecko_IsInAnonymousSubtree(self.0) }
}
/// Returns true if this node is the shadow root of an use-element shadow tree. /// Returns true if this node is the shadow root of an use-element shadow tree.
#[inline] #[inline]
fn is_root_of_use_element_shadow_tree(&self) -> bool { fn is_root_of_use_element_shadow_tree(&self) -> bool {
@ -1025,7 +1023,7 @@ impl<'le> TElement for GeckoElement<'le> {
// StyleChildrenIterator::IsNeeded does, except that it might return // StyleChildrenIterator::IsNeeded does, except that it might return
// true if we used to (but no longer) have anonymous content from // true if we used to (but no longer) have anonymous content from
// ::before/::after, or nsIAnonymousContentCreators. // ::before/::after, or nsIAnonymousContentCreators.
if self.is_in_anonymous_subtree() || if self.is_in_native_anonymous_subtree() ||
self.is_html_slot_element() || self.is_html_slot_element() ||
self.shadow_root().is_some() || self.shadow_root().is_some() ||
self.may_have_anonymous_children() self.may_have_anonymous_children()
@ -1250,6 +1248,11 @@ impl<'le> TElement for GeckoElement<'le> {
.get_bool_flag(nsINode_BooleanFlag::ElementHasPart) .get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
} }
#[inline]
fn exports_any_part(&self) -> bool {
snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some()
}
// FIXME(emilio): we should probably just return a reference to the Atom. // FIXME(emilio): we should probably just return a reference to the Atom.
#[inline] #[inline]
fn id(&self) -> Option<&WeakAtom> { fn id(&self) -> Option<&WeakAtom> {
@ -2192,7 +2195,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline] #[inline]
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
self.state() self.state()
.intersects(NonTSPseudoClass::AnyLink.state_flag()) .intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE)
} }
#[inline] #[inline]
@ -2219,6 +2222,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
} }
#[inline]
fn exported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::exported_part(self.attrs(), name)
}
#[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> {
snapshot_helpers::imported_part(self.attrs(), name)
}
#[inline(always)] #[inline(always)]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
let attr = match self.get_class_attr() { let attr = match self.get_class_attr() {
@ -2241,7 +2254,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline] #[inline]
fn ignores_nth_child_selectors(&self) -> bool { fn ignores_nth_child_selectors(&self) -> bool {
self.is_root_of_anonymous_subtree() self.is_root_of_native_anonymous_subtree()
} }
} }

View file

@ -26,6 +26,7 @@ use std::fmt::{self, Write};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::iter::Cloned; use std::iter::Cloned;
use std::mem::{self, ManuallyDrop}; use std::mem::{self, ManuallyDrop};
use std::num::NonZeroUsize;
use std::ops::Deref; use std::ops::Deref;
use std::{slice, str}; use std::{slice, str};
use style_traits::SpecifiedValueInfo; use style_traits::SpecifiedValueInfo;
@ -48,13 +49,18 @@ macro_rules! local_name {
}; };
} }
/// A handle to a Gecko atom. /// A handle to a Gecko atom. This is a type that can represent either:
///
/// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case
/// the `usize` just holds the pointer value.
///
/// * A byte offset from `gGkAtoms` to the `nsStaticAtom` object (shifted to
/// the left one bit, and with the lower bit set to `1` to differentiate it
/// from the above), so `(offset << 1 | 1)`.
/// ///
/// This is either a strong reference to a dynamic atom (an nsAtom pointer),
/// or an offset from gGkAtoms to the nsStaticAtom object.
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Atom(usize); pub struct Atom(NonZeroUsize);
/// An atom *without* a strong reference. /// An atom *without* a strong reference.
/// ///
@ -86,7 +92,7 @@ fn static_atoms() -> &'static [nsStaticAtom; STATIC_ATOM_COUNT] {
fn valid_static_atom_addr(addr: usize) -> bool { fn valid_static_atom_addr(addr: usize) -> bool {
unsafe { unsafe {
let atoms = static_atoms(); let atoms = static_atoms();
let start = atoms.get_unchecked(0) as *const _; let start = atoms.as_ptr();
let end = atoms.get_unchecked(STATIC_ATOM_COUNT) as *const _; let end = atoms.get_unchecked(STATIC_ATOM_COUNT) as *const _;
let in_range = addr >= start as usize && addr < end as usize; let in_range = addr >= start as usize && addr < end as usize;
let aligned = addr % mem::align_of::<nsStaticAtom>() == 0; let aligned = addr % mem::align_of::<nsStaticAtom>() == 0;
@ -101,9 +107,9 @@ impl Deref for Atom {
fn deref(&self) -> &WeakAtom { fn deref(&self) -> &WeakAtom {
unsafe { unsafe {
let addr = if self.is_static() { let addr = if self.is_static() {
(&gGkAtoms as *const _ as usize) + (self.0 >> 1) (&gGkAtoms as *const _ as usize) + (self.0.get() >> 1)
} else { } else {
self.0 self.0.get()
}; };
debug_assert!(!self.is_static() || valid_static_atom_addr(addr)); debug_assert!(!self.is_static() || valid_static_atom_addr(addr));
WeakAtom::new(addr as *const nsAtom) WeakAtom::new(addr as *const nsAtom)
@ -341,29 +347,29 @@ impl fmt::Display for WeakAtom {
} }
#[inline] #[inline]
unsafe fn make_handle(ptr: *const nsAtom) -> usize { unsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize {
debug_assert!(!ptr.is_null()); debug_assert!(!ptr.is_null());
if !WeakAtom::new(ptr).is_static() { if !WeakAtom::new(ptr).is_static() {
ptr as usize NonZeroUsize::new_unchecked(ptr as usize)
} else { } else {
make_static_handle(ptr as *mut nsStaticAtom) make_static_handle(ptr as *mut nsStaticAtom)
} }
} }
#[inline] #[inline]
unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> usize { unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> NonZeroUsize {
// FIXME(heycam): Use offset_from once it's stabilized. // FIXME(heycam): Use offset_from once it's stabilized.
// https://github.com/rust-lang/rust/issues/41079 // https://github.com/rust-lang/rust/issues/41079
debug_assert!(valid_static_atom_addr(ptr as usize)); debug_assert!(valid_static_atom_addr(ptr as usize));
let base = &gGkAtoms as *const _; let base = &gGkAtoms as *const _;
let offset = ptr as usize - base as usize; let offset = ptr as usize - base as usize;
(offset << 1) | 1 NonZeroUsize::new_unchecked((offset << 1) | 1)
} }
impl Atom { impl Atom {
#[inline] #[inline]
fn is_static(&self) -> bool { fn is_static(&self) -> bool {
self.0 & 1 == 1 self.0.get() & 1 == 1
} }
/// Execute a callback with the atom represented by `ptr`. /// Execute a callback with the atom represented by `ptr`.
@ -378,17 +384,17 @@ impl Atom {
} }
/// Creates a static atom from its index in the static atom table, without /// Creates a static atom from its index in the static atom table, without
/// checking in release builds. /// checking.
#[inline] #[inline]
pub unsafe fn from_index(index: u16) -> Self { pub const unsafe fn from_index_unchecked(index: u16) -> Self {
let ptr = static_atoms().get_unchecked(index as usize) as *const _; // FIXME(emilio): No support for debug_assert! in const fn for now. Note
let handle = make_static_handle(ptr); // that violating this invariant will debug-assert in the `Deref` impl
let atom = Atom(handle); // though.
debug_assert!(valid_static_atom_addr(ptr as usize)); //
debug_assert!(atom.is_static()); // debug_assert!((index as usize) < STATIC_ATOM_COUNT);
debug_assert!((*atom).is_static()); let offset =
debug_assert!(handle == make_handle(atom.as_ptr())); (index as usize) * std::mem::size_of::<nsStaticAtom>() + kGkAtomsArrayOffset as usize;
atom Atom(NonZeroUsize::new_unchecked((offset << 1) | 1))
} }
/// Creates an atom from an atom pointer. /// Creates an atom from an atom pointer.

View file

@ -79,7 +79,13 @@ where
continue; continue;
} }
self_invalidations.push(Invalidation::new(&dependency.selector, 0)); // We pass `None` as a scope, as document state selectors aren't
// affected by the current scope.
self_invalidations.push(Invalidation::new(
&dependency.selector,
/* scope = */ None,
0,
));
} }
} }

View file

@ -62,6 +62,12 @@ pub trait ElementSnapshot: Sized {
/// called if `has_attrs()` returns true. /// called if `has_attrs()` returns true.
fn is_part(&self, name: &Atom) -> bool; fn is_part(&self, name: &Atom) -> bool;
/// See Element::exported_part.
fn exported_part(&self, name: &Atom) -> Option<Atom>;
/// See Element::imported_part.
fn imported_part(&self, name: &Atom) -> Option<Atom>;
/// A callback that should be called for each class of the snapshot. Should /// A callback that should be called for each class of the snapshot. Should
/// only be called if `has_attrs()` returns true. /// only be called if `has_attrs()` returns true.
fn each_class<F>(&self, _: F) fn each_class<F>(&self, _: F)
@ -270,7 +276,10 @@ where
} }
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
self.element.is_link() match self.snapshot().and_then(|s| s.state()) {
Some(state) => state.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE),
None => self.element.is_link(),
}
} }
fn opaque(&self) -> OpaqueElement { fn opaque(&self) -> OpaqueElement {
@ -362,6 +371,20 @@ where
} }
} }
fn exported_part(&self, name: &Atom) -> Option<Atom> {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name),
_ => self.element.exported_part(name),
}
}
fn imported_part(&self, name: &Atom) -> Option<Atom> {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
_ => self.element.imported_part(name),
}
}
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity), Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),

View file

@ -11,6 +11,7 @@ use crate::selector_parser::SelectorImpl;
use selectors::matching::matches_compound_selector_from; use selectors::matching::matches_compound_selector_from;
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext}; use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
use selectors::parser::{Combinator, Component, Selector}; use selectors::parser::{Combinator, Component, Selector};
use selectors::OpaqueElement;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt; use std::fmt;
@ -127,6 +128,12 @@ enum InvalidationKind {
#[derive(Clone)] #[derive(Clone)]
pub struct Invalidation<'a> { pub struct Invalidation<'a> {
selector: &'a Selector<SelectorImpl>, selector: &'a Selector<SelectorImpl>,
/// The right shadow host from where the rule came from, if any.
///
/// This is needed to ensure that we match the selector with the right
/// state, as whether some selectors like :host and ::part() match depends
/// on it.
scope: Option<OpaqueElement>,
/// The offset of the selector pointing to a compound selector. /// The offset of the selector pointing to a compound selector.
/// ///
/// This order is a "parse order" offset, that is, zero is the leftmost part /// This order is a "parse order" offset, that is, zero is the leftmost part
@ -143,9 +150,14 @@ pub struct Invalidation<'a> {
impl<'a> Invalidation<'a> { impl<'a> Invalidation<'a> {
/// Create a new invalidation for a given selector and offset. /// Create a new invalidation for a given selector and offset.
pub fn new(selector: &'a Selector<SelectorImpl>, offset: usize) -> Self { pub fn new(
selector: &'a Selector<SelectorImpl>,
scope: Option<OpaqueElement>,
offset: usize,
) -> Self {
Self { Self {
selector, selector,
scope,
offset, offset,
matched_by_any_previous: false, matched_by_any_previous: false,
} }
@ -471,21 +483,25 @@ where
any_descendant any_descendant
} }
fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool { fn invalidate_parts_in_shadow_tree(
if invalidations.is_empty() { &mut self,
return false; shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
} invalidations: &[Invalidation<'b>],
) -> bool {
let shadow = match self.element.shadow_root() { debug_assert!(!invalidations.is_empty());
Some(s) => s,
None => return false,
};
let mut any = false; let mut any = false;
let mut sibling_invalidations = InvalidationVector::new(); let mut sibling_invalidations = InvalidationVector::new();
for element in shadow.parts() {
for node in shadow.as_node().dom_descendants() {
let element = match node.as_element() {
Some(e) => e,
None => continue,
};
if element.has_part_attr() {
any |= self.invalidate_child( any |= self.invalidate_child(
*element, element,
invalidations, invalidations,
&mut sibling_invalidations, &mut sibling_invalidations,
DescendantInvalidationKind::Part, DescendantInvalidationKind::Part,
@ -497,9 +513,30 @@ where
sibling_invalidations sibling_invalidations
); );
} }
if let Some(shadow) = element.shadow_root() {
if element.exports_any_part() {
any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
}
}
}
any any
} }
fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
if invalidations.is_empty() {
return false;
}
let shadow = match self.element.shadow_root() {
Some(s) => s,
None => return false,
};
self.invalidate_parts_in_shadow_tree(shadow, invalidations)
}
fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool { fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
if invalidations.is_empty() { if invalidations.is_empty() {
return false; return false;
@ -721,12 +758,17 @@ where
self.element, invalidation, invalidation_kind self.element, invalidation, invalidation_kind
); );
let matching_result = matches_compound_selector_from( let matching_result = {
let context = self.processor.matching_context();
context.current_host = invalidation.scope;
matches_compound_selector_from(
&invalidation.selector, &invalidation.selector,
invalidation.offset, invalidation.offset,
self.processor.matching_context(), context,
&self.element, &self.element,
); )
};
let mut invalidated_self = false; let mut invalidated_self = false;
let mut matched = false; let mut matched = false;
@ -809,6 +851,7 @@ where
let next_invalidation = Invalidation { let next_invalidation = Invalidation {
selector: invalidation.selector, selector: invalidation.selector,
scope: invalidation.scope,
offset: next_combinator_offset + 1, offset: next_combinator_offset + 1,
matched_by_any_previous: false, matched_by_any_previous: false,
}; };

View file

@ -158,7 +158,11 @@ where
// If we the visited state changed, we force a restyle here. Matching // If we the visited state changed, we force a restyle here. Matching
// doesn't depend on the actual visited state at all, so we can't look // doesn't depend on the actual visited state at all, so we can't look
// at matching results to decide what to do for this case. // at matching results to decide what to do for this case.
if state_changes.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE) { //
// TODO(emilio): This piece of code should be removed when
// layout.css.always-repaint-on-unvisited is true, since we cannot get
// into this situation in that case.
if state_changes.contains(ElementState::IN_VISITED_OR_UNVISITED_STATE) {
trace!(" > visitedness change, force subtree restyle"); trace!(" > visitedness change, force subtree restyle");
// We can't just return here because there may also be attribute // We can't just return here because there may also be attribute
// changes as well that imply additional hints for siblings. // changes as well that imply additional hints for siblings.
@ -453,6 +457,7 @@ where
let invalidation = Invalidation::new( let invalidation = Invalidation::new(
&dependency.selector, &dependency.selector,
self.matching_context.current_host.clone(),
dependency.selector.len() - dependency.selector_offset + 1, dependency.selector.len() - dependency.selector_offset + 1,
); );

View file

@ -137,12 +137,12 @@ trait PrivateMatchMethods: TElement {
if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) { if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute(); let style_attribute = self.style_attribute();
result |= replace_rule_node( result |= replace_rule_node(
CascadeLevel::StyleAttributeNormal, CascadeLevel::same_tree_author_normal(),
style_attribute, style_attribute,
primary_rules, primary_rules,
); );
result |= replace_rule_node( result |= replace_rule_node(
CascadeLevel::StyleAttributeImportant, CascadeLevel::same_tree_author_important(),
style_attribute, style_attribute,
primary_rules, primary_rules,
); );

View file

@ -15,7 +15,7 @@ use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
use crate::properties::CASCADE_PROPERTY; use crate::properties::CASCADE_PROPERTY;
use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::{CascadeLevel, StrongRuleNode}; use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use crate::stylesheets::{Origin, PerOrigin}; use crate::stylesheets::{Origin, PerOrigin};
use servo_arc::Arc; use servo_arc::Arc;
@ -134,11 +134,15 @@ where
let restriction = pseudo.and_then(|p| p.property_restriction()); let restriction = pseudo.and_then(|p| p.property_restriction());
let iter_declarations = || { let iter_declarations = || {
rule_node.self_and_ancestors().flat_map(|node| { rule_node.self_and_ancestors().flat_map(|node| {
let cascade_level = node.cascade_level(); let origin = node.cascade_level().origin();
let node_importance = node.importance(); let node_importance = node.importance();
let guard = match origin {
Origin::Author => guards.author,
Origin::User | Origin::UserAgent => guards.ua_or_user,
};
let declarations = match node.style_source() { let declarations = match node.style_source() {
Some(source) => source Some(source) => source
.read(cascade_level.guard(guards)) .read(guard)
.declaration_importance_iter(), .declaration_importance_iter(),
None => DeclarationImportanceIterator::new(&[], &empty), None => DeclarationImportanceIterator::new(&[], &empty),
}; };
@ -153,14 +157,14 @@ where
// longhands are only allowed if they have our // longhands are only allowed if they have our
// restriction flag set. // restriction flag set.
if let PropertyDeclarationId::Longhand(id) = declaration.id() { if let PropertyDeclarationId::Longhand(id) = declaration.id() {
if !id.flags().contains(restriction) && cascade_level.origin() != Origin::UserAgent { if !id.flags().contains(restriction) && origin != Origin::UserAgent {
return None; return None;
} }
} }
} }
if declaration_importance == node_importance { if declaration_importance == node_importance {
Some((declaration, cascade_level)) Some((declaration, origin))
} else { } else {
None None
} }
@ -223,7 +227,7 @@ pub fn apply_declarations<'a, E, F, I>(
where where
E: TElement, E: TElement,
F: Fn() -> I, F: Fn() -> I,
I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>, I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
{ {
debug_assert!(layout_parent_style.is_none() || parent_style.is_some()); debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
debug_assert_eq!( debug_assert_eq!(
@ -242,17 +246,17 @@ where
let inherited_style = parent_style.unwrap_or(device.default_computed_values()); let inherited_style = parent_style.unwrap_or(device.default_computed_values());
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new(); let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
let custom_properties = { let custom_properties = {
let mut builder = CustomPropertiesBuilder::new( let mut builder = CustomPropertiesBuilder::new(
inherited_style.custom_properties(), inherited_style.custom_properties(),
device.environment(), device.environment(),
); );
for (declaration, cascade_level) in iter_declarations() { for (declaration, origin) in iter_declarations() {
declarations.push((declaration, cascade_level)); declarations.push((declaration, origin));
if let PropertyDeclaration::Custom(ref declaration) = *declaration { if let PropertyDeclaration::Custom(ref declaration) = *declaration {
builder.cascade(declaration, cascade_level.origin()); builder.cascade(declaration, origin);
} }
} }
@ -332,7 +336,7 @@ where
fn should_ignore_declaration_when_ignoring_document_colors( fn should_ignore_declaration_when_ignoring_document_colors(
device: &Device, device: &Device,
longhand_id: LonghandId, longhand_id: LonghandId,
cascade_level: CascadeLevel, origin: Origin,
pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
declaration: &mut Cow<PropertyDeclaration>, declaration: &mut Cow<PropertyDeclaration>,
) -> bool { ) -> bool {
@ -340,22 +344,15 @@ fn should_ignore_declaration_when_ignoring_document_colors(
return false; return false;
} }
let is_ua_or_user_rule = let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
matches!(cascade_level.origin(), Origin::User | Origin::UserAgent);
if is_ua_or_user_rule { if is_ua_or_user_rule {
return false; return false;
} }
let is_style_attribute = matches!( // Don't override background-color on ::-moz-color-swatch. It is set as an
cascade_level, // author style (via the style attribute), but it's pretty important for it
CascadeLevel::StyleAttributeNormal | CascadeLevel::StyleAttributeImportant // to show up for obvious reasons :)
); if pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
// Don't override colors on pseudo-element's style attributes. The
// background-color on ::-moz-color-swatch is an example. Those are set
// as an author style (via the style attribute), but it's pretty
// important for it to show up for obvious reasons :)
if pseudo.is_some() && is_style_attribute {
return false; return false;
} }
@ -452,7 +449,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
declarations: I, declarations: I,
) where ) where
Phase: CascadePhase, Phase: CascadePhase,
I: Iterator<Item = (&'decls PropertyDeclaration, CascadeLevel)>, I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
{ {
let apply_reset = apply_reset == ApplyResetProperties::Yes; let apply_reset = apply_reset == ApplyResetProperties::Yes;
@ -465,9 +462,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let ignore_colors = !self.context.builder.device.use_document_colors(); let ignore_colors = !self.context.builder.device.use_document_colors();
for (declaration, cascade_level) in declarations { for (declaration, origin) in declarations {
let declaration_id = declaration.id(); let declaration_id = declaration.id();
let origin = cascade_level.origin();
let longhand_id = match declaration_id { let longhand_id = match declaration_id {
PropertyDeclarationId::Longhand(id) => id, PropertyDeclarationId::Longhand(id) => id,
PropertyDeclarationId::Custom(..) => continue, PropertyDeclarationId::Custom(..) => continue,
@ -515,7 +511,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let should_ignore = should_ignore_declaration_when_ignoring_document_colors( let should_ignore = should_ignore_declaration_when_ignoring_document_colors(
self.context.builder.device, self.context.builder.device,
longhand_id, longhand_id,
cascade_level, origin,
self.context.builder.pseudo, self.context.builder.pseudo,
&mut declaration, &mut declaration,
); );

View file

@ -257,10 +257,12 @@ class Longhand(object):
def type(): def type():
return "longhand" return "longhand"
# For a given logical property return all the physical # For a given logical property return all the physical property names
# property names corresponding to it. # corresponding to it.
def all_physical_mapped_properties(self): def all_physical_mapped_properties(self, data):
assert self.logical if not self.logical:
return []
candidates = [s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS candidates = [s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS
if s in self.name] + [s for s in LOGICAL_AXES if self.name.endswith(s)] if s in self.name] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
assert(len(candidates) == 1) assert(len(candidates) == 1)
@ -270,7 +272,7 @@ class Longhand(object):
else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \ else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \
else PHYSICAL_AXES if logical_side in LOGICAL_AXES \ else PHYSICAL_AXES if logical_side in LOGICAL_AXES \
else LOGICAL_CORNERS else LOGICAL_CORNERS
return [to_phys(self.name, logical_side, physical_side) return [data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
for physical_side in physical] for physical_side in physical]
def experimental(self, engine): def experimental(self, engine):

View file

@ -46,7 +46,7 @@ use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc, UniqueArc}; use servo_arc::{Arc, RawOffsetArc, UniqueArc};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::{forget, zeroed, ManuallyDrop}; use std::mem::{forget, MaybeUninit};
use std::{cmp, ops, ptr}; use std::{cmp, ops, ptr};
use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
use crate::values::computed::{Percentage, TransitionProperty}; use crate::values::computed::{Percentage, TransitionProperty};
@ -218,7 +218,7 @@ impl ComputedValuesInner {
unsafe { unsafe {
let mut arc = UniqueArc::<ComputedValues>::new_uninit(); let mut arc = UniqueArc::<ComputedValues>::new_uninit();
bindings::Gecko_ComputedStyle_Init( bindings::Gecko_ComputedStyle_Init(
&mut (*arc.as_mut_ptr()).0, arc.as_mut_ptr() as *mut _,
&self, &self,
pseudo_ty, pseudo_ty,
); );
@ -621,14 +621,17 @@ def set_gecko_property(ffi_name, expr):
impl ${style_struct.gecko_struct_name} { impl ${style_struct.gecko_struct_name} {
#[allow(dead_code, unused_variables)] #[allow(dead_code, unused_variables)]
pub fn default(document: &structs::Document) -> Arc<Self> { pub fn default(document: &structs::Document) -> Arc<Self> {
let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(unsafe { zeroed() }) });
unsafe { unsafe {
let mut result = UniqueArc::<Self>::new_uninit();
// FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
// these looks like Valgrind false-positives at a quick glance.
ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
Gecko_Construct_Default_${style_struct.gecko_ffi_name}( Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
&mut *Arc::get_mut(&mut result).unwrap().gecko, result.as_mut_ptr() as *mut _,
document, document,
); );
UniqueArc::assume_init(result).shareable()
} }
result
} }
} }
impl Drop for ${style_struct.gecko_struct_name} { impl Drop for ${style_struct.gecko_struct_name} {
@ -641,9 +644,12 @@ impl Drop for ${style_struct.gecko_struct_name} {
impl Clone for ${style_struct.gecko_struct_name} { impl Clone for ${style_struct.gecko_struct_name} {
fn clone(&self) -> Self { fn clone(&self) -> Self {
unsafe { unsafe {
let mut result = ${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(zeroed()) }; let mut result = MaybeUninit::<Self>::uninit();
Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut *result.gecko, &*self.gecko); // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
result // these looks like Valgrind false-positives at a quick glance.
ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &*self.gecko);
result.assume_init()
} }
} }
} }

View file

@ -51,6 +51,7 @@ ${helpers.single_keyword(
"mixed upright sideways", "mixed upright sideways",
engines="gecko", engines="gecko",
gecko_aliases="sideways-right=sideways", gecko_aliases="sideways-right=sideways",
gecko_enum_prefix="StyleTextOrientation",
animation_value_type="none", animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation", spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
)} )}

View file

@ -25,6 +25,7 @@ ${helpers.single_keyword(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty", spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty",
gecko_enum_prefix="StyleTextAnchor",
)} )}
// Section 11 - Painting: Filling, Stroking and Marker Symbols // Section 11 - Painting: Filling, Stroking and Marker Symbols
@ -80,6 +81,7 @@ ${helpers.single_keyword(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty", spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty",
gecko_enum_prefix = "StyleShapeRendering",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
@ -107,6 +109,7 @@ ${helpers.single_keyword(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty", spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty",
gecko_enum_prefix = "StyleStrokeLinecap",
)} )}
${helpers.single_keyword( ${helpers.single_keyword(

View file

@ -331,6 +331,7 @@ ${helpers.single_keyword(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-images/#propdef-object-fit", spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
gecko_enum_prefix = "StyleObjectFit",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -15,6 +15,7 @@ ${helpers.single_keyword(
"ime-mode", "ime-mode",
"auto normal active disabled inactive", "auto normal active disabled inactive",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleImeMode",
gecko_ffi_name="mIMEMode", gecko_ffi_name="mIMEMode",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ui/#input-method-editor", spec="https://drafts.csswg.org/css-ui/#input-method-editor",
@ -58,7 +59,7 @@ ${helpers.single_keyword(
"none default menu tooltip sheet", "none default menu tooltip sheet",
engines="gecko", engines="gecko",
gecko_ffi_name="mWindowShadow", gecko_ffi_name="mWindowShadow",
gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW", gecko_enum_prefix="StyleWindowShadow",
animation_value_type="discrete", animation_value_type="discrete",
enabled_in="chrome", enabled_in="chrome",
spec="None (Nonstandard internal property)", spec="None (Nonstandard internal property)",

View file

@ -64,16 +64,6 @@ ${helpers.single_keyword(
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
)} )}
${helpers.single_keyword(
"-moz-stack-sizing",
"stretch-to-fit ignore ignore-horizontal ignore-vertical",
engines="gecko",
gecko_ffi_name="mStackSizing",
gecko_enum_prefix="StyleStackSizing",
animation_value_type="discrete",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)",
)}
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-box-ordinal-group", "-moz-box-ordinal-group",
"Integer", "Integer",

View file

@ -1651,13 +1651,25 @@ impl UnparsedValue {
shorthands::${shorthand.ident}::parse_value(&context, input) shorthands::${shorthand.ident}::parse_value(&context, input)
.map(|longhands| { .map(|longhands| {
match longhand_id { match longhand_id {
<% seen = set() %>
% for property in shorthand.sub_properties: % for property in shorthand.sub_properties:
LonghandId::${property.camel_case} => { // When animating logical properties, we end up
PropertyDeclaration::${property.camel_case}( // physicalizing the value during the animation, but
// the value still comes from the logical shorthand.
//
// So we need to handle the physical properties too.
% for prop in [property] + property.all_physical_mapped_properties(data):
% if prop.camel_case not in seen:
LonghandId::${prop.camel_case} => {
PropertyDeclaration::${prop.camel_case}(
longhands.${property.ident} longhands.${property.ident}
) )
} }
<% seen.add(prop.camel_case) %>
% endif
% endfor % endfor
% endfor
<% del seen %>
_ => unreachable!() _ => unreachable!()
} }
}) })
@ -1801,8 +1813,8 @@ pub enum CountedUnknownProperty {
} }
impl CountedUnknownProperty { impl CountedUnknownProperty {
/// Parse the counted unknown property. /// Parse the counted unknown property, for testing purposes only.
pub fn parse_for_test(property_name: &str) -> Option<Self> { pub fn parse_for_testing(property_name: &str) -> Option<Self> {
ascii_case_insensitive_phf_map! { ascii_case_insensitive_phf_map! {
unknown_id -> CountedUnknownProperty = { unknown_id -> CountedUnknownProperty = {
% for property in data.counted_unknown_properties: % for property in data.counted_unknown_properties:
@ -1814,6 +1826,7 @@ impl CountedUnknownProperty {
} }
/// Returns the underlying index, used for use counter. /// Returns the underlying index, used for use counter.
#[inline]
pub fn bit(self) -> usize { pub fn bit(self) -> usize {
self as usize self as usize
} }
@ -1830,9 +1843,16 @@ impl PropertyId {
}) })
} }
/// Returns a given property from the string `s`. /// Returns a given property from the given name, _regardless of whether it
/// is enabled or not_, or Err(()) for unknown properties.
/// ///
/// Returns Err(()) for unknown properties. /// Do not use for non-testing purposes.
pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
Self::parse_unchecked(name, None)
}
/// Returns a given property from the given name, _regardless of whether it
/// is enabled or not_, or Err(()) for unknown properties.
fn parse_unchecked( fn parse_unchecked(
property_name: &str, property_name: &str,
use_counters: Option< &UseCounters>, use_counters: Option< &UseCounters>,
@ -2176,14 +2196,12 @@ impl PropertyDeclaration {
let mut ret = self.clone(); let mut ret = self.clone();
% for prop in data.longhands: % for prop in data.longhands:
% if prop.logical: % for physical_property in prop.all_physical_mapped_properties(data):
% for physical_property in prop.all_physical_mapped_properties(): % if physical_property.specified_type() != prop.specified_type():
% if data.longhands_by_name[physical_property].specified_type() != prop.specified_type():
<% raise "Logical property %s should share specified value with physical property %s" % \ <% raise "Logical property %s should share specified value with physical property %s" % \
(prop.name, physical_property) %> (prop.name, physical_property.name) %>
% endif % endif
% endfor % endfor
% endif
% endfor % endfor
unsafe { unsafe {

View file

@ -464,11 +464,13 @@ ${helpers.two_properties_shorthand(
}; };
// Make sure that the initial value matches the values for the // Make sure that the initial value matches the values for the
// longhands, just for general sanity. // longhands, just for general sanity. `zoom: 1` and `zoom: 0` are
// ignored, see [1][2]. They are just hack for the "has layout" mode on
// IE.
// //
// FIXME: Should we just do this for the "normal" case? Seems weird // [1]: https://bugs.webkit.org/show_bug.cgi?id=18467
// either way, so maybe not? // [2]: https://bugzilla.mozilla.org/show_bug.cgi?id=1593009
Ok(if zoom.get() == 1.0 { Ok(if zoom.get() == 1.0 || zoom.get() == 0.0 {
expanded! { expanded! {
transform: Transform::none(), transform: Transform::none(),
transform_origin: TransformOrigin::initial_value(), transform_origin: TransformOrigin::initial_value(),

View file

@ -181,8 +181,18 @@
} }
impl<'a> ToCss for LonghandsToSerialize<'a> { impl<'a> ToCss for LonghandsToSerialize<'a> {
// Return the shortest possible serialization of the `grid-${kind}-[start/end]` values.
// This function exploits the opportunities to omit the end value per this spec text:
//
// https://drafts.csswg.org/css-grid/#propdef-grid-column
// "When the second value is omitted, if the first value is a <custom-ident>,
// the grid-row-end/grid-column-end longhand is also set to that <custom-ident>;
// otherwise, it is set to auto."
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.grid_${kind}_start.to_css(dest)?; self.grid_${kind}_start.to_css(dest)?;
if self.grid_${kind}_start.can_omit(self.grid_${kind}_end) {
return Ok(()); // the end value is redundant
}
dest.write_str(" / ")?; dest.write_str(" / ")?;
self.grid_${kind}_end.to_css(dest) self.grid_${kind}_end.to_css(dest)
} }
@ -247,14 +257,39 @@
} }
impl<'a> ToCss for LonghandsToSerialize<'a> { impl<'a> ToCss for LonghandsToSerialize<'a> {
// Return the shortest possible serialization of the `grid-[column/row]-[start/end]` values.
// This function exploits the opportunities to omit trailing values per this spec text:
//
// https://drafts.csswg.org/css-grid/#propdef-grid-area
// "If four <grid-line> values are specified, grid-row-start is set to the first value,
// grid-column-start is set to the second value, grid-row-end is set to the third value,
// and grid-column-end is set to the fourth value.
//
// When grid-column-end is omitted, if grid-column-start is a <custom-ident>,
// grid-column-end is set to that <custom-ident>; otherwise, it is set to auto.
//
// When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is
// set to that <custom-ident>; otherwise, it is set to auto.
//
// When grid-column-start is omitted, if grid-row-start is a <custom-ident>, all four
// longhands are set to that value. Otherwise, it is set to auto."
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.grid_row_start.to_css(dest)?; self.grid_row_start.to_css(dest)?;
let mut trailing_values = 3;
if self.grid_column_start.can_omit(self.grid_column_end) {
trailing_values -= 1;
if self.grid_row_start.can_omit(self.grid_row_end) {
trailing_values -= 1;
if self.grid_row_start.can_omit(self.grid_column_start) {
trailing_values -= 1;
}
}
}
let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end]; let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
for value in &values { for value in values.iter().take(trailing_values) {
dest.write_str(" / ")?; dest.write_str(" / ")?;
value.to_css(dest)?; value.to_css(dest)?;
} }
Ok(()) Ok(())
} }
} }
@ -301,27 +336,37 @@
% endfor % endfor
let first_line_names = input.try(parse_line_names).unwrap_or_default(); let first_line_names = input.try(parse_line_names).unwrap_or_default();
if let Ok(mut string) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) { if let Ok(string) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
let mut strings = vec![]; let mut strings = vec![];
let mut values = vec![]; let mut values = vec![];
let mut line_names = vec![]; let mut line_names = vec![];
let mut names = first_line_names; line_names.push(first_line_names);
loop {
line_names.push(names);
strings.push(string); strings.push(string);
loop {
let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default(); let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default();
values.push(TrackListValue::TrackSize(size)); values.push(TrackListValue::TrackSize(size));
names = input.try(parse_line_names).unwrap_or_default(); let mut names = input.try(parse_line_names).unwrap_or_default();
if let Ok(v) = input.try(parse_line_names) { let more_names = input.try(parse_line_names);
match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
Ok(string) => {
strings.push(string);
if let Ok(v) = more_names {
// We got `[names] [more_names] "string"` - merge the two name lists.
let mut names_vec = names.into_vec(); let mut names_vec = names.into_vec();
names_vec.extend(v.into_iter()); names_vec.extend(v.into_iter());
names = names_vec.into(); names = names_vec.into();
} }
line_names.push(names);
string = match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) { },
Ok(s) => s, Err(e) => {
_ => { // only the named area determines whether we should bail out if more_names.is_ok() {
line_names.push(names.into()); // We've parsed `"string" [names] [more_names]` but then failed to parse another `"string"`.
// The grammar doesn't allow two trailing `<line-names>` so this is an invalid value.
return Err(e.into());
}
// only the named area determines whether we should bail out
line_names.push(names);
break break
}, },
}; };
@ -350,7 +395,7 @@
value value
} else { } else {
GenericGridTemplateComponent::None GridTemplateComponent::default()
}; };
Ok(( Ok((
@ -399,6 +444,9 @@
W: Write { W: Write {
match *template_areas { match *template_areas {
GridTemplateAreas::None => { GridTemplateAreas::None => {
if template_rows.is_initial() && template_columns.is_initial() {
return GridTemplateComponent::default().to_css(dest);
}
template_rows.to_css(dest)?; template_rows.to_css(dest)?;
dest.write_str(" / ")?; dest.write_str(" / ")?;
template_columns.to_css(dest) template_columns.to_css(dest)
@ -455,9 +503,13 @@
} }
string.to_css(dest)?; string.to_css(dest)?;
// If the track size is the initial value then it's redundant here.
if !value.is_initial() {
dest.write_str(" ")?; dest.write_str(" ")?;
value.to_css(dest)?; value.to_css(dest)?;
} }
}
if let Some(names) = names_iter.next() { if let Some(names) = names_iter.next() {
concat_serialize_idents(" [", "]", names, " ", dest)?; concat_serialize_idents(" [", "]", names, " ", dest)?;
@ -503,8 +555,8 @@
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> { ) -> Result<Longhands, ParseError<'i>> {
let mut temp_rows = GridTemplateComponent::None; let mut temp_rows = GridTemplateComponent::default();
let mut temp_cols = GridTemplateComponent::None; let mut temp_cols = GridTemplateComponent::default();
let mut temp_areas = GridTemplateAreas::None; let mut temp_areas = GridTemplateAreas::None;
let mut auto_rows = ImplicitGridTracks::default(); let mut auto_rows = ImplicitGridTracks::default();
let mut auto_cols = ImplicitGridTracks::default(); let mut auto_cols = ImplicitGridTracks::default();
@ -577,8 +629,8 @@
impl<'a> ToCss for LonghandsToSerialize<'a> { impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
if *self.grid_template_areas != GridTemplateAreas::None || if *self.grid_template_areas != GridTemplateAreas::None ||
(*self.grid_template_rows != GridTemplateComponent::None && (!self.grid_template_rows.is_initial() &&
*self.grid_template_columns != GridTemplateComponent::None) || !self.grid_template_columns.is_initial()) ||
self.is_grid_template() { self.is_grid_template() {
return super::grid_template::serialize_grid_template(self.grid_template_rows, return super::grid_template::serialize_grid_template(self.grid_template_rows,
self.grid_template_columns, self.grid_template_columns,
@ -588,7 +640,7 @@
if self.grid_auto_flow.autoflow == AutoFlow::Column { if self.grid_auto_flow.autoflow == AutoFlow::Column {
// It should fail to serialize if other branch of the if condition's values are set. // It should fail to serialize if other branch of the if condition's values are set.
if !self.grid_auto_rows.is_initial() || if !self.grid_auto_rows.is_initial() ||
*self.grid_template_columns != GridTemplateComponent::None { !self.grid_template_columns.is_initial() {
return Ok(()); return Ok(());
} }
@ -612,7 +664,7 @@
} else { } else {
// It should fail to serialize if other branch of the if condition's values are set. // It should fail to serialize if other branch of the if condition's values are set.
if !self.grid_auto_columns.is_initial() || if !self.grid_auto_columns.is_initial() ||
*self.grid_template_rows != GridTemplateComponent::None { !self.grid_template_rows.is_initial() {
return Ok(()); return Ok(());
} }

View file

@ -26,9 +26,7 @@ pub struct RuleCacheConditions {
impl RuleCacheConditions { impl RuleCacheConditions {
/// Sets the style as depending in the font-size value. /// Sets the style as depending in the font-size value.
pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) { pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) {
if let Some(f) = &self.font_size { debug_assert!(self.font_size.map_or(true, |f| f == font_size));
debug_assert_eq!(*f, font_size);
}
self.font_size = Some(font_size); self.font_size = Some(font_size);
} }

View file

@ -13,6 +13,7 @@ use crate::selector_parser::PseudoElement;
use crate::shared_lock::Locked; use crate::shared_lock::Locked;
use crate::stylesheets::Origin; use crate::stylesheets::Origin;
use crate::stylist::{AuthorStylesEnabled, Rule, RuleInclusion, Stylist}; use crate::stylist::{AuthorStylesEnabled, Rule, RuleInclusion, Stylist};
use crate::Atom;
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use servo_arc::ArcBorrow; use servo_arc::ArcBorrow;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -79,7 +80,6 @@ where
rules: &'a mut ApplicableDeclarationList, rules: &'a mut ApplicableDeclarationList,
context: &'a mut MatchingContext<'b, E::Impl>, context: &'a mut MatchingContext<'b, E::Impl>,
flags_setter: &'a mut F, flags_setter: &'a mut F,
shadow_cascade_order: ShadowCascadeOrder,
matches_user_and_author_rules: bool, matches_user_and_author_rules: bool,
matches_document_author_rules: bool, matches_document_author_rules: bool,
} }
@ -132,7 +132,6 @@ where
context, context,
flags_setter, flags_setter,
rules, rules,
shadow_cascade_order: 0,
matches_user_and_author_rules, matches_user_and_author_rules,
matches_document_author_rules: matches_user_and_author_rules, matches_document_author_rules: matches_user_and_author_rules,
} }
@ -142,7 +141,7 @@ where
let cascade_level = match origin { let cascade_level = match origin {
Origin::UserAgent => CascadeLevel::UANormal, Origin::UserAgent => CascadeLevel::UANormal,
Origin::User => CascadeLevel::UserNormal, Origin::User => CascadeLevel::UserNormal,
Origin::Author => CascadeLevel::SameTreeAuthorNormal, Origin::Author => CascadeLevel::same_tree_author_normal(),
}; };
let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin); let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);
@ -198,7 +197,6 @@ where
) { ) {
debug_assert!(shadow_host.shadow_root().is_some()); debug_assert!(shadow_host.shadow_root().is_some());
self.collect_rules_internal(Some(shadow_host), map, cascade_level); self.collect_rules_internal(Some(shadow_host), map, cascade_level);
self.shadow_cascade_order += 1;
} }
#[inline] #[inline]
@ -212,7 +210,6 @@ where
let rule_hash_target = self.rule_hash_target; let rule_hash_target = self.rule_hash_target;
let rules = &mut self.rules; let rules = &mut self.rules;
let flags_setter = &mut self.flags_setter; let flags_setter = &mut self.flags_setter;
let shadow_cascade_order = self.shadow_cascade_order;
let start = rules.len(); let start = rules.len();
self.context.with_shadow_host(shadow_host, |context| { self.context.with_shadow_host(shadow_host, |context| {
map.get_all_matching_rules( map.get_all_matching_rules(
@ -222,16 +219,18 @@ where
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
); );
}); });
sort_rules_from(rules, start); sort_rules_from(rules, start);
} }
/// Collects the rules for the ::slotted pseudo-element. /// Collects the rules for the ::slotted pseudo-element and the :host
fn collect_slotted_rules(&mut self) { /// pseudo-class.
fn collect_host_and_slotted_rules(&mut self) {
let mut slots = SmallVec::<[_; 3]>::new(); let mut slots = SmallVec::<[_; 3]>::new();
let mut current = self.rule_hash_target.assigned_slot(); let mut current = self.rule_hash_target.assigned_slot();
let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();
while let Some(slot) = current { while let Some(slot) = current {
debug_assert!( debug_assert!(
self.matches_user_and_author_rules, self.matches_user_and_author_rules,
@ -239,11 +238,16 @@ where
); );
slots.push(slot); slots.push(slot);
current = slot.assigned_slot(); current = slot.assigned_slot();
shadow_cascade_order.dec();
} }
self.collect_host_rules(shadow_cascade_order);
// Match slotted rules in reverse order, so that the outer slotted rules // Match slotted rules in reverse order, so that the outer slotted rules
// come before the inner rules (and thus have less priority). // come before the inner rules (and thus have less priority).
for slot in slots.iter().rev() { for slot in slots.iter().rev() {
shadow_cascade_order.inc();
let shadow = slot.containing_shadow().unwrap(); let shadow = slot.containing_shadow().unwrap();
let data = match shadow.style_data() { let data = match shadow.style_data() {
Some(d) => d, Some(d) => d,
@ -253,10 +257,13 @@ where
Some(r) => r, Some(r) => r,
None => continue, None => continue,
}; };
self.collect_rules_in_shadow_tree( self.collect_rules_in_shadow_tree(
shadow.host(), shadow.host(),
slotted_rules, slotted_rules,
CascadeLevel::InnerShadowNormal, CascadeLevel::AuthorNormal {
shadow_cascade_order,
},
); );
} }
} }
@ -277,12 +284,12 @@ where
let cascade_data = containing_shadow.style_data(); let cascade_data = containing_shadow.style_data();
let host = containing_shadow.host(); let host = containing_shadow.host();
if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) { if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) {
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::SameTreeAuthorNormal); self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal());
} }
} }
/// Collects the rules for the :host pseudo-class. /// Collects the rules for the :host pseudo-class.
fn collect_host_rules(&mut self) { fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {
let shadow = match self.rule_hash_target.shadow_root() { let shadow = match self.rule_hash_target.shadow_root() {
Some(s) => s, Some(s) => s,
None => return, None => return,
@ -307,7 +314,9 @@ where
self.collect_rules_in_shadow_tree( self.collect_rules_in_shadow_tree(
rule_hash_target, rule_hash_target,
host_rules, host_rules,
CascadeLevel::InnerShadowNormal, CascadeLevel::AuthorNormal {
shadow_cascade_order,
},
); );
} }
@ -324,14 +333,23 @@ where
return; return;
} }
let shadow = match self.rule_hash_target.containing_shadow() { let mut inner_shadow = match self.rule_hash_target.containing_shadow() {
Some(s) => s, Some(s) => s,
None => return, None => return,
}; };
let host = shadow.host(); let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();
let containing_shadow = host.containing_shadow();
let part_rules = match containing_shadow { let mut parts = SmallVec::<[Atom; 3]>::new();
self.rule_hash_target.each_part(|p| parts.push(p.clone()));
loop {
if parts.is_empty() {
return;
}
let outer_shadow = inner_shadow.host().containing_shadow();
let part_rules = match outer_shadow {
Some(shadow) => shadow Some(shadow) => shadow
.style_data() .style_data()
.and_then(|data| data.part_rules(self.pseudo_element)), .and_then(|data| data.part_rules(self.pseudo_element)),
@ -342,24 +360,17 @@ where
.part_rules(self.pseudo_element), .part_rules(self.pseudo_element),
}; };
// TODO(emilio): SameTreeAuthorNormal is a bit of a lie here, we may
// need an OuterTreeAuthorNormal cascade level or such, and change the
// cascade order, if we allow to forward parts to even outer trees.
//
// Though the current thing kinda works because we apply them after
// the outer tree, so as long as we don't allow forwarding we're
// good.
if let Some(part_rules) = part_rules { if let Some(part_rules) = part_rules {
let containing_host = containing_shadow.map(|s| s.host()); let containing_host = outer_shadow.map(|s| s.host());
let element = self.element; let element = self.element;
let rule_hash_target = self.rule_hash_target;
let rules = &mut self.rules; let rules = &mut self.rules;
let flags_setter = &mut self.flags_setter; let flags_setter = &mut self.flags_setter;
let shadow_cascade_order = self.shadow_cascade_order; let cascade_level = CascadeLevel::AuthorNormal {
let cascade_level = CascadeLevel::SameTreeAuthorNormal; shadow_cascade_order,
};
let start = rules.len(); let start = rules.len();
self.context.with_shadow_host(containing_host, |context| { self.context.with_shadow_host(containing_host, |context| {
rule_hash_target.each_part(|p| { for p in &parts {
if let Some(part_rules) = part_rules.get(p) { if let Some(part_rules) = part_rules.get(p) {
SelectorMap::get_matching_rules( SelectorMap::get_matching_rules(
element, element,
@ -368,24 +379,43 @@ where
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
); );
} }
}); }
}); });
sort_rules_from(rules, start); sort_rules_from(rules, start);
shadow_cascade_order.inc();
}
let inner_shadow_host = inner_shadow.host();
inner_shadow = match outer_shadow {
Some(s) => s,
None => break, // Nowhere to export to.
};
parts.retain(|part| {
let exported_part = match inner_shadow_host.exported_part(part) {
Some(part) => part,
None => return false,
};
std::mem::replace(part, exported_part);
true
});
} }
} }
fn collect_style_attribute_and_animation_rules(&mut self) { fn collect_style_attribute(&mut self) {
if let Some(sa) = self.style_attribute { if let Some(sa) = self.style_attribute {
self.rules self.rules
.push(ApplicableDeclarationBlock::from_declarations( .push(ApplicableDeclarationBlock::from_declarations(
sa.clone_arc(), sa.clone_arc(),
CascadeLevel::StyleAttributeNormal, CascadeLevel::same_tree_author_normal(),
)); ));
} }
}
fn collect_animation_rules(&mut self) {
if let Some(so) = self.smil_override { if let Some(so) = self.smil_override {
self.rules self.rules
.push(ApplicableDeclarationBlock::from_declarations( .push(ApplicableDeclarationBlock::from_declarations(
@ -431,11 +461,11 @@ where
if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No { if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {
return; return;
} }
self.collect_host_rules(); self.collect_host_and_slotted_rules();
self.collect_slotted_rules();
self.collect_normal_rules_from_containing_shadow_tree(); self.collect_normal_rules_from_containing_shadow_tree();
self.collect_document_author_rules(); self.collect_document_author_rules();
self.collect_style_attribute();
self.collect_part_rules(); self.collect_part_rules();
self.collect_style_attribute_and_animation_rules(); self.collect_animation_rules();
} }
} }

View file

@ -169,15 +169,60 @@ const FREE_LIST_SENTINEL: *mut RuleNode = 0x01 as *mut RuleNode;
/// another thread is currently adding an entry). We spin if we find this value. /// another thread is currently adding an entry). We spin if we find this value.
const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode; const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode;
/// A counter to track how many inner shadow roots rules deep we are. /// A counter to track how many shadow root rules deep we are. This is used to
/// /// handle:
/// This is used to handle:
/// ///
/// https://drafts.csswg.org/css-scoping/#shadow-cascading /// https://drafts.csswg.org/css-scoping/#shadow-cascading
/// ///
/// In particular, it'd be `0` for the innermost shadow host, `1` for the next, /// See the static functions for the meaning of different values.
/// and so on. #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
pub type ShadowCascadeOrder = u8; pub struct ShadowCascadeOrder(i8);
impl ShadowCascadeOrder {
/// A level for the outermost shadow tree (the shadow tree we own, and the
/// ones from the slots we're slotted in).
#[inline]
pub fn for_outermost_shadow_tree() -> Self {
Self(-1)
}
/// A level for the element's tree.
#[inline]
fn for_same_tree() -> Self {
Self(0)
}
/// A level for the innermost containing tree (the one closest to the
/// element).
#[inline]
pub fn for_innermost_containing_tree() -> Self {
Self(1)
}
/// Decrement the level, moving inwards. We should only move inwards if
/// we're traversing slots.
#[inline]
pub fn dec(&mut self) {
debug_assert!(self.0 < 0);
self.0 = self.0.saturating_sub(1);
}
/// The level, moving inwards. We should only move inwards if we're
/// traversing slots.
#[inline]
pub fn inc(&mut self) {
debug_assert_ne!(self.0, -1);
self.0 = self.0.saturating_add(1);
}
}
impl std::ops::Neg for ShadowCascadeOrder {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(self.0.neg())
}
}
impl RuleTree { impl RuleTree {
/// Construct a new rule tree. /// Construct a new rule tree.
@ -215,26 +260,20 @@ impl RuleTree {
guards: &StylesheetGuards, guards: &StylesheetGuards,
) -> StrongRuleNode ) -> StrongRuleNode
where where
I: Iterator<Item = (StyleSource, CascadeLevel, ShadowCascadeOrder)>, I: Iterator<Item = (StyleSource, CascadeLevel)>,
{ {
use self::CascadeLevel::*; use self::CascadeLevel::*;
let mut current = self.root.clone(); let mut current = self.root.clone();
let mut last_level = current.get().level;
let mut found_important = false; let mut found_important = false;
let mut important_style_attr = None;
let mut important_same_tree = SmallVec::<[StyleSource; 4]>::new(); let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new();
let mut important_inner_shadow = SmallVec::<[SmallVec<[StyleSource; 4]>; 4]>::new();
important_inner_shadow.push(SmallVec::new());
let mut important_user = SmallVec::<[StyleSource; 4]>::new(); let mut important_user = SmallVec::<[StyleSource; 4]>::new();
let mut important_ua = SmallVec::<[StyleSource; 4]>::new(); let mut important_ua = SmallVec::<[StyleSource; 4]>::new();
let mut transition = None; let mut transition = None;
let mut last_cascade_order = 0; for (source, level) in iter {
for (source, level, shadow_cascade_order) in iter {
debug_assert!(level >= last_level, "Not really ordered");
debug_assert!(!level.is_important(), "Important levels handled internally"); debug_assert!(!level.is_important(), "Important levels handled internally");
let any_important = { let any_important = {
let pdb = source.read(level.guard(guards)); let pdb = source.read(level.guard(guards));
@ -244,29 +283,13 @@ impl RuleTree {
if any_important { if any_important {
found_important = true; found_important = true;
match level { match level {
InnerShadowNormal => { AuthorNormal {
debug_assert!( shadow_cascade_order,
shadow_cascade_order >= last_cascade_order, } => {
"Not really ordered" important_author.push((source.clone(), shadow_cascade_order));
);
if shadow_cascade_order > last_cascade_order &&
!important_inner_shadow.last().unwrap().is_empty()
{
last_cascade_order = shadow_cascade_order;
important_inner_shadow.push(SmallVec::new());
}
important_inner_shadow
.last_mut()
.unwrap()
.push(source.clone())
}, },
SameTreeAuthorNormal => important_same_tree.push(source.clone()),
UANormal => important_ua.push(source.clone()), UANormal => important_ua.push(source.clone()),
UserNormal => important_user.push(source.clone()), UserNormal => important_user.push(source.clone()),
StyleAttributeNormal => {
debug_assert!(important_style_attr.is_none());
important_style_attr = Some(source.clone());
},
_ => {}, _ => {},
}; };
} }
@ -290,7 +313,6 @@ impl RuleTree {
} else { } else {
current = current.ensure_child(self.root.downgrade(), source, level); current = current.ensure_child(self.root.downgrade(), source, level);
} }
last_level = level;
} }
// Early-return in the common case of no !important declarations. // Early-return in the common case of no !important declarations.
@ -298,23 +320,39 @@ impl RuleTree {
return current; return current;
} }
//
// Insert important declarations, in order of increasing importance, // Insert important declarations, in order of increasing importance,
// followed by any transition rule. // followed by any transition rule.
// //
// Inner shadow wins over same-tree, which wins over outer-shadow.
for source in important_same_tree.drain() { //
current = current.ensure_child(self.root.downgrade(), source, SameTreeAuthorImportant); // We negate the shadow cascade order to preserve the right PartialOrd
// behavior.
if !important_author.is_empty() &&
important_author.first().unwrap().1 != important_author.last().unwrap().1
{
// We only need to sort if the important rules come from
// different trees, but we need this sort to be stable.
//
// FIXME(emilio): This could maybe be smarter, probably by chunking
// the important rules while inserting, and iterating the outer
// chunks in reverse order.
//
// That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,
// we're really only sorting the chunks, while keeping elements
// inside the same chunk already sorted. Seems like we could try to
// keep a SmallVec-of-SmallVecs with the chunks and just iterate the
// outer in reverse.
important_author.sort_by_key(|&(_, order)| -order);
} }
if let Some(source) = important_style_attr { for (source, shadow_cascade_order) in important_author.drain() {
current = current.ensure_child(self.root.downgrade(), source, StyleAttributeImportant); current = current.ensure_child(
} self.root.downgrade(),
source,
for mut list in important_inner_shadow.drain().rev() { AuthorImportant {
for source in list.drain() { shadow_cascade_order: -shadow_cascade_order,
current = current.ensure_child(self.root.downgrade(), source, InnerShadowImportant); },
} );
} }
for source in important_user.drain() { for source in important_user.drain() {
@ -359,11 +397,8 @@ impl RuleTree {
I: Iterator<Item = (StyleSource, CascadeLevel)>, I: Iterator<Item = (StyleSource, CascadeLevel)>,
{ {
let mut current = from; let mut current = from;
let mut last_level = current.get().level;
for (source, level) in iter { for (source, level) in iter {
debug_assert!(last_level <= level, "Not really ordered");
current = current.ensure_child(self.root.downgrade(), source, level); current = current.ensure_child(self.root.downgrade(), source, level);
last_level = level;
} }
current current
} }
@ -439,7 +474,6 @@ impl RuleTree {
guards: &StylesheetGuards, guards: &StylesheetGuards,
important_rules_changed: &mut bool, important_rules_changed: &mut bool,
) -> Option<StrongRuleNode> { ) -> Option<StrongRuleNode> {
debug_assert!(level.is_unique_per_element());
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
// the refcount churn. // the refcount churn.
let mut current = path.clone(); let mut current = path.clone();
@ -468,7 +502,11 @@ impl RuleTree {
if current.get().level == level { if current.get().level == level {
*important_rules_changed |= level.is_important(); *important_rules_changed |= level.is_important();
if let Some(pdb) = pdb { let current_decls = current.get().source.as_ref().unwrap().as_declarations();
// If the only rule at the level we're replacing is exactly the
// same as `pdb`, we're done, and `path` is still valid.
if let (Some(ref pdb), Some(ref current_decls)) = (pdb, current_decls) {
// If the only rule at the level we're replacing is exactly the // If the only rule at the level we're replacing is exactly the
// same as `pdb`, we're done, and `path` is still valid. // same as `pdb`, we're done, and `path` is still valid.
// //
@ -478,25 +516,17 @@ impl RuleTree {
// also equally valid. This is less likely, and would require an // also equally valid. This is less likely, and would require an
// in-place mutation of the source, which is, at best, fiddly, // in-place mutation of the source, which is, at best, fiddly,
// so let's skip it for now. // so let's skip it for now.
let current_decls = current let is_here_already = ArcBorrow::ptr_eq(pdb, current_decls);
.get()
.source
.as_ref()
.unwrap()
.as_declarations()
.expect("Replacing non-declarations style?");
let is_here_already = ArcBorrow::ptr_eq(&pdb, &current_decls);
if is_here_already { if is_here_already {
debug!("Picking the fast path in rule replacement"); debug!("Picking the fast path in rule replacement");
return None; return None;
} }
} }
if current_decls.is_some() {
current = current.parent().unwrap().clone(); current = current.parent().unwrap().clone();
} }
debug_assert!( }
current.get().level != level,
"Multiple rules should've been replaced?"
);
// Insert the rule if it's relevant at this level in the cascade. // Insert the rule if it's relevant at this level in the cascade.
// //
@ -611,61 +641,127 @@ const RULE_TREE_GC_INTERVAL: usize = 300;
/// tree may affect an element connected to the document or an "outer" shadow /// tree may affect an element connected to the document or an "outer" shadow
/// tree. /// tree.
/// ///
/// We need to differentiate between rules from the same tree and "inner" shadow
/// trees in order to be able to find the right position for the style attribute
/// easily. Otherwise we wouldn't be able to avoid selector-matching when a
/// style attribute is added or removed.
///
/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin /// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
/// [2]: https://drafts.csswg.org/css-cascade/#preshint /// [2]: https://drafts.csswg.org/css-cascade/#preshint
/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints /// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading /// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
#[repr(u8)] #[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub enum CascadeLevel { pub enum CascadeLevel {
/// Normal User-Agent rules. /// Normal User-Agent rules.
UANormal = 0, UANormal,
/// User normal rules. /// User normal rules.
UserNormal, UserNormal,
/// Presentational hints. /// Presentational hints.
PresHints, PresHints,
/// Shadow DOM styles from "inner" shadow trees. /// Shadow DOM styles from author styles.
AuthorNormal {
/// The order in the shadow tree hierarchy. This number is relative to
/// the tree of the element, and thus the only invariants that need to
/// be preserved is:
/// ///
/// See above for why this is needed instead of merging InnerShadowNormal, /// * Zero is the same tree as the element that matched the rule. This
/// SameTreeAuthorNormal and StyleAttributeNormal inside something like /// is important so that we can optimize style attribute insertions.
/// AuthorNormal. ///
InnerShadowNormal, /// * The levels are ordered in accordance with
/// Author normal rules from the same tree the element is in. /// https://drafts.csswg.org/css-scoping/#shadow-cascading
SameTreeAuthorNormal, shadow_cascade_order: ShadowCascadeOrder,
/// Style attribute normal rules. },
StyleAttributeNormal,
/// SVG SMIL animations. /// SVG SMIL animations.
SMILOverride, SMILOverride,
/// CSS animations and script-generated animations. /// CSS animations and script-generated animations.
Animations, Animations,
/// Author-supplied important rules from the same tree the element came /// Author-supplied important rules.
/// from. AuthorImportant {
SameTreeAuthorImportant, /// The order in the shadow tree hierarchy, inverted, so that PartialOrd
/// Style attribute important rules. /// does the right thing.
StyleAttributeImportant, shadow_cascade_order: ShadowCascadeOrder,
/// Shadow DOM important rules. },
InnerShadowImportant,
/// User important rules. /// User important rules.
UserImportant, UserImportant,
/// User-agent important rules. /// User-agent important rules.
UAImportant, UAImportant,
/// Transitions /// Transitions
///
/// NB: If this changes from being last, change from_byte below.
Transitions, Transitions,
} }
impl CascadeLevel { impl CascadeLevel {
/// Converts a raw byte to a CascadeLevel. /// Pack this cascade level in a single byte.
pub unsafe fn from_byte(byte: u8) -> Self { ///
debug_assert!(byte <= CascadeLevel::Transitions as u8); /// We have 10 levels, which we can represent with 4 bits, and then a
mem::transmute(byte) /// cascade order optionally, which we can clamp to three bits max, and
/// represent with a fourth bit for the sign.
///
/// So this creates: SOOODDDD
///
/// Where `S` is the sign of the order (one if negative, 0 otherwise), `O`
/// is the absolute value of the order, and `D`s are the discriminant.
#[inline]
pub fn to_byte_lossy(&self) -> u8 {
let (discriminant, order) = match *self {
Self::UANormal => (0, 0),
Self::UserNormal => (1, 0),
Self::PresHints => (2, 0),
Self::AuthorNormal {
shadow_cascade_order,
} => (3, shadow_cascade_order.0),
Self::SMILOverride => (4, 0),
Self::Animations => (5, 0),
Self::AuthorImportant {
shadow_cascade_order,
} => (6, shadow_cascade_order.0),
Self::UserImportant => (7, 0),
Self::UAImportant => (8, 0),
Self::Transitions => (9, 0),
};
debug_assert_eq!(discriminant & 0xf, discriminant);
if order == 0 {
return discriminant;
}
let negative = order < 0;
let value = std::cmp::min(order.abs() as u8, 0b111);
(negative as u8) << 7 | value << 4 | discriminant
}
/// Convert back from the single-byte representation of the cascade level
/// explained above.
#[inline]
pub fn from_byte(b: u8) -> Self {
let order = {
let abs = ((b & 0b01110000) >> 4) as i8;
let negative = b & 0b10000000 != 0;
if negative {
-abs
} else {
abs
}
};
let discriminant = b & 0xf;
let level = match discriminant {
0 => Self::UANormal,
1 => Self::UserNormal,
2 => Self::PresHints,
3 => {
return Self::AuthorNormal {
shadow_cascade_order: ShadowCascadeOrder(order),
}
},
4 => Self::SMILOverride,
5 => Self::Animations,
6 => {
return Self::AuthorImportant {
shadow_cascade_order: ShadowCascadeOrder(order),
}
},
7 => Self::UserImportant,
8 => Self::UAImportant,
9 => Self::Transitions,
_ => unreachable!("Didn't expect {} as a discriminant", discriminant),
};
debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level);
level
} }
/// Select a lock guard for this level /// Select a lock guard for this level
@ -679,16 +775,21 @@ impl CascadeLevel {
} }
} }
/// Returns whether this cascade level is unique per element, in which case /// Returns the cascade level for author important declarations from the
/// we can replace the path in the cascade without fear. /// same tree as the element.
pub fn is_unique_per_element(&self) -> bool { #[inline]
match *self { pub fn same_tree_author_important() -> Self {
CascadeLevel::Transitions | CascadeLevel::AuthorImportant {
CascadeLevel::Animations | shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
CascadeLevel::SMILOverride | }
CascadeLevel::StyleAttributeNormal | }
CascadeLevel::StyleAttributeImportant => true,
_ => false, /// Returns the cascade level for author normal declarations from the same
/// tree as the element.
#[inline]
pub fn same_tree_author_normal() -> Self {
CascadeLevel::AuthorNormal {
shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
} }
} }
@ -697,9 +798,7 @@ impl CascadeLevel {
#[inline] #[inline]
pub fn is_important(&self) -> bool { pub fn is_important(&self) -> bool {
match *self { match *self {
CascadeLevel::SameTreeAuthorImportant | CascadeLevel::AuthorImportant { .. } |
CascadeLevel::InnerShadowImportant |
CascadeLevel::StyleAttributeImportant |
CascadeLevel::UserImportant | CascadeLevel::UserImportant |
CascadeLevel::UAImportant => true, CascadeLevel::UAImportant => true,
_ => false, _ => false,
@ -724,14 +823,10 @@ impl CascadeLevel {
CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent, CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent,
CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User, CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User,
CascadeLevel::PresHints | CascadeLevel::PresHints |
CascadeLevel::InnerShadowNormal | CascadeLevel::AuthorNormal { .. } |
CascadeLevel::SameTreeAuthorNormal | CascadeLevel::AuthorImportant { .. } |
CascadeLevel::StyleAttributeNormal |
CascadeLevel::SMILOverride | CascadeLevel::SMILOverride |
CascadeLevel::Animations | CascadeLevel::Animations |
CascadeLevel::SameTreeAuthorImportant |
CascadeLevel::StyleAttributeImportant |
CascadeLevel::InnerShadowImportant |
CascadeLevel::Transitions => Origin::Author, CascadeLevel::Transitions => Origin::Author,
} }
} }
@ -1146,6 +1241,15 @@ impl StrongRuleNode {
) -> StrongRuleNode { ) -> StrongRuleNode {
use parking_lot::RwLockUpgradableReadGuard; use parking_lot::RwLockUpgradableReadGuard;
debug_assert!(
self.get().level <= level,
"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
self.get().level,
level,
self.get().source,
source,
);
let key = ChildKey(level, source.key()); let key = ChildKey(level, source.key());
let read_guard = self.get().children.upgradable_read(); let read_guard = self.get().children.upgradable_read();
@ -1448,14 +1552,24 @@ impl StrongRuleNode {
} }
}); });
match node.cascade_level() { let is_author = node.cascade_level().origin() == Origin::Author;
// Non-author rules:
CascadeLevel::UANormal |
CascadeLevel::UAImportant |
CascadeLevel::UserNormal |
CascadeLevel::UserImportant => {
for (id, declaration) in longhands { for (id, declaration) in longhands {
if properties.contains(id) { if !properties.contains(id) {
continue;
}
if is_author {
if !author_colors_allowed {
// FIXME(emilio): this looks wrong, this should
// do: if color is not transparent, then return
// true, or something.
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
return *color == Color::transparent();
}
}
return true;
}
// This property was set by a non-author rule. // This property was set by a non-author rule.
// Stop looking for it in this element's rule // Stop looking for it in this element's rule
// nodes. // nodes.
@ -1464,41 +1578,12 @@ impl StrongRuleNode {
// However, if it is inherited, then it might be // However, if it is inherited, then it might be
// inherited from an author rule from an // inherited from an author rule from an
// ancestor element's rule nodes. // ancestor element's rule nodes.
if declaration.get_css_wide_keyword() == if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) {
Some(CSSWideKeyword::Inherit)
{
have_explicit_ua_inherit = true; have_explicit_ua_inherit = true;
inherited_properties.insert(id); inherited_properties.insert(id);
} }
} }
} }
},
// Author rules:
CascadeLevel::PresHints |
CascadeLevel::SameTreeAuthorNormal |
CascadeLevel::InnerShadowNormal |
CascadeLevel::StyleAttributeNormal |
CascadeLevel::SMILOverride |
CascadeLevel::Animations |
CascadeLevel::SameTreeAuthorImportant |
CascadeLevel::InnerShadowImportant |
CascadeLevel::StyleAttributeImportant |
CascadeLevel::Transitions => {
for (id, declaration) in longhands {
if properties.contains(id) {
if !author_colors_allowed {
if let PropertyDeclaration::BackgroundColor(ref color) =
*declaration
{
return *color == Color::transparent();
}
}
return true;
}
}
},
}
}
if !have_explicit_ua_inherit { if !have_explicit_ua_inherit {
break; break;

View file

@ -10,7 +10,7 @@ use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
use crate::hash::map as hash_map; use crate::hash::map as hash_map;
use crate::hash::{HashMap, HashSet}; use crate::hash::{HashMap, HashSet};
use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder}; use crate::rule_tree::CascadeLevel;
use crate::selector_parser::SelectorImpl; use crate::selector_parser::SelectorImpl;
use crate::stylist::Rule; use crate::stylist::Rule;
use crate::{Atom, LocalName, Namespace, WeakAtom}; use crate::{Atom, LocalName, Namespace, WeakAtom};
@ -171,7 +171,6 @@ impl SelectorMap<Rule> {
context: &mut MatchingContext<E::Impl>, context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F, flags_setter: &mut F,
cascade_level: CascadeLevel, cascade_level: CascadeLevel,
shadow_cascade_order: ShadowCascadeOrder,
) where ) where
E: TElement, E: TElement,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
@ -190,7 +189,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
); );
} }
@ -203,7 +201,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
) )
} }
} }
@ -217,7 +214,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
) )
} }
}); });
@ -230,7 +226,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
) )
} }
@ -242,7 +237,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
) )
} }
@ -253,7 +247,6 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
cascade_level, cascade_level,
shadow_cascade_order,
); );
} }
@ -265,7 +258,6 @@ impl SelectorMap<Rule> {
context: &mut MatchingContext<E::Impl>, context: &mut MatchingContext<E::Impl>,
flags_setter: &mut F, flags_setter: &mut F,
cascade_level: CascadeLevel, cascade_level: CascadeLevel,
shadow_cascade_order: ShadowCascadeOrder,
) where ) where
E: TElement, E: TElement,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
@ -279,9 +271,7 @@ impl SelectorMap<Rule> {
context, context,
flags_setter, flags_setter,
) { ) {
matching_rules.push( matching_rules.push(rule.to_applicable_declaration_block(cascade_level));
rule.to_applicable_declaration_block(cascade_level, shadow_cascade_order),
);
} }
} }
} }

View file

@ -173,6 +173,12 @@ impl PseudoElement {
false false
} }
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
#[inline]
pub fn is_color_swatch(&self) -> bool {
false
}
/// Whether this pseudo-element is eagerly-cascaded. /// Whether this pseudo-element is eagerly-cascaded.
#[inline] #[inline]
pub fn is_eager(&self) -> bool { pub fn is_eager(&self) -> bool {
@ -694,6 +700,14 @@ impl ElementSnapshot for ServoElementSnapshot {
false false
} }
fn exported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
None
}
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
self.get_attr(&ns!(), &local_name!("class")) self.get_attr(&ns!(), &local_name!("class"))
.map_or(false, |v| { .map_or(false, |v| {

View file

@ -63,9 +63,7 @@ where
{ {
use crate::Atom; use crate::Atom;
// FIXME(emilio): This should be an actual static. const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
lazy_static! {
static ref SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
atom!("br"), atom!("br"),
atom!("wbr"), atom!("wbr"),
atom!("meter"), atom!("meter"),
@ -83,15 +81,13 @@ where
atom!("textarea"), atom!("textarea"),
atom!("select"), atom!("select"),
]; ];
}
// https://drafts.csswg.org/css-display/#unbox-svg // https://drafts.csswg.org/css-display/#unbox-svg
// //
// There's a note about "Unknown elements", but there's not a good way to // There's a note about "Unknown elements", but there's not a good way to
// know what that means, or to get that information from here, and no other // know what that means, or to get that information from here, and no other
// UA implements this either. // UA implements this either.
lazy_static! { const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
static ref SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
atom!("svg"), atom!("svg"),
atom!("a"), atom!("a"),
atom!("g"), atom!("g"),
@ -99,7 +95,6 @@ where
atom!("tspan"), atom!("tspan"),
atom!("textPath"), atom!("textPath"),
]; ];
}
// https://drafts.csswg.org/css-display/#unbox-html // https://drafts.csswg.org/css-display/#unbox-html
if element.is_html_element() { if element.is_html_element() {

View file

@ -124,6 +124,7 @@ impl StylesheetContents {
url_data: UrlExtraData, url_data: UrlExtraData,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
) -> Self { ) -> Self {
debug_assert!(rules.is_static());
Self { Self {
rules, rules,
origin, origin,
@ -144,6 +145,9 @@ impl StylesheetContents {
/// Measure heap usage. /// Measure heap usage.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
if self.rules.is_static() {
return 0;
}
// Measurement of other fields may be added later. // Measurement of other fields may be added later.
self.rules.unconditional_shallow_size_of(ops) + self.rules.unconditional_shallow_size_of(ops) +
self.rules.read_with(guard).size_of(guard, ops) self.rules.read_with(guard).size_of(guard, ops)

View file

@ -178,8 +178,7 @@ impl SupportsCondition {
while input.try(Parser::expect_whitespace).is_ok() {} while input.try(Parser::expect_whitespace).is_ok() {}
let pos = input.position(); let pos = input.position();
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove clone() when lifetimes are non-lexical match *input.next()? {
match input.next()?.clone() {
Token::ParenthesisBlock => { Token::ParenthesisBlock => {
let nested = let nested =
input.try(|input| input.parse_nested_block(parse_condition_or_declaration)); input.try(|input| input.parse_nested_block(parse_condition_or_declaration));
@ -187,7 +186,8 @@ impl SupportsCondition {
return nested; return nested;
} }
}, },
Token::Function(ident) => { Token::Function(ref ident) => {
let ident = ident.clone();
let nested = input.try(|input| { let nested = input.try(|input| {
input.parse_nested_block(|input| { input.parse_nested_block(|input| {
SupportsCondition::parse_functional(&ident, input) SupportsCondition::parse_functional(&ident, input)
@ -197,7 +197,7 @@ impl SupportsCondition {
return nested; return nested;
} }
}, },
t => return Err(location.new_unexpected_token_error(t)), ref t => return Err(location.new_unexpected_token_error(t.clone())),
} }
input.parse_nested_block(consume_any_value)?; input.parse_nested_block(consume_any_value)?;
Ok(SupportsCondition::FutureSyntax( Ok(SupportsCondition::FutureSyntax(

View file

@ -18,7 +18,7 @@ use crate::properties::{self, CascadeMode, ComputedValues};
use crate::properties::{AnimationRules, PropertyDeclarationBlock}; use crate::properties::{AnimationRules, PropertyDeclarationBlock};
use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_collector::{containing_shadow_ignoring_svg_use, RuleCollector}; use crate::rule_collector::{containing_shadow_ignoring_svg_use, RuleCollector};
use crate::rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource}; use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry}; use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap}; use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
@ -1322,16 +1322,17 @@ impl Stylist {
let iter_declarations = || { let iter_declarations = || {
block block
.declaration_importance_iter() .declaration_importance_iter()
.map(|(declaration, importance)| { .map(|(declaration, _)| (declaration, Origin::Author))
debug_assert!(!importance.important());
(declaration, CascadeLevel::StyleAttributeNormal)
})
}; };
let metrics = get_metrics_provider_for_product(); let metrics = get_metrics_provider_for_product();
// We don't bother inserting these declarations in the rule tree, since // We don't bother inserting these declarations in the rule tree, since
// it'd be quite useless and slow. // it'd be quite useless and slow.
//
// TODO(emilio): Now that we fixed bug 1493420, we should consider
// reversing this as it shouldn't be slow anymore, and should avoid
// generating two instantiations of apply_declarations.
properties::apply_declarations::<E, _, _>( properties::apply_declarations::<E, _, _>(
&self.device, &self.device,
/* pseudo = */ None, /* pseudo = */ None,
@ -1987,7 +1988,6 @@ impl CascadeData {
self.rules_source_order, self.rules_source_order,
CascadeLevel::UANormal, CascadeLevel::UANormal,
selector.specificity(), selector.specificity(),
0,
)); ));
continue; continue;
} }
@ -2323,16 +2323,9 @@ impl Rule {
pub fn to_applicable_declaration_block( pub fn to_applicable_declaration_block(
&self, &self,
level: CascadeLevel, level: CascadeLevel,
shadow_cascade_order: ShadowCascadeOrder,
) -> ApplicableDeclarationBlock { ) -> ApplicableDeclarationBlock {
let source = StyleSource::from_rule(self.style_rule.clone()); let source = StyleSource::from_rule(self.style_rule.clone());
ApplicableDeclarationBlock::new( ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity())
source,
self.source_order,
level,
self.specificity(),
shadow_cascade_order,
)
} }
/// Creates a new Rule. /// Creates a new Rule.

View file

@ -1249,7 +1249,11 @@ impl Animate for ComputedRotate {
match (self, other) { match (self, other) {
(&Rotate::None, &Rotate::None) => Ok(Rotate::None), (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
(&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => { (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
// No need to normalize `none`, so animate angle directly. // We always normalize direction vector for rotate3d() first, so we should also
// apply the same rule for rotate property. In other words, we promote none into
// a 3d rotate, and normalize both direction vector first, and then do
// interpolation.
let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
Ok(Rotate::Rotate3D( Ok(Rotate::Rotate3D(
fx, fx,
fy, fy,
@ -1258,7 +1262,8 @@ impl Animate for ComputedRotate {
)) ))
}, },
(&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => { (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
// No need to normalize `none`, so animate angle directly. // Normalize direction vector first.
let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
Ok(Rotate::Rotate3D( Ok(Rotate::Rotate3D(
tx, tx,
ty, ty,
@ -1368,8 +1373,7 @@ impl ComputedTranslate {
LengthPercentage::zero(), LengthPercentage::zero(),
Length::zero(), Length::zero(),
), ),
Translate::Translate3D(tx, ty, tz) => (tx, ty, tz), Translate::Translate(tx, ty, tz) => (tx, ty, tz),
Translate::Translate(tx, ty) => (tx, ty, Length::zero()),
} }
} }
} }
@ -1379,19 +1383,12 @@ impl Animate for ComputedTranslate {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) { match (self, other) {
(&Translate::None, &Translate::None) => Ok(Translate::None), (&Translate::None, &Translate::None) => Ok(Translate::None),
(&Translate::Translate3D(_, ..), _) | (_, &Translate::Translate3D(_, ..)) => {
let (from, to) = (self.resolve(), other.resolve());
Ok(Translate::Translate3D(
from.0.animate(&to.0, procedure)?,
from.1.animate(&to.1, procedure)?,
from.2.animate(&to.2, procedure)?,
))
},
(&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => { (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
let (from, to) = (self.resolve(), other.resolve()); let (from, to) = (self.resolve(), other.resolve());
Ok(Translate::Translate( Ok(Translate::Translate(
from.0.animate(&to.0, procedure)?, from.0.animate(&to.0, procedure)?,
from.1.animate(&to.1, procedure)?, from.1.animate(&to.1, procedure)?,
from.2.animate(&to.2, procedure)?,
)) ))
}, },
} }
@ -1417,8 +1414,7 @@ impl ComputedScale {
// Unspecified scales default to 1 // Unspecified scales default to 1
match *self { match *self {
Scale::None => (1.0, 1.0, 1.0), Scale::None => (1.0, 1.0, 1.0),
Scale::Scale3D(sx, sy, sz) => (sx, sy, sz), Scale::Scale(sx, sy, sz) => (sx, sy, sz),
Scale::Scale(sx, sy) => (sx, sy, 1.),
} }
} }
} }
@ -1428,7 +1424,7 @@ impl Animate for ComputedScale {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) { match (self, other) {
(&Scale::None, &Scale::None) => Ok(Scale::None), (&Scale::None, &Scale::None) => Ok(Scale::None),
(&Scale::Scale3D(_, ..), _) | (_, &Scale::Scale3D(_, ..)) => { (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
let (from, to) = (self.resolve(), other.resolve()); let (from, to) = (self.resolve(), other.resolve());
// For transform lists, we add by appending to the list of // For transform lists, we add by appending to the list of
// transform functions. However, ComputedScale cannot be // transform functions. However, ComputedScale cannot be
@ -1436,24 +1432,12 @@ impl Animate for ComputedScale {
// result here. // result here.
if procedure == Procedure::Add { if procedure == Procedure::Add {
// scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2) // scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)
return Ok(Scale::Scale3D(from.0 * to.0, from.1 * to.1, from.2 * to.2)); return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
}
Ok(Scale::Scale3D(
animate_multiplicative_factor(from.0, to.0, procedure)?,
animate_multiplicative_factor(from.1, to.1, procedure)?,
animate_multiplicative_factor(from.2, to.2, procedure)?,
))
},
(&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
let (from, to) = (self.resolve(), other.resolve());
// As with Scale3D, addition needs special handling.
if procedure == Procedure::Add {
// scale(x1,y1)*scale(x2,y2) = scale(x1*x2, y1*y2)
return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1));
} }
Ok(Scale::Scale( Ok(Scale::Scale(
animate_multiplicative_factor(from.0, to.0, procedure)?, animate_multiplicative_factor(from.0, to.0, procedure)?,
animate_multiplicative_factor(from.1, to.1, procedure)?, animate_multiplicative_factor(from.1, to.1, procedure)?,
animate_multiplicative_factor(from.2, to.2, procedure)?,
)) ))
}, },
} }

View file

@ -58,7 +58,7 @@ pub use self::GenericGridLine as GridLine;
impl<Integer> GridLine<Integer> impl<Integer> GridLine<Integer>
where where
Integer: Zero, Integer: PartialEq + Zero,
{ {
/// The `auto` value. /// The `auto` value.
pub fn auto() -> Self { pub fn auto() -> Self {
@ -73,11 +73,27 @@ where
pub fn is_auto(&self) -> bool { pub fn is_auto(&self) -> bool {
self.ident == atom!("") && self.line_num.is_zero() && !self.is_span self.ident == atom!("") && self.line_num.is_zero() && !self.is_span
} }
/// Check whether this `<grid-line>` represents a `<custom-ident>` value.
pub fn is_ident_only(&self) -> bool {
self.ident != atom!("") && self.line_num.is_zero() && !self.is_span
}
/// Check if `self` makes `other` omittable according to the rules at:
/// https://drafts.csswg.org/css-grid/#propdef-grid-column
/// https://drafts.csswg.org/css-grid/#propdef-grid-area
pub fn can_omit(&self, other: &Self) -> bool {
if self.is_ident_only() {
self == other
} else {
other.is_auto()
}
}
} }
impl<Integer> ToCss for GridLine<Integer> impl<Integer> ToCss for GridLine<Integer>
where where
Integer: ToCss + Zero, Integer: ToCss + PartialEq + Zero,
{ {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
@ -261,6 +277,19 @@ pub enum GenericTrackSize<L> {
pub use self::GenericTrackSize as TrackSize; pub use self::GenericTrackSize as TrackSize;
impl<L> TrackSize<L> { impl<L> TrackSize<L> {
/// The initial value.
const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);
/// Returns the initial value.
pub const fn initial_value() -> Self {
Self::INITIAL_VALUE
}
/// Returns true if `self` is the initial value.
pub fn is_initial(&self) -> bool {
matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
}
/// Check whether this is a `<fixed-size>` /// Check whether this is a `<fixed-size>`
/// ///
/// <https://drafts.csswg.org/css-grid/#typedef-fixed-size> /// <https://drafts.csswg.org/css-grid/#typedef-fixed-size>
@ -286,17 +315,9 @@ impl<L> TrackSize<L> {
} }
} }
impl<L: PartialEq> TrackSize<L> {
/// Return true if it is `auto`.
#[inline]
pub fn is_auto(&self) -> bool {
*self == TrackSize::Breadth(TrackBreadth::Auto)
}
}
impl<L> Default for TrackSize<L> { impl<L> Default for TrackSize<L> {
fn default() -> Self { fn default() -> Self {
TrackSize::Breadth(TrackBreadth::Auto) Self::initial_value()
} }
} }
@ -513,9 +534,27 @@ pub enum GenericTrackListValue<LengthPercentage, Integer> {
pub use self::GenericTrackListValue as TrackListValue; pub use self::GenericTrackListValue as TrackListValue;
impl<L, I> TrackListValue<L, I> { impl<L, I> TrackListValue<L, I> {
// FIXME: can't use TrackSize::initial_value() here b/c rustc error "is not yet stable as a const fn"
const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));
fn is_repeat(&self) -> bool { fn is_repeat(&self) -> bool {
matches!(*self, TrackListValue::TrackRepeat(..)) matches!(*self, TrackListValue::TrackRepeat(..))
} }
/// Returns true if `self` is the initial value.
pub fn is_initial(&self) -> bool {
matches!(
*self,
TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))
) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
}
}
impl<L, I> Default for TrackListValue<L, I> {
#[inline]
fn default() -> Self {
Self::INITIAL_VALUE
}
} }
/// A grid `<track-list>` type. /// A grid `<track-list>` type.
@ -755,6 +794,9 @@ pub enum GenericGridTemplateComponent<L, I> {
pub use self::GenericGridTemplateComponent as GridTemplateComponent; pub use self::GenericGridTemplateComponent as GridTemplateComponent;
impl<L, I> GridTemplateComponent<L, I> { impl<L, I> GridTemplateComponent<L, I> {
/// The initial value.
const INITIAL_VALUE: Self = Self::None;
/// Returns length of the <track-list>s <track-size> /// Returns length of the <track-list>s <track-size>
pub fn track_list_len(&self) -> usize { pub fn track_list_len(&self) -> usize {
match *self { match *self {
@ -762,4 +804,16 @@ impl<L, I> GridTemplateComponent<L, I> {
_ => 0, _ => 0,
} }
} }
/// Returns true if `self` is the initial value.
pub fn is_initial(&self) -> bool {
matches!(*self, Self::None) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
}
}
impl<L, I> Default for GridTemplateComponent<L, I> {
#[inline]
fn default() -> Self {
Self::INITIAL_VALUE
}
} }

View file

@ -700,40 +700,52 @@ where
pub enum GenericScale<Number> { pub enum GenericScale<Number> {
/// 'none' /// 'none'
None, None,
/// '<number>{1,2}' /// '<number>{1,3}'
Scale(Number, Number), Scale(Number, Number, Number),
/// '<number>{3}'
Scale3D(Number, Number, Number),
} }
pub use self::GenericScale as Scale; pub use self::GenericScale as Scale;
impl<Number: ToCss + PartialEq> ToCss for Scale<Number> { impl<Number> ToCss for Scale<Number>
where
Number: ToCss + PartialEq + Copy,
f32: From<Number>,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
W: fmt::Write, W: fmt::Write,
f32: From<Number>,
{ {
match *self { match *self {
Scale::None => dest.write_str("none"), Scale::None => dest.write_str("none"),
Scale::Scale(ref x, ref y) => { Scale::Scale(ref x, ref y, ref z) => {
x.to_css(dest)?; x.to_css(dest)?;
if x != y {
let is_3d = f32::from(*z) != 1.0;
if is_3d || x != y {
dest.write_char(' ')?; dest.write_char(' ')?;
y.to_css(dest)?; y.to_css(dest)?;
} }
if is_3d {
dest.write_char(' ')?;
z.to_css(dest)?;
}
Ok(()) Ok(())
}, },
Scale::Scale3D(ref x, ref y, ref z) => {
x.to_css(dest)?;
dest.write_char(' ')?;
y.to_css(dest)?;
dest.write_char(' ')?;
z.to_css(dest)
},
} }
} }
} }
#[inline]
fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero, Length: Zero>(
_: &LengthPercentage,
y: &LengthPercentage,
z: &Length,
) -> bool {
y.is_zero() && z.is_zero()
}
#[derive( #[derive(
Clone, Clone,
Debug, Debug,
@ -755,25 +767,24 @@ impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
/// or two values (per usual, if the second value is 0px, the default, it must /// or two values (per usual, if the second value is 0px, the default, it must
/// be omitted when serializing). /// be omitted when serializing).
/// ///
/// If a 3d translation is specified, all three values must be serialized. /// If a 3d translation is specified and the value can be expressed as 2d, we treat as 2d and
/// /// serialize accoringly. Otherwise, we serialize all three values.
/// We don't omit the 3rd component even if it is 0px for now, and the /// https://github.com/w3c/csswg-drafts/issues/3305
/// related spec issue is https://github.com/w3c/csswg-drafts/issues/3305
/// ///
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms> /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
pub enum GenericTranslate<LengthPercentage, Length> pub enum GenericTranslate<LengthPercentage, Length>
where where
LengthPercentage: Zero, LengthPercentage: Zero,
Length: Zero,
{ {
/// 'none' /// 'none'
None, None,
/// '<length-percentage>' or '<length-percentage> <length-percentage>' /// <length-percentage> [ <length-percentage> <length>? ]?
Translate( Translate(
LengthPercentage, LengthPercentage,
#[css(skip_if = "Zero::is_zero")] LengthPercentage, #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
#[css(skip_if = "Zero::is_zero")] Length,
), ),
/// '<length-percentage> <length-percentage> <length>'
Translate3D(LengthPercentage, LengthPercentage, Length),
} }
pub use self::GenericTranslate as Translate; pub use self::GenericTranslate as Translate;

View file

@ -208,24 +208,34 @@ impl Angle {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allow_unitless_zero: AllowUnitlessZeroAngle, allow_unitless_zero: AllowUnitlessZeroAngle,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
// FIXME: remove clone() when lifetimes are non-lexical let t = input.next()?;
let token = input.next()?.clone(); match *t {
match token {
Token::Dimension { Token::Dimension {
value, ref unit, .. value, ref unit, ..
} => { } => {
Angle::parse_dimension(value, unit, /* from_calc = */ false) match Angle::parse_dimension(value, unit, /* from_calc = */ false) {
Ok(angle) => Ok(angle),
Err(()) => {
let t = t.clone();
Err(input.new_unexpected_token_error(t))
},
}
}, },
Token::Number { value, .. } if value == 0. => match allow_unitless_zero { Token::Number { value, .. } if value == 0. => match allow_unitless_zero {
AllowUnitlessZeroAngle::Yes => Ok(Angle::zero()), AllowUnitlessZeroAngle::Yes => Ok(Angle::zero()),
AllowUnitlessZeroAngle::No => Err(()), AllowUnitlessZeroAngle::No => {
let t = t.clone();
Err(input.new_unexpected_token_error(t))
},
}, },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
return input.parse_nested_block(|i| CalcNode::parse_angle(context, i)); return input.parse_nested_block(|i| CalcNode::parse_angle(context, i));
}, },
_ => Err(()), ref t => {
let t = t.clone();
Err(input.new_unexpected_token_error(t))
},
} }
.map_err(|()| input.new_unexpected_token_error(token.clone()))
} }
} }

View file

@ -109,8 +109,6 @@ pub enum DisplayInside {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozGridLine, MozGridLine,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozStack,
#[cfg(feature = "gecko")]
MozDeck, MozDeck,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozGroupbox, MozGroupbox,
@ -243,8 +241,6 @@ impl Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine); pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
#[cfg(feature = "gecko")]
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck); pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox); pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox);
@ -669,8 +665,6 @@ impl Parse for Display {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine, "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
#[cfg(feature = "gecko")]
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck, "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-groupbox" if moz_display_values_enabled(context) => Display::MozGroupbox, "-moz-groupbox" if moz_display_values_enabled(context) => Display::MozGroupbox,
@ -1157,7 +1151,9 @@ fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
LonghandId::Opacity => WillChangeBits::OPACITY, LonghandId::Opacity => WillChangeBits::OPACITY,
LonghandId::Transform => WillChangeBits::TRANSFORM, LonghandId::Transform => WillChangeBits::TRANSFORM,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale => WillChangeBits::TRANSFORM, LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => {
WillChangeBits::TRANSFORM
},
_ => WillChangeBits::empty(), _ => WillChangeBits::empty(),
}; };

View file

@ -163,9 +163,8 @@ impl CalcNode {
expected_unit: CalcUnit, expected_unit: CalcUnit,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove early returns when lifetimes are non-lexical
match (input.next()?, expected_unit) { match (input.next()?, expected_unit) {
(&Token::Number { value, .. }, _) => return Ok(CalcNode::Number(value)), (&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)),
( (
&Token::Dimension { &Token::Dimension {
value, ref unit, .. value, ref unit, ..
@ -177,24 +176,18 @@ impl CalcNode {
value, ref unit, .. value, ref unit, ..
}, },
CalcUnit::LengthPercentage, CalcUnit::LengthPercentage,
) => { ) => NoCalcLength::parse_dimension(context, value, unit)
return NoCalcLength::parse_dimension(context, value, unit)
.map(CalcNode::Length) .map(CalcNode::Length)
.map_err(|()| { .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
});
},
( (
&Token::Dimension { &Token::Dimension {
value, ref unit, .. value, ref unit, ..
}, },
CalcUnit::Angle, CalcUnit::Angle,
) => { ) => {
return Angle::parse_dimension(value, unit, /* from_calc = */ true) Angle::parse_dimension(value, unit, /* from_calc = */ true)
.map(CalcNode::Angle) .map(CalcNode::Angle)
.map_err(|()| { .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
});
}, },
( (
&Token::Dimension { &Token::Dimension {
@ -202,21 +195,22 @@ impl CalcNode {
}, },
CalcUnit::Time, CalcUnit::Time,
) => { ) => {
return Time::parse_dimension(value, unit, /* from_calc = */ true) Time::parse_dimension(value, unit, /* from_calc = */ true)
.map(CalcNode::Time) .map(CalcNode::Time)
.map_err(|()| { .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
});
}, },
(&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) | (&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => { (&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
return Ok(CalcNode::Percentage(unit_value)); Ok(CalcNode::Percentage(unit_value))
}, },
(&Token::ParenthesisBlock, _) => {}, (&Token::ParenthesisBlock, _) => {
(&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {},
(t, _) => return Err(location.new_unexpected_token_error(t.clone())),
}
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit)) input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
},
(&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
},
(t, _) => Err(location.new_unexpected_token_error(t.clone())),
}
} }
/// Parse a top-level `calc` expression, with all nested sub-expressions. /// Parse a top-level `calc` expression, with all nested sub-expressions.
@ -236,8 +230,7 @@ impl CalcNode {
if input.is_exhausted() { if input.is_exhausted() {
break; // allow trailing whitespace break; // allow trailing whitespace
} }
// FIXME: remove clone() when lifetimes are non-lexical match *input.next()? {
match input.next()?.clone() {
Token::Delim('+') => { Token::Delim('+') => {
let rhs = Self::parse_product(context, input, expected_unit)?; let rhs = Self::parse_product(context, input, expected_unit)?;
let new_root = CalcNode::Sum(Box::new(root), Box::new(rhs)); let new_root = CalcNode::Sum(Box::new(root), Box::new(rhs));
@ -248,7 +241,10 @@ impl CalcNode {
let new_root = CalcNode::Sub(Box::new(root), Box::new(rhs)); let new_root = CalcNode::Sub(Box::new(root), Box::new(rhs));
root = new_root; root = new_root;
}, },
t => return Err(input.new_unexpected_token_error(t)), ref t => {
let t = t.clone();
return Err(input.new_unexpected_token_error(t));
},
} }
}, },
_ => { _ => {

View file

@ -36,9 +36,6 @@ pub enum Color {
/// A system color /// A system color
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
System(SystemColor), System(SystemColor),
/// A special color keyword value used in Gecko
#[cfg(feature = "gecko")]
Special(gecko::SpecialColorKeyword),
/// Quirksmode-only rule for inheriting color from the body /// Quirksmode-only rule for inheriting color from the body
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
InheritFromBodyQuirk, InheritFromBodyQuirk,
@ -143,6 +140,8 @@ pub enum SystemColor {
Windowframe, Windowframe,
Windowtext, Windowtext,
MozButtondefault, MozButtondefault,
MozDefaultColor,
MozDefaultBackgroundColor,
MozDialog, MozDialog,
MozDialogtext, MozDialogtext,
/// Used to highlight valid regions to drop something onto. /// Used to highlight valid regions to drop something onto.
@ -231,6 +230,10 @@ pub enum SystemColor {
/// colors. /// colors.
MozNativehyperlinktext, MozNativehyperlinktext,
MozHyperlinktext,
MozActivehyperlinktext,
MozVisitedhyperlinktext,
/// Combobox widgets /// Combobox widgets
MozComboboxtext, MozComboboxtext,
MozCombobox, MozCombobox,
@ -246,24 +249,20 @@ impl SystemColor {
#[inline] #[inline]
fn compute(&self, cx: &Context) -> ComputedColor { fn compute(&self, cx: &Context) -> ComputedColor {
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
unsafe {
convert_nscolor_to_computedcolor(bindings::Gecko_GetLookAndFeelSystemColor(
*self as i32,
cx.device().document(),
))
}
}
}
#[cfg(feature = "gecko")] let prefs = cx.device().pref_sheet_prefs();
mod gecko {
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] convert_nscolor_to_computedcolor(match *self {
pub enum SpecialColorKeyword { SystemColor::MozDefaultColor => prefs.mDefaultColor,
MozDefaultColor, SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
MozDefaultBackgroundColor, SystemColor::MozHyperlinktext => prefs.mLinkColor,
MozHyperlinktext, SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor,
MozActivehyperlinktext, SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
MozVisitedhyperlinktext,
_ => unsafe {
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
},
})
} }
} }
@ -364,10 +363,6 @@ impl Parse for Color {
if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) { if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) {
return Ok(Color::System(system)); return Ok(Color::System(system));
} }
if let Ok(c) = input.try(gecko::SpecialColorKeyword::parse) {
return Ok(Color::Special(c));
}
} }
match e.kind { match e.kind {
@ -401,8 +396,6 @@ impl ToCss for Color {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::System(system) => system.to_css(dest), Color::System(system) => system.to_css(dest),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::Special(special) => special.to_css(dest),
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => Ok(()), Color::InheritFromBodyQuirk => Ok(()),
} }
} }
@ -553,18 +546,6 @@ impl Color {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::System(system) => system.compute(_context?), Color::System(system) => system.compute(_context?),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Color::Special(special) => {
use self::gecko::SpecialColorKeyword as Keyword;
let prefs = _context?.device().pref_sheet_prefs();
convert_nscolor_to_computedcolor(match special {
Keyword::MozDefaultColor => prefs.mDefaultColor,
Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
Keyword::MozHyperlinktext => prefs.mLinkColor,
Keyword::MozActivehyperlinktext => prefs.mActiveLinkColor,
Keyword::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
})
},
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => { Color::InheritFromBodyQuirk => {
ComputedColor::rgba(_context?.device().body_text_color()) ComputedColor::rgba(_context?.device().body_text_color())
}, },

View file

@ -63,7 +63,10 @@ fn parse_counters<'i, 't>(
let location = input.current_source_location(); let location = input.current_source_location();
let name = match input.next() { let name = match input.next() {
Ok(&Token::Ident(ref ident)) => CustomIdent::from_ident(location, ident, &["none"])?, Ok(&Token::Ident(ref ident)) => CustomIdent::from_ident(location, ident, &["none"])?,
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())), Ok(t) => {
let t = t.clone();
return Err(location.new_unexpected_token_error(t));
},
Err(_) => break, Err(_) => break,
}; };
@ -147,57 +150,60 @@ impl Parse for Content {
continue; continue;
} }
} }
// FIXME: remove clone() when lifetimes are non-lexical match input.next() {
match input.next().map(|t| t.clone()) { Ok(&Token::QuotedString(ref value)) => {
Ok(Token::QuotedString(ref value)) => {
content.push(generics::ContentItem::String( content.push(generics::ContentItem::String(
value.as_ref().to_owned().into_boxed_str(), value.as_ref().to_owned().into_boxed_str(),
)); ));
}, },
Ok(Token::Function(ref name)) => { Ok(&Token::Function(ref name)) => {
let result = match_ignore_ascii_case! { &name, let result = match_ignore_ascii_case! { &name,
"counter" => Some(input.parse_nested_block(|input| { "counter" => input.parse_nested_block(|input| {
let location = input.current_source_location(); let location = input.current_source_location();
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
let style = Content::parse_counter_style(context, input); let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counter(name, style)) Ok(generics::ContentItem::Counter(name, style))
})), }),
"counters" => Some(input.parse_nested_block(|input| { "counters" => input.parse_nested_block(|input| {
let location = input.current_source_location(); let location = input.current_source_location();
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
input.expect_comma()?; input.expect_comma()?;
let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str(); let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str();
let style = Content::parse_counter_style(context, input); let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counters(name, separator, style)) Ok(generics::ContentItem::Counters(name, separator, style))
})), }),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"attr" => Some(input.parse_nested_block(|input| { "attr" => input.parse_nested_block(|input| {
Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
})), }),
_ => None _ => {
}; let name = name.clone();
match result {
Some(result) => content.push(result?),
None => {
return Err(input.new_custom_error( return Err(input.new_custom_error(
StyleParseErrorKind::UnexpectedFunction(name.clone()), StyleParseErrorKind::UnexpectedFunction(name),
)); ))
},
} }
}?;
content.push(result);
}, },
Ok(Token::Ident(ref ident)) => { Ok(&Token::Ident(ref ident)) => {
content.push(match_ignore_ascii_case! { &ident, content.push(match_ignore_ascii_case! { &ident,
"open-quote" => generics::ContentItem::OpenQuote, "open-quote" => generics::ContentItem::OpenQuote,
"close-quote" => generics::ContentItem::CloseQuote, "close-quote" => generics::ContentItem::CloseQuote,
"no-open-quote" => generics::ContentItem::NoOpenQuote, "no-open-quote" => generics::ContentItem::NoOpenQuote,
"no-close-quote" => generics::ContentItem::NoCloseQuote, "no-close-quote" => generics::ContentItem::NoCloseQuote,
_ => return Err(input.new_custom_error( _ =>{
SelectorParseErrorKind::UnexpectedIdent(ident.clone()) let ident = ident.clone();
)) return Err(input.new_custom_error(
SelectorParseErrorKind::UnexpectedIdent(ident)
));
}
}); });
}, },
Err(_) => break, Err(_) => break,
Ok(t) => return Err(input.new_unexpected_token_error(t)), Ok(t) => {
let t = t.clone();
return Err(input.new_unexpected_token_error(t));
},
} }
} }
if content.is_empty() { if content.is_empty() {

View file

@ -1212,15 +1212,15 @@ impl Parse for FontVariantAlternates {
parsed_alternates |= $flag; parsed_alternates |= $flag;
) )
); );
while let Ok(_) = input.try(|input| { while let Ok(_) = input.try(|input| match *input.next()? {
// FIXME: remove clone() when lifetimes are non-lexical
match input.next()?.clone() {
Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
alternates.push(VariantAlternates::HistoricalForms); alternates.push(VariantAlternates::HistoricalForms);
Ok(()) Ok(())
}, },
Token::Function(ref name) => input.parse_nested_block(|i| { Token::Function(ref name) => {
let name = name.clone();
input.parse_nested_block(|i| {
match_ignore_ascii_case! { &name, match_ignore_ascii_case! { &name,
"swash" => { "swash" => {
check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH); check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
@ -1270,9 +1270,9 @@ impl Parse for FontVariantAlternates {
}, },
_ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
}), })
},
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
}) {} }) {}
if parsed_alternates.is_empty() { if parsed_alternates.is_empty() {

View file

@ -102,8 +102,8 @@ impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
use style_traits::{Separator, Space}; use style_traits::{Separator, Space};
let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?; let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;
if track_sizes.len() == 1 && track_sizes[0].is_auto() { if track_sizes.len() == 1 && track_sizes[0].is_initial() {
//`auto`, which is the initial value, is always represented by an empty slice. // A single track with the initial value is always represented by an empty slice.
return Ok(Default::default()); return Ok(Default::default());
} }
return Ok(ImplicitGridTracks(track_sizes.into())); return Ok(ImplicitGridTracks(track_sizes.into()));

View file

@ -192,62 +192,57 @@ impl Parse for Gradient {
Radial, Radial,
} }
// FIXME: remove clone() when lifetimes are non-lexical let func = input.expect_function()?;
let func = input.expect_function()?.clone(); let (shape, repeating, mut compat_mode) = match_ignore_ascii_case! { &func,
let result = match_ignore_ascii_case! { &func,
"linear-gradient" => { "linear-gradient" => {
Some((Shape::Linear, false, GradientCompatMode::Modern)) (Shape::Linear, false, GradientCompatMode::Modern)
}, },
"-webkit-linear-gradient" => { "-webkit-linear-gradient" => {
Some((Shape::Linear, false, GradientCompatMode::WebKit)) (Shape::Linear, false, GradientCompatMode::WebKit)
}, },
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-linear-gradient" => { "-moz-linear-gradient" => {
Some((Shape::Linear, false, GradientCompatMode::Moz)) (Shape::Linear, false, GradientCompatMode::Moz)
}, },
"repeating-linear-gradient" => { "repeating-linear-gradient" => {
Some((Shape::Linear, true, GradientCompatMode::Modern)) (Shape::Linear, true, GradientCompatMode::Modern)
}, },
"-webkit-repeating-linear-gradient" => { "-webkit-repeating-linear-gradient" => {
Some((Shape::Linear, true, GradientCompatMode::WebKit)) (Shape::Linear, true, GradientCompatMode::WebKit)
}, },
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-repeating-linear-gradient" => { "-moz-repeating-linear-gradient" => {
Some((Shape::Linear, true, GradientCompatMode::Moz)) (Shape::Linear, true, GradientCompatMode::Moz)
}, },
"radial-gradient" => { "radial-gradient" => {
Some((Shape::Radial, false, GradientCompatMode::Modern)) (Shape::Radial, false, GradientCompatMode::Modern)
}, },
"-webkit-radial-gradient" => { "-webkit-radial-gradient" => {
Some((Shape::Radial, false, GradientCompatMode::WebKit)) (Shape::Radial, false, GradientCompatMode::WebKit)
}, },
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-radial-gradient" => { "-moz-radial-gradient" => {
Some((Shape::Radial, false, GradientCompatMode::Moz)) (Shape::Radial, false, GradientCompatMode::Moz)
}, },
"repeating-radial-gradient" => { "repeating-radial-gradient" => {
Some((Shape::Radial, true, GradientCompatMode::Modern)) (Shape::Radial, true, GradientCompatMode::Modern)
}, },
"-webkit-repeating-radial-gradient" => { "-webkit-repeating-radial-gradient" => {
Some((Shape::Radial, true, GradientCompatMode::WebKit)) (Shape::Radial, true, GradientCompatMode::WebKit)
}, },
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
"-moz-repeating-radial-gradient" => { "-moz-repeating-radial-gradient" => {
Some((Shape::Radial, true, GradientCompatMode::Moz)) (Shape::Radial, true, GradientCompatMode::Moz)
}, },
"-webkit-gradient" => { "-webkit-gradient" => {
return input.parse_nested_block(|i| { return input.parse_nested_block(|i| {
Self::parse_webkit_gradient_argument(context, i) Self::parse_webkit_gradient_argument(context, i)
}); });
}, },
_ => None, _ => {
}; let func = func.clone();
let (shape, repeating, mut compat_mode) = match result {
Some(result) => result,
None => {
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func))); return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
}, }
}; };
let (kind, items) = input.parse_nested_block(|i| { let (kind, items) = input.parse_nested_block(|i| {

View file

@ -587,39 +587,34 @@ impl Length {
num_context: AllowedNumericType, num_context: AllowedNumericType,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
// FIXME: remove early returns when lifetimes are non-lexical
{
let location = input.current_source_location(); let location = input.current_source_location();
let token = input.next()?; let token = input.next()?;
match *token { match *token {
Token::Dimension { Token::Dimension {
value, ref unit, .. value, ref unit, ..
} if num_context.is_ok(context.parsing_mode, value) => { } if num_context.is_ok(context.parsing_mode, value) => {
return NoCalcLength::parse_dimension(context, value, unit) NoCalcLength::parse_dimension(context, value, unit)
.map(Length::NoCalc) .map(Length::NoCalc)
.map_err(|()| location.new_unexpected_token_error(token.clone())); .map_err(|()| location.new_unexpected_token_error(token.clone()))
}, },
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
if value != 0. && if value != 0. &&
!context.parsing_mode.allows_unitless_lengths() && !context.parsing_mode.allows_unitless_lengths() &&
!allow_quirks.allowed(context.quirks_mode) !allow_quirks.allowed(context.quirks_mode)
{ {
return Err( return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
);
} }
return Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px( Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
value, value,
)))); ))))
}, },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input
ref token => return Err(location.new_unexpected_token_error(token.clone())), .parse_nested_block(|input| {
}
}
input.parse_nested_block(|input| {
CalcNode::parse_length(context, input, num_context) CalcNode::parse_length(context, input, num_context)
.map(|calc| Length::Calc(Box::new(calc))) .map(|calc| Length::Calc(Box::new(calc)))
}) }),
ref token => return Err(location.new_unexpected_token_error(token.clone())),
}
} }
/// Parse a non-negative length /// Parse a non-negative length
@ -809,8 +804,6 @@ impl LengthPercentage {
num_context: AllowedNumericType, num_context: AllowedNumericType,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
// FIXME: remove early returns when lifetimes are non-lexical
{
let location = input.current_source_location(); let location = input.current_source_location();
let token = input.next()?; let token = input.next()?;
match *token { match *token {
@ -838,15 +831,14 @@ impl LengthPercentage {
return Ok(LengthPercentage::Length(NoCalcLength::from_px(value))); return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
} }
}, },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
_ => return Err(location.new_unexpected_token_error(token.clone())),
}
}
let calc = input.parse_nested_block(|i| { let calc = input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i, num_context) CalcNode::parse_length_or_percentage(context, i, num_context)
})?; })?;
Ok(LengthPercentage::Calc(Box::new(calc))) Ok(LengthPercentage::Calc(Box::new(calc)))
},
_ => return Err(location.new_unexpected_token_error(token.clone())),
}
} }
/// Parses allowing the unitless length quirk. /// Parses allowing the unitless length quirk.

View file

@ -136,24 +136,22 @@ fn parse_number_with_clamping_mode<'i, 't>(
clamping_mode: AllowedNumericType, clamping_mode: AllowedNumericType,
) -> Result<Number, ParseError<'i>> { ) -> Result<Number, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove early returns when lifetimes are non-lexical
match *input.next()? { match *input.next()? {
Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => { Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
return Ok(Number { Ok(Number {
value: value.min(f32::MAX).max(f32::MIN), value: value.min(f32::MAX).max(f32::MIN),
calc_clamping_mode: None, calc_clamping_mode: None,
}); })
}, },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
ref t => return Err(location.new_unexpected_token_error(t.clone())),
}
let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?; let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?;
Ok(Number { Ok(Number {
value: result.min(f32::MAX).max(f32::MIN), value: result.min(f32::MAX).max(f32::MIN),
calc_clamping_mode: Some(clamping_mode), calc_clamping_mode: Some(clamping_mode),
}) })
},
ref t => Err(location.new_unexpected_token_error(t.clone())),
}
} }
/// A CSS `<number>` specified value. /// A CSS `<number>` specified value.
@ -540,19 +538,16 @@ impl Parse for Integer {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove early returns when lifetimes are non-lexical
match *input.next()? { match *input.next()? {
Token::Number { Token::Number {
int_value: Some(v), .. int_value: Some(v), ..
} => return Ok(Integer::new(v)), } => Ok(Integer::new(v)),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
ref t => return Err(location.new_unexpected_token_error(t.clone())),
}
let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?; let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?;
Ok(Integer::from_calc(result)) Ok(Integer::from_calc(result))
},
ref t => Err(location.new_unexpected_token_error(t.clone())),
}
} }
} }

View file

@ -16,11 +16,24 @@ use style_traits::{ParseError, StyleParseErrorKind};
/// The specified value of `offset-path`. /// The specified value of `offset-path`.
pub type OffsetPath = GenericOffsetPath<Angle>; pub type OffsetPath = GenericOffsetPath<Angle>;
#[cfg(feature = "gecko")]
fn is_ray_enabled() -> bool {
static_prefs::pref!("layout.css.motion-path-ray.enabled")
}
#[cfg(feature = "servo")]
fn is_ray_enabled() -> bool {
false
}
impl Parse for RayFunction<Angle> { impl Parse for RayFunction<Angle> {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
if !is_ray_enabled() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let mut angle = None; let mut angle = None;
let mut size = None; let mut size = None;
let mut contain = false; let mut contain = false;

View file

@ -111,18 +111,15 @@ impl Percentage {
num_context: AllowedNumericType, num_context: AllowedNumericType,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove early returns when lifetimes are non-lexical
match *input.next()? { match *input.next()? {
Token::Percentage { unit_value, .. } Token::Percentage { unit_value, .. }
if num_context.is_ok(context.parsing_mode, unit_value) => if num_context.is_ok(context.parsing_mode, unit_value) =>
{ {
return Ok(Percentage::new(unit_value)); Ok(Percentage::new(unit_value))
} },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
ref t => return Err(location.new_unexpected_token_error(t.clone())), let result =
} input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
let result = input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
// TODO(emilio): -moz-image-rect is the only thing that uses // TODO(emilio): -moz-image-rect is the only thing that uses
// the clamping mode... I guess we could disallow it... // the clamping mode... I guess we could disallow it...
@ -130,6 +127,9 @@ impl Percentage {
value: result, value: result,
calc_clamping_mode: Some(num_context), calc_clamping_mode: Some(num_context),
}) })
},
ref t => Err(location.new_unexpected_token_error(t.clone())),
}
} }
/// Parses a percentage token, but rejects it if it's negative. /// Parses a percentage token, but rejects it if it's negative.

View file

@ -83,28 +83,26 @@ impl Time {
use style_traits::ParsingMode; use style_traits::ParsingMode;
let location = input.current_source_location(); let location = input.current_source_location();
// FIXME: remove early returns when lifetimes are non-lexical match *input.next()? {
match input.next() {
// Note that we generally pass ParserContext to is_ok() to check // Note that we generally pass ParserContext to is_ok() to check
// that the ParserMode of the ParserContext allows all numeric // that the ParserMode of the ParserContext allows all numeric
// values for SMIL regardless of clamping_mode, but in this Time // values for SMIL regardless of clamping_mode, but in this Time
// value case, the value does not animate for SMIL at all, so we use // value case, the value does not animate for SMIL at all, so we use
// ParsingMode::DEFAULT directly. // ParsingMode::DEFAULT directly.
Ok(&Token::Dimension { Token::Dimension {
value, ref unit, .. value, ref unit, ..
}) if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => { } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
return Time::parse_dimension(value, unit, /* from_calc = */ false).map_err(|()| { Time::parse_dimension(value, unit, /* from_calc = */ false)
location.new_custom_error(StyleParseErrorKind::UnspecifiedError) .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
});
}, },
Ok(&Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {}, Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
Err(e) => return Err(e.into()),
}
match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) { match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time), Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time),
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
},
ref t => return Err(location.new_unexpected_token_error(t.clone())),
}
} }
/// Parses a non-negative time value. /// Parses a non-negative time value.

View file

@ -12,7 +12,9 @@ use crate::values::generics::transform::{Matrix, Matrix3D};
use crate::values::specified::position::{ use crate::values::specified::position::{
HorizontalPositionKeyword, Side, VerticalPositionKeyword, HorizontalPositionKeyword, Side, VerticalPositionKeyword,
}; };
use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number}; use crate::values::specified::{
self, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,
};
use crate::Zero; use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use style_traits::{ParseError, StyleParseErrorKind}; use style_traits::{ParseError, StyleParseErrorKind};
@ -163,32 +165,32 @@ impl Transform {
Ok(generic::TransformOperation::Translate3D(tx, ty, tz)) Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
}, },
"scale" => { "scale" => {
let sx = Number::parse(context, input)?; let sx = NumberOrPercentage::parse(context, input)?.to_number();
if input.try(|input| input.expect_comma()).is_ok() { if input.try(|input| input.expect_comma()).is_ok() {
let sy = Number::parse(context, input)?; let sy = NumberOrPercentage::parse(context, input)?.to_number();
Ok(generic::TransformOperation::Scale(sx, sy)) Ok(generic::TransformOperation::Scale(sx, sy))
} else { } else {
Ok(generic::TransformOperation::Scale(sx, sx)) Ok(generic::TransformOperation::Scale(sx, sx))
} }
}, },
"scalex" => { "scalex" => {
let sx = Number::parse(context, input)?; let sx = NumberOrPercentage::parse(context, input)?.to_number();
Ok(generic::TransformOperation::ScaleX(sx)) Ok(generic::TransformOperation::ScaleX(sx))
}, },
"scaley" => { "scaley" => {
let sy = Number::parse(context, input)?; let sy = NumberOrPercentage::parse(context, input)?.to_number();
Ok(generic::TransformOperation::ScaleY(sy)) Ok(generic::TransformOperation::ScaleY(sy))
}, },
"scalez" => { "scalez" => {
let sz = Number::parse(context, input)?; let sz = NumberOrPercentage::parse(context, input)?.to_number();
Ok(generic::TransformOperation::ScaleZ(sz)) Ok(generic::TransformOperation::ScaleZ(sz))
}, },
"scale3d" => { "scale3d" => {
let sx = Number::parse(context, input)?; let sx = NumberOrPercentage::parse(context, input)?.to_number();
input.expect_comma()?; input.expect_comma()?;
let sy = Number::parse(context, input)?; let sy = NumberOrPercentage::parse(context, input)?.to_number();
input.expect_comma()?; input.expect_comma()?;
let sz = Number::parse(context, input)?; let sz = NumberOrPercentage::parse(context, input)?.to_number();
Ok(generic::TransformOperation::Scale3D(sx, sy, sz)) Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
}, },
"rotate" => { "rotate" => {
@ -421,17 +423,22 @@ impl Parse for Translate {
if let Ok(ty) = input.try(|i| specified::LengthPercentage::parse(context, i)) { if let Ok(ty) = input.try(|i| specified::LengthPercentage::parse(context, i)) {
if let Ok(tz) = input.try(|i| specified::Length::parse(context, i)) { if let Ok(tz) = input.try(|i| specified::Length::parse(context, i)) {
// 'translate: <length-percentage> <length-percentage> <length>' // 'translate: <length-percentage> <length-percentage> <length>'
return Ok(generic::Translate::Translate3D(tx, ty, tz)); return Ok(generic::Translate::Translate(tx, ty, tz));
} }
// translate: <length-percentage> <length-percentage>' // translate: <length-percentage> <length-percentage>'
return Ok(generic::Translate::Translate(tx, ty)); return Ok(generic::Translate::Translate(
tx,
ty,
specified::Length::zero(),
));
} }
// 'translate: <length-percentage> ' // 'translate: <length-percentage> '
Ok(generic::Translate::Translate( Ok(generic::Translate::Translate(
tx, tx,
specified::LengthPercentage::zero(), specified::LengthPercentage::zero(),
specified::Length::zero(),
)) ))
} }
} }
@ -440,6 +447,9 @@ impl Parse for Translate {
pub type Scale = generic::Scale<Number>; pub type Scale = generic::Scale<Number>;
impl Parse for Scale { impl Parse for Scale {
/// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
/// and then convert into an Number if it's a Percentage.
/// https://github.com/w3c/csswg-drafts/pull/4396
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
@ -448,18 +458,19 @@ impl Parse for Scale {
return Ok(generic::Scale::None); return Ok(generic::Scale::None);
} }
let sx = Number::parse(context, input)?; let sx = NumberOrPercentage::parse(context, input)?.to_number();
if let Ok(sy) = input.try(|i| Number::parse(context, i)) { if let Ok(sy) = input.try(|i| NumberOrPercentage::parse(context, i)) {
if let Ok(sz) = input.try(|i| Number::parse(context, i)) { let sy = sy.to_number();
if let Ok(sz) = input.try(|i| NumberOrPercentage::parse(context, i)) {
// 'scale: <number> <number> <number>' // 'scale: <number> <number> <number>'
return Ok(generic::Scale::Scale3D(sx, sy, sz)); return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));
} }
// 'scale: <number> <number>' // 'scale: <number> <number>'
return Ok(generic::Scale::Scale(sx, sy)); return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));
} }
// 'scale: <number>' // 'scale: <number>'
Ok(generic::Scale::Scale(sx, sx)) Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))
} }
} }

View file

@ -15,7 +15,7 @@ gecko = []
[dependencies] [dependencies]
app_units = "0.7" app_units = "0.7"
cssparser = "0.27.1" cssparser = "0.27"
bitflags = "1.0" bitflags = "1.0"
euclid = "0.20" euclid = "0.20"
lazy_static = "1" lazy_static = "1"

View file

@ -14,7 +14,7 @@ servo = ["cssparser/serde", "string_cache"]
gecko = [] gecko = []
[dependencies] [dependencies]
cssparser = "0.27.1" cssparser = "0.27"
servo_arc = { path = "../servo_arc" } servo_arc = { path = "../servo_arc" }
smallbitvec = "2.1.1" smallbitvec = "2.1.1"
smallvec = "0.6.6" smallvec = "0.6.6"

View file

@ -11,7 +11,7 @@ doctest = false
[dependencies] [dependencies]
app_units = "0.7" app_units = "0.7"
cssparser = "0.27.1" cssparser = "0.27"
euclid = "0.20" euclid = "0.20"
html5ever = "0.25" html5ever = "0.25"
rayon = "1" rayon = "1"

View file

@ -95,9 +95,6 @@
[CSS Transitions: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (-1) should be [0 1 0 -10deg\]] [CSS Transitions: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (-1) should be [0 1 0 -10deg\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <rotate> from [none\] to [7 -8 9 400grad\] at (1) should be [0.5 -0.57 0.65 400grad\]]
expected: FAIL
[CSS Animations: property <rotate> from [0 1 0 100deg\] to [0 1 0 -100deg\] at (0.25) should be [0 1 0 50deg\]] [CSS Animations: property <rotate> from [0 1 0 100deg\] to [0 1 0 -100deg\] at (0.25) should be [0 1 0 50deg\]]
expected: FAIL expected: FAIL
@ -821,9 +818,6 @@
[Web Animations: property <rotate> from [100deg\] to [-100deg\] at (0) should be [100deg\]] [Web Animations: property <rotate> from [100deg\] to [-100deg\] at (0) should be [100deg\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <rotate> from [none\] to [7 -8 9 400grad\] at (1) should be [0.5 -0.57 0.65 400grad\]]
expected: FAIL
[CSS Animations: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (1) should be [0 1 0 10deg\]] [CSS Animations: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (1) should be [0 1 0 10deg\]]
expected: FAIL expected: FAIL

View file

@ -98,9 +98,6 @@
[CSS Transitions: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]] [CSS Transitions: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (2) should be [7 5 3\]] [CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (2) should be [7 5 3\]]
expected: FAIL expected: FAIL
@ -251,9 +248,6 @@
[Web Animations: property <scale> from [-10 5\] to [10 -5\] at (1) should be [10 -5\]] [Web Animations: property <scale> from [-10 5\] to [10 -5\] at (1) should be [10 -5\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (1) should be [2 0.5\]]
expected: FAIL
[CSS Transitions: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]] [CSS Transitions: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]]
expected: FAIL expected: FAIL
@ -272,9 +266,6 @@
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (2) should be [3 0\]] [CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (2) should be [3 0\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [-10 5 1\] to [1\] at (1) should be [1\]]
expected: FAIL
[CSS Transitions: property <scale> from [-10 5\] to [10 -5\] at (0.75) should be [5 -2.5\]] [CSS Transitions: property <scale> from [-10 5\] to [10 -5\] at (0.75) should be [5 -2.5\]]
expected: FAIL expected: FAIL
@ -311,9 +302,6 @@
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (2) should be [-1 1.5 3\]] [Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (2) should be [-1 1.5 3\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0) should be [1\]]
expected: FAIL
[CSS Animations: property <scale> from [none\] to [none\] at (0.125) should be [none\]] [CSS Animations: property <scale> from [none\] to [none\] at (0.125) should be [none\]]
expected: FAIL expected: FAIL
@ -347,9 +335,6 @@
[CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (2) should be [1.9 1\]] [CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (2) should be [1.9 1\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
expected: FAIL
[CSS Animations: property <scale> from [initial\] to [inherit\] at (1) should be [0.5 1 2\]] [CSS Animations: property <scale> from [initial\] to [inherit\] at (1) should be [0.5 1 2\]]
expected: FAIL expected: FAIL
@ -359,27 +344,15 @@
[Web Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (-1) should be [-6 -50 -400\]] [Web Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (-1) should be [-6 -50 -400\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [26 17 9\] to [2 1\] at (1) should be [2 1\]]
expected: FAIL
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.25) should be [0.875 0.875 1.75\]] [CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.25) should be [0.875 0.875 1.75\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [-10 5 1\] to [1\] at (0) should be [-10 5\]]
expected: FAIL
[CSS Animations: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]] [CSS Animations: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [-10 5 1\] to [1\] at (1) should be [1\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [1\] to [10 -5 0\] at (0.25) should be [3.25 -0.5 0.75\]] [CSS Transitions with transition: all: property <scale> from [1\] to [10 -5 0\] at (0.25) should be [3.25 -0.5 0.75\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (0) should be [2 0.5\]]
expected: FAIL
[CSS Transitions: property <scale> from [none\] to [4 3 2\] at (0.875) should be [3.625 2.75 1.875\]] [CSS Transitions: property <scale> from [none\] to [4 3 2\] at (0.875) should be [3.625 2.75 1.875\]]
expected: FAIL expected: FAIL
@ -479,9 +452,6 @@
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]] [Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [initial\] to [2 0.5 1\] at (1) should be [2 0.5\]]
expected: FAIL
[Web Animations: property <scale> from [-10 5 1\] to [1\] at (0.75) should be [-1.75 2\]] [Web Animations: property <scale> from [-10 5 1\] to [1\] at (0.75) should be [-1.75 2\]]
expected: FAIL expected: FAIL
@ -515,9 +485,6 @@
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (0.125) should be [1.375 1.25 1.125\]] [CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (0.125) should be [1.375 1.25 1.125\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (1) should be [1\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]] [CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]]
expected: FAIL expected: FAIL
@ -533,9 +500,6 @@
[CSS Transitions with transition: all: property <scale> from [26 17 9\] to [2 1\] at (-1) should be [50 33 17\]] [CSS Transitions with transition: all: property <scale> from [26 17 9\] to [2 1\] at (-1) should be [50 33 17\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [1\] to [10 -5 0\] at (0) should be [1\]]
expected: FAIL
[Web Animations: property <scale> from [unset\] to [1.5 1\] at (0.75) should be [1.375 1\]] [Web Animations: property <scale> from [unset\] to [1.5 1\] at (0.75) should be [1.375 1\]]
expected: FAIL expected: FAIL
@ -581,15 +545,6 @@
[Web Animations: property <scale> from [1\] to [10 -5 0\] at (2) should be [19 -11 -1\]] [Web Animations: property <scale> from [1\] to [10 -5 0\] at (2) should be [19 -11 -1\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (1) should be [1\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (0) should be [1\]]
expected: FAIL
[Web Animations: property <scale> from [inherit\] to [initial\] at (1) should be [1\]] [Web Animations: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
expected: FAIL expected: FAIL
@ -605,9 +560,6 @@
[CSS Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]] [CSS Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (1) should be [2 0.5\]]
expected: FAIL
[CSS Animations: property <scale> from neutral to [1.5 1\] at (0.75) should be [1.4 1\]] [CSS Animations: property <scale> from neutral to [1.5 1\] at (0.75) should be [1.4 1\]]
expected: FAIL expected: FAIL
@ -629,9 +581,6 @@
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (-1) should be [3 0\]] [CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (-1) should be [3 0\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [initial\] to [inherit\] at (0) should be [1\]]
expected: FAIL
[Web Animations: property <scale> from [inherit\] to [initial\] at (0) should be [0.5 1 2\]] [Web Animations: property <scale> from [inherit\] to [initial\] at (0) should be [0.5 1 2\]]
expected: FAIL expected: FAIL
@ -698,9 +647,6 @@
[Web Animations: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]] [Web Animations: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [-10 5 1\] to [1\] at (0) should be [-10 5\]]
expected: FAIL
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]] [Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
expected: FAIL expected: FAIL
@ -752,9 +698,6 @@
[Web Animations: property <scale> from [unset\] to [1.5 1\] at (1) should be [1.5 1\]] [Web Animations: property <scale> from [unset\] to [1.5 1\] at (1) should be [1.5 1\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
expected: FAIL
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0.25) should be [1.625 0.625 1.25\]] [Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0.25) should be [1.625 0.625 1.25\]]
expected: FAIL expected: FAIL
@ -791,9 +734,6 @@
[CSS Animations: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]] [CSS Animations: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [1\] to [10 -5 0\] at (0) should be [1\]]
expected: FAIL
[CSS Transitions: property <scale> from [26 17 9\] to [2 1\] at (2) should be [-22 -15 -7\]] [CSS Transitions: property <scale> from [26 17 9\] to [2 1\] at (2) should be [-22 -15 -7\]]
expected: FAIL expected: FAIL
@ -809,9 +749,6 @@
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]] [CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
expected: FAIL
[CSS Animations: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]] [CSS Animations: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]]
expected: FAIL expected: FAIL
@ -827,9 +764,6 @@
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]] [CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [26 17 9\] to [2 1\] at (1) should be [2 1\]]
expected: FAIL
[CSS Animations: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]] [CSS Animations: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]]
expected: FAIL expected: FAIL
@ -908,9 +842,6 @@
[CSS Animations: property <scale> from neutral to [1.5 1\] at (1) should be [1.5 1\]] [CSS Animations: property <scale> from neutral to [1.5 1\] at (1) should be [1.5 1\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
expected: FAIL
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (0.25) should be [1.75 0.6251\]] [CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (0.25) should be [1.75 0.6251\]]
expected: FAIL expected: FAIL
@ -944,9 +875,6 @@
[CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]] [CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [inherit\] to [2 0.5 1\] at (1) should be [2 0.5\]]
expected: FAIL
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.75) should be [1.625 0.625 1.25\]] [CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.75) should be [1.625 0.625 1.25\]]
expected: FAIL expected: FAIL
@ -977,9 +905,6 @@
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (-1) should be [1.5 1 0\]] [CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (-1) should be [1.5 1 0\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (0) should be [2 0.5\]]
expected: FAIL
[CSS Animations: property <scale> from neutral to [1.5 1\] at (-1) should be [0.7 1\]] [CSS Animations: property <scale> from neutral to [1.5 1\] at (-1) should be [0.7 1\]]
expected: FAIL expected: FAIL
@ -1001,9 +926,6 @@
[CSS Transitions: property <scale> from [1\] to [10 -5 0\] at (0.75) should be [7.75 -3.5 0.25\]] [CSS Transitions: property <scale> from [1\] to [10 -5 0\] at (0.75) should be [7.75 -3.5 0.25\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <scale> from [initial\] to [2 0.5 1\] at (0) should be [1\]]
expected: FAIL
[CSS Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]] [CSS Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]]
expected: FAIL expected: FAIL

View file

@ -74,15 +74,6 @@
[translate interpolation] [translate interpolation]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [480px 400px 320px\] to [240% 160%\] at (1) should be [240% 160%\]]
expected: FAIL
[CSS Transitions with transition: all: property <translate> from [200px 100px 400px\] to [initial\] at (1) should be [0px\]]
expected: FAIL
[CSS Transitions: property <translate> from [none\] to [8px 80% 800px\] at (0) should be [0px\]]
expected: FAIL
[CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]] [CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]]
expected: FAIL expected: FAIL
@ -125,9 +116,6 @@
[CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (-1) should be [400px 200px 800px\]] [CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (-1) should be [400px 200px 800px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [-100px -50px 100px\] to [0px\] at (1) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]] [CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]]
expected: FAIL expected: FAIL
@ -161,9 +149,6 @@
[CSS Transitions with transition: all: property <translate> from [unset\] to [20px\] at (0.75) should be [15px\]] [CSS Transitions with transition: all: property <translate> from [unset\] to [20px\] at (0.75) should be [15px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [480px 400px 320px\] to [240% 160%\] at (1) should be [240% 160%\]]
expected: FAIL
[CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0) should be [200px 100px 400px\]] [CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0) should be [200px 100px 400px\]]
expected: FAIL expected: FAIL
@ -173,15 +158,9 @@
[Web Animations: property <translate> from [inherit\] to [initial\] at (0) should be [100px 200px 300px\]] [Web Animations: property <translate> from [inherit\] to [initial\] at (0) should be [100px 200px 300px\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [0px\] to [-100px -50px 100px\] at (0) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from [-100%\] to [100%\] at (0) should be [-100%\]] [CSS Animations: property <translate> from [-100%\] to [100%\] at (0) should be [-100%\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [inherit\] to [initial\] at (1) should be [0px\]]
expected: FAIL
[Web Animations: property <translate> from [-100px -50px\] to [100px 50px\] at (0.25) should be [-50px -25px\]] [Web Animations: property <translate> from [-100px -50px\] to [100px 50px\] at (0.25) should be [-50px -25px\]]
expected: FAIL expected: FAIL
@ -245,9 +224,6 @@
[CSS Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (0.125) should be [230px 260px 290px\]] [CSS Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (0.125) should be [230px 260px 290px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [initial\] to [200px 100px 200px\] at (0) should be [0px\]]
expected: FAIL
[Web Animations: property <translate> from [-100px\] to [100px\] at (-1) should be [-300px\]] [Web Animations: property <translate> from [-100px\] to [100px\] at (-1) should be [-300px\]]
expected: FAIL expected: FAIL
@ -341,9 +317,6 @@
[CSS Transitions: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]] [CSS Transitions: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [initial\] to [inherit\] at (0) should be [0px\]]
expected: FAIL
[Web Animations: property <translate> from [none\] to [8px 80% 800px\] at (1) should be [8px 80% 800px\]] [Web Animations: property <translate> from [none\] to [8px 80% 800px\] at (1) should be [8px 80% 800px\]]
expected: FAIL expected: FAIL
@ -437,9 +410,6 @@
[Web Animations: property <translate> from [-100px -50px 100px\] to [0px\] at (-1) should be [-200px -100px 200px\]] [Web Animations: property <translate> from [-100px -50px 100px\] to [0px\] at (-1) should be [-200px -100px 200px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [0px\] to [-100px -50px 100px\] at (0) should be [0px\]]
expected: FAIL
[CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (0.75) should be [50px 25px\]] [CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (0.75) should be [50px 25px\]]
expected: FAIL expected: FAIL
@ -527,9 +497,6 @@
[CSS Animations: property <translate> from neutral to [20px\] at (2) should be [30px\]] [CSS Animations: property <translate> from neutral to [20px\] at (2) should be [30px\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [-100px -50px 100px\] to [0px\] at (1) should be [0px\]]
expected: FAIL
[CSS Transitions: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]] [CSS Transitions: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]]
expected: FAIL expected: FAIL
@ -635,9 +602,6 @@
[Web Animations: property <translate> from [inherit\] to [initial\] at (-1) should be [200px 400px 600px\]] [Web Animations: property <translate> from [inherit\] to [initial\] at (-1) should be [200px 400px 600px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [none\] to [8px 80% 800px\] at (0) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from [0px\] to [-100px -50px 100px\] at (0.75) should be [-75px -37.5px 75px\]] [CSS Animations: property <translate> from [0px\] to [-100px -50px 100px\] at (0.75) should be [-75px -37.5px 75px\]]
expected: FAIL expected: FAIL
@ -764,9 +728,6 @@
[Web Animations: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.875) should be [calc(210% + 60px) calc(140% + 50px) 40px\]] [Web Animations: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.875) should be [calc(210% + 60px) calc(140% + 50px) 40px\]]
expected: FAIL expected: FAIL
[CSS Transitions with transition: all: property <translate> from [inherit\] to [initial\] at (1) should be [0px\]]
expected: FAIL
[CSS Transitions: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.125) should be [calc(420px + 30%) calc(350px + 20%) 280px\]] [CSS Transitions: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.125) should be [calc(420px + 30%) calc(350px + 20%) 280px\]]
expected: FAIL expected: FAIL
@ -788,9 +749,6 @@
[CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (0.75) should be [125px 175px 275px\]] [CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (0.75) should be [125px 175px 275px\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [initial\] to [200px 100px 200px\] at (0) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from [none\] to [8px 80% 800px\] at (0.125) should be [1px 10% 100px\]] [CSS Animations: property <translate> from [none\] to [8px 80% 800px\] at (0.125) should be [1px 10% 100px\]]
expected: FAIL expected: FAIL
@ -1037,9 +995,6 @@
[CSS Transitions with transition: all: property <translate> from [none\] to [8px 80% 800px\] at (2) should be [16px 160% 1600px\]] [CSS Transitions with transition: all: property <translate> from [none\] to [8px 80% 800px\] at (2) should be [16px 160% 1600px\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [initial\] to [inherit\] at (0) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from neutral to [20px\] at (0.75) should be [17.5px\]] [CSS Animations: property <translate> from neutral to [20px\] at (0.75) should be [17.5px\]]
expected: FAIL expected: FAIL
@ -1064,9 +1019,6 @@
[Web Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (1) should be [300px 400px 500px\]] [Web Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (1) should be [300px 400px 500px\]]
expected: FAIL expected: FAIL
[CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (1) should be [0px\]]
expected: FAIL
[CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0.25) should be [150px 75px 300px\]] [CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0.25) should be [150px 75px 300px\]]
expected: FAIL expected: FAIL

View file

@ -1,7 +0,0 @@
[scale-parsing-valid.html]
[e.style['scale'\] = "100 200 1" should set the property value]
expected: FAIL
[e.style['scale'\] = "100 100 1" should set the property value]
expected: FAIL

View file

@ -8,12 +8,3 @@
[e.style['translate'\] = "100px calc(10px - 10%)" should set the property value] [e.style['translate'\] = "100px calc(10px - 10%)" should set the property value]
expected: FAIL expected: FAIL
[e.style['translate'\] = "100px 200px 0px" should set the property value]
expected: FAIL
[e.style['translate'\] = "100px 0px 0px" should set the property value]
expected: FAIL
[e.style['translate'\] = "1px 2px 0" should set the property value]
expected: FAIL

View file

@ -0,0 +1,2 @@
[transform-scale-percent-001.html]
expected: FAIL