diff options
76 files changed, 1329 insertions, 1044 deletions
diff --git a/Cargo.lock b/Cargo.lock index f6daa13a696..531fa815666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5333,7 +5333,7 @@ name = "style" version = "0.0.1" dependencies = [ "app_units", - "arrayvec 0.4.6", + "arrayvec 0.5.1", "atomic_refcell", "bindgen", "bitflags", @@ -5941,11 +5941,11 @@ checksum = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" [[package]] name = "uluru" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2606e9192f308ddc4f0b3c5d1bf3400e28a70fff956e9d9f46d23b094746d9f" +checksum = "6d7b39d0c32eba57d52d334e4bdd150df6e755264eefaa1ae2e7cd125f35e1ca" dependencies = [ - "arrayvec 0.4.6", + "arrayvec 0.5.1", ] [[package]] diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 6c52fa07d1d..daa07fab430 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -22,7 +22,7 @@ bitflags = "1.0" byteorder = "1" canvas_traits = {path = "../canvas_traits"} crossbeam-channel = "0.3" -cssparser = "0.27.1" +cssparser = "0.27" embedder_traits = {path = "../embedder_traits"} euclid = "0.20" fnv = "1.0" diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml index b4dae5e2017..7b412d993fa 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -14,7 +14,7 @@ path = "lib.rs" webgl_backtrace = [] [dependencies] -cssparser = "0.27.1" +cssparser = "0.27" euclid = "0.20" ipc-channel = "0.12" lazy_static = "1" diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 5d815517532..466d844418a 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -514,6 +514,10 @@ impl<'le> TElement for ServoLayoutElement<'le> { false } + fn exports_any_part(&self) -> bool { + false + } + fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> { unsafe { (*self.element.style_attribute()) @@ -999,6 +1003,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { false } + fn exported_part(&self, _: &Atom) -> Option<Atom> { + None + } + + fn imported_part(&self, _: &Atom) -> Option<Atom> { + None + } + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { unsafe { self.element.has_class_for_layout(name, case_sensitivity) } @@ -1533,6 +1545,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { 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 { debug!("ServoThreadSafeLayoutElement::has_class called"); false diff --git a/components/layout_thread_2020/dom_wrapper.rs b/components/layout_thread_2020/dom_wrapper.rs index a9f09e11073..520014837db 100644 --- a/components/layout_thread_2020/dom_wrapper.rs +++ b/components/layout_thread_2020/dom_wrapper.rs @@ -521,6 +521,10 @@ impl<'le> TElement for ServoLayoutElement<'le> { false } + fn exports_any_part(&self) -> bool { + false + } + fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> { unsafe { (*self.element.style_attribute()) @@ -1006,6 +1010,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { false } + fn exported_part(&self, _: &Atom) -> Option<Atom> { + None + } + + fn imported_part(&self, _: &Atom) -> Option<Atom> { + None + } + #[inline] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { unsafe { self.element.has_class_for_layout(name, case_sensitivity) } @@ -1540,6 +1552,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { 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 { debug!("ServoThreadSafeLayoutElement::has_class called"); false diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml index 9ba39c1e0d6..2e112805067 100644 --- a/components/malloc_size_of/Cargo.toml +++ b/components/malloc_size_of/Cargo.toml @@ -28,7 +28,7 @@ servo = [ app_units = "0.7" content-security-policy = {version = "0.3.0", features = ["serde"], optional = true} crossbeam-channel = { version = "0.3", optional = true } -cssparser = "0.27.1" +cssparser = "0.27" euclid = "0.20" hashglobe = { path = "../hashglobe" } hyper = { version = "0.12", optional = true } diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d7032f290e0..cfa02f3363e 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -43,7 +43,7 @@ content-security-policy = {version = "0.3.0", features = ["serde"]} cookie = "0.11" chrono = "0.4" crossbeam-channel = "0.3" -cssparser = "0.27.1" +cssparser = "0.27" deny_public_fields = {path = "../deny_public_fields"} devtools_traits = {path = "../devtools_traits"} dom_struct = {path = "../dom_struct"} diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index baf4fcd0878..f359c43a18f 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -3073,6 +3073,14 @@ impl<'a> SelectorsElement for DomRoot<Element> { 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 { Element::has_class(&**self, name, case_sensitivity) } diff --git a/components/selectors/Cargo.toml b/components/selectors/Cargo.toml index d3dda6cb2c1..d32e82a2336 100644 --- a/components/selectors/Cargo.toml +++ b/components/selectors/Cargo.toml @@ -22,7 +22,7 @@ bench = [] [dependencies] bitflags = "1.0" matches = "0.1" -cssparser = "0.27.1" +cssparser = "0.27" derive_more = "0.13" log = "0.4" fxhash = "0.2" diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 49b7f3dbd32..8993a5ea003 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -8,6 +8,7 @@ use crate::nth_index_cache::NthIndexCacheInner; use crate::parser::{AncestorHashes, Combinator, Component, LocalName}; use crate::parser::{NonTSPseudoClass, Selector, SelectorImpl, SelectorIter, SelectorList}; use crate::tree::Element; +use smallvec::SmallVec; use std::borrow::Borrow; use std::iter; @@ -667,7 +668,41 @@ where match *selector { 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) => { // <slots> are never flattened tree slottables. !element.is_html_slot_element() && diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index f892abd4e94..d6198c5a5f5 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -117,6 +117,20 @@ pub trait Element: Sized + Clone + Debug { case_sensitivity: CaseSensitivity, ) -> 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; /// Returns whether this element matches `:empty`. diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index ad4489c10bc..bd6b5289504 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -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 /// a static reference. #[inline] @@ -501,10 +509,7 @@ impl<T: ?Sized> Drop for Arc<T> { fn drop(&mut self) { // NOTE(emilio): If you change anything here, make sure that the // implementation in layout/style/ServoStyleConstsInlines.h matches! - // - // 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 { + if self.is_static() { return; } diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index f15c64e2f56..47a0d4392ef 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -18,8 +18,9 @@ doctest = false [features] gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"] servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever", - "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union", - "servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"] + "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url", + "string_cache", "crossbeam-channel", "to_shmem/servo", + "servo_arc/servo"] servo-layout-2013 = [] servo-layout-2020 = [] gecko_debug = [] @@ -28,11 +29,11 @@ gecko_profiler = [] [dependencies] app_units = "0.7" -arrayvec = "0.4.6" +arrayvec = "0.5" atomic_refcell = "0.1" bitflags = "1.0" byteorder = "1.0" -cssparser = "0.27.1" +cssparser = "0.27" crossbeam-channel = { version = "0.3", optional = true } derive_more = "0.13" new_debug_unreachable = "1.0" @@ -74,7 +75,7 @@ thin-slice = "0.1.0" to_shmem = {path = "../to_shmem"} to_shmem_derive = {path = "../to_shmem_derive"} time = "0.1" -uluru = "0.3" +uluru = "0.4" unicode-bidi = "0.3" unicode-segmentation = "1.0" void = "1.0.2" diff --git a/components/style/animation.rs b/components/style/animation.rs index c9b4fc5a6f1..7f96e55b18f 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -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_play_state::computed_value::single_value::T as AnimationPlayState; use crate::properties::{self, CascadeMode, ComputedValues, LonghandId}; -use crate::rule_tree::CascadeLevel; use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; +use crate::stylesheets::Origin; use crate::timer::Timer; use crate::values::computed::box_::TransitionProperty; use crate::values::computed::Time; @@ -491,7 +491,7 @@ where guard .normal_declaration_iter() .filter(|declaration| declaration.is_animatable()) - .map(|decl| (decl, CascadeLevel::Animations)) + .map(|decl| (decl, Origin::Author)) }; // This currently ignores visited styles, which seems acceptable, diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs index e4bf996a1d6..6353fb4c5e5 100644 --- a/components/style/applicable_declarations.rs +++ b/components/style/applicable_declarations.rs @@ -5,11 +5,10 @@ //! Applicable declarations management. use crate::properties::PropertyDeclarationBlock; -use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder, StyleSource}; +use crate::rule_tree::{CascadeLevel, StyleSource}; use crate::shared_lock::Locked; use servo_arc::Arc; use smallvec::SmallVec; -use std::fmt::{self, Debug}; /// List of applicable declarations. This is a transient structure that shuttles /// 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_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT; -/// We store up-to-15 shadow order levels. -/// -/// You'd need an element slotted across 16 components with ::slotted rules to -/// 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; +/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy +/// for the different trade-offs there. +const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS; /// Stores the source order of a block, the cascade level it belongs to, and the /// 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); impl ApplicableDeclarationBits { - fn new( - source_order: u32, - cascade_level: CascadeLevel, - shadow_cascade_order: ShadowCascadeOrder, - ) -> 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 new(source_order: u32, cascade_level: CascadeLevel) -> Self { + Self( + (source_order & SOURCE_ORDER_MASK) | + ((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT), + ) } fn source_order(&self) -> u32 { - (self.0 & SOURCE_ORDER_MASK) >> SOURCE_ORDER_SHIFT - } - - fn shadow_cascade_order(&self) -> ShadowCascadeOrder { - ((self.0 & SHADOW_CASCADE_ORDER_MASK) >> SHADOW_CASCADE_ORDER_SHIFT) as ShadowCascadeOrder + self.0 & SOURCE_ORDER_MASK } fn level(&self) -> CascadeLevel { - let byte = ((self.0 & CASCADE_LEVEL_MASK) >> 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() + CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8) } } @@ -119,23 +83,17 @@ impl ApplicableDeclarationBlock { ) -> Self { ApplicableDeclarationBlock { source: StyleSource::from_declarations(declarations), - bits: ApplicableDeclarationBits::new(0, level, 0), + bits: ApplicableDeclarationBits::new(0, level), specificity: 0, } } /// Constructs an applicable declaration block from the given components #[inline] - pub fn new( - source: StyleSource, - order: u32, - level: CascadeLevel, - specificity: u32, - shadow_cascade_order: ShadowCascadeOrder, - ) -> Self { + pub fn new(source: StyleSource, order: u32, level: CascadeLevel, specificity: u32) -> Self { ApplicableDeclarationBlock { source, - bits: ApplicableDeclarationBits::new(order, level, shadow_cascade_order), + bits: ApplicableDeclarationBits::new(order, level), specificity, } } @@ -155,9 +113,8 @@ impl ApplicableDeclarationBlock { /// Convenience method to consume self and return the right thing for the /// rule tree to iterate over. #[inline] - pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel, ShadowCascadeOrder) { + pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) { let level = self.level(); - let cascade_order = self.bits.shadow_cascade_order(); - (self.source, level, cascade_order) + (self.source, level) } } diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index 639d32a97a9..3717e1f0614 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -306,8 +306,7 @@ fn parse_declaration_value_block<'i, 't>( ) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { let mut token_start = input.position(); let mut token = match input.next_including_whitespace_and_comments() { - // FIXME: remove clone() when borrows are non-lexical - Ok(token) => token.clone(), + Ok(token) => token, Err(_) => { return Ok(( 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(_) => { + let serialization_type = token.serialization_type(); let token_slice = input.slice_from(token_start); 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) => { - let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u); + Token::BadUrl(ref u) => { + let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone()); return Err(input.new_custom_error(e)); }, - Token::BadString(s) => { - let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s); + Token::BadString(ref s) => { + let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone()); return Err(input.new_custom_error(e)); }, Token::CloseParenthesis => { @@ -401,13 +401,14 @@ fn parse_declaration_value_block<'i, 't>( Token::CloseSquareBracket.serialization_type() }, Token::QuotedString(_) => { + let serialization_type = token.serialization_type(); let token_slice = input.slice_from(token_start); let quote = &token_slice[..1]; debug_assert!(matches!(quote, "\"" | "'")); if !(token_slice.ends_with(quote) && token_slice.len() > 1) { missing_closing_characters.push_str(quote) } - token.serialization_type() + serialization_type }, Token::Ident(ref value) | Token::AtKeyword(ref value) | @@ -417,6 +418,8 @@ fn parse_declaration_value_block<'i, 't>( Token::Dimension { unit: ref value, .. } => { + let serialization_type = token.serialization_type(); + let is_unquoted_url = matches!(token, Token::UnquotedUrl(_)); if value.ends_with("�") && input.slice_from(token_start).ends_with("\\") { // Unescaped backslash at EOF in these contexts is interpreted as U+FFFD // 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.) missing_closing_characters.push_str("�") } - if matches!(token, Token::UnquotedUrl(_)) { + if is_unquoted_url { check_closed!(")"); } - token.serialization_type() + serialization_type }, _ => token.serialization_type(), }; token_start = input.position(); token = match input.next_including_whitespace_and_comments() { - // FIXME: remove clone() when borrows are non-lexical - Ok(token) => token.clone(), + Ok(token) => token, 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; loop { let before_this_token = input.position(); - // FIXME: remove clone() when borrows are non-lexical - let next = input - .next_including_whitespace_and_comments() - .map(|t| t.clone()); + let next = input.next_including_whitespace_and_comments(); if set_position_at_next_iteration { *position = ( before_this_token, match next { - Ok(ref token) => token.serialization_type(), + Ok(token) => token.serialization_type(), Err(_) => TokenSerializationType::nothing(), }, ); diff --git a/components/style/dom.rs b/components/style/dom.rs index 2ea64bbb3dc..fbe35e2e49d 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -331,7 +331,7 @@ where } /// The ShadowRoot trait. -pub trait TShadowRoot: Sized + Copy + Clone + PartialEq { +pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq { /// The concrete node type. type ConcreteNode: TNode<ConcreteShadowRoot = Self>; @@ -523,6 +523,9 @@ pub trait TElement: /// Returns whether this element has a `part` attribute. 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. fn id(&self) -> Option<&WeakAtom>; diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs index e43c861271e..8f764dc8958 100644 --- a/components/style/dom_apis.rs +++ b/components/style/dom_apis.rs @@ -172,7 +172,11 @@ where }; 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 diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index bb126e16948..3463c1f95e9 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -237,7 +237,13 @@ impl Device { /// used for viewport unit resolution. pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> { 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. @@ -268,13 +274,7 @@ impl Device { if doc.mIsBeingUsedAsImage() { return true; } - let document_color_use = static_prefs::pref!("browser.display.document_color_use"); - let prefs = self.pref_sheet_prefs(); - match document_color_use { - 1 => true, - 2 => prefs.mIsChrome, - _ => !prefs.mUseAccessibilityTheme, - } + self.pref_sheet_prefs().mUseDocumentColors } /// Returns the default background color. diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index aa849ceee02..8d05dd9bbb1 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -140,6 +140,12 @@ impl PseudoElement { *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. #[inline] pub fn is_lazy(&self) -> bool { diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py index 0066d06d054..cf7cc77c16f 100755 --- a/components/style/gecko/regen_atoms.py +++ b/components/style/gecko/regen_atoms.py @@ -132,7 +132,7 @@ PRELUDE = ''' RULE_TEMPLATE = ''' ("{atom}") => {{{{ #[allow(unsafe_code)] #[allow(unused_unsafe)] - unsafe {{ $crate::string_cache::Atom::from_index({index}) }} + unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }} }}}}; '''[1:] diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index cffb78d3e9e..02707682b4d 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -194,6 +194,16 @@ impl ElementSnapshot for GeckoElementSnapshot { } #[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] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { if !self.has_any(Flags::MaybeClass) { return false; diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index b8b31bc87dd..cb3056e7bd5 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -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"))?) }) } +#[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, /// returns whether the attribute has that name. #[inline(always)] diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index c2c4a0c0feb..bf4da090646 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -144,6 +144,13 @@ impl<'ld> TDocument for GeckoDocument<'ld> { #[derive(Clone, Copy)] 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> { #[inline] 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); } - if self.is_shadow_root() { - return write!(f, "<shadow-root> ({:#x})", self.opaque().0); + if let Some(sr) = self.as_shadow_root() { + return sr.fmt(f); } 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 { use crate::gecko_bindings::structs::*; 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; } @@ -758,13 +768,6 @@ impl<'le> GeckoElement<'le> { 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. #[inline] 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; } - #[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. #[inline] 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 // true if we used to (but no longer) have anonymous content from // ::before/::after, or nsIAnonymousContentCreators. - if self.is_in_anonymous_subtree() || + if self.is_in_native_anonymous_subtree() || self.is_html_slot_element() || self.shadow_root().is_some() || self.may_have_anonymous_children() @@ -1250,6 +1248,11 @@ impl<'le> TElement for GeckoElement<'le> { .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. #[inline] fn id(&self) -> Option<&WeakAtom> { @@ -2192,7 +2195,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn is_link(&self) -> bool { self.state() - .intersects(NonTSPseudoClass::AnyLink.state_flag()) + .intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE) } #[inline] @@ -2219,6 +2222,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { 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)] fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { let attr = match self.get_class_attr() { @@ -2241,7 +2254,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn ignores_nth_child_selectors(&self) -> bool { - self.is_root_of_anonymous_subtree() + self.is_root_of_native_anonymous_subtree() } } diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs index b209f316d09..b9e3a0c608a 100644 --- a/components/style/gecko_string_cache/mod.rs +++ b/components/style/gecko_string_cache/mod.rs @@ -26,6 +26,7 @@ use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::iter::Cloned; use std::mem::{self, ManuallyDrop}; +use std::num::NonZeroUsize; use std::ops::Deref; use std::{slice, str}; 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)] #[repr(C)] -pub struct Atom(usize); +pub struct Atom(NonZeroUsize); /// 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 { unsafe { 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 in_range = addr >= start as usize && addr < end as usize; let aligned = addr % mem::align_of::<nsStaticAtom>() == 0; @@ -101,9 +107,9 @@ impl Deref for Atom { fn deref(&self) -> &WeakAtom { unsafe { let addr = if self.is_static() { - (&gGkAtoms as *const _ as usize) + (self.0 >> 1) + (&gGkAtoms as *const _ as usize) + (self.0.get() >> 1) } else { - self.0 + self.0.get() }; debug_assert!(!self.is_static() || valid_static_atom_addr(addr)); WeakAtom::new(addr as *const nsAtom) @@ -341,29 +347,29 @@ impl fmt::Display for WeakAtom { } #[inline] -unsafe fn make_handle(ptr: *const nsAtom) -> usize { +unsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize { debug_assert!(!ptr.is_null()); if !WeakAtom::new(ptr).is_static() { - ptr as usize + NonZeroUsize::new_unchecked(ptr as usize) } else { make_static_handle(ptr as *mut nsStaticAtom) } } #[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. // https://github.com/rust-lang/rust/issues/41079 debug_assert!(valid_static_atom_addr(ptr as usize)); let base = &gGkAtoms as *const _; let offset = ptr as usize - base as usize; - (offset << 1) | 1 + NonZeroUsize::new_unchecked((offset << 1) | 1) } impl Atom { #[inline] fn is_static(&self) -> bool { - self.0 & 1 == 1 + self.0.get() & 1 == 1 } /// 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 - /// checking in release builds. + /// checking. #[inline] - pub unsafe fn from_index(index: u16) -> Self { - let ptr = static_atoms().get_unchecked(index as usize) as *const _; - let handle = make_static_handle(ptr); - let atom = Atom(handle); - debug_assert!(valid_static_atom_addr(ptr as usize)); - debug_assert!(atom.is_static()); - debug_assert!((*atom).is_static()); - debug_assert!(handle == make_handle(atom.as_ptr())); - atom + pub const unsafe fn from_index_unchecked(index: u16) -> Self { + // FIXME(emilio): No support for debug_assert! in const fn for now. Note + // that violating this invariant will debug-assert in the `Deref` impl + // though. + // + // debug_assert!((index as usize) < STATIC_ATOM_COUNT); + let offset = + (index as usize) * std::mem::size_of::<nsStaticAtom>() + kGkAtomsArrayOffset as usize; + Atom(NonZeroUsize::new_unchecked((offset << 1) | 1)) } /// Creates an atom from an atom pointer. diff --git a/components/style/invalidation/element/document_state.rs b/components/style/invalidation/element/document_state.rs index 4e29e91eb0e..7fc51338b1e 100644 --- a/components/style/invalidation/element/document_state.rs +++ b/components/style/invalidation/element/document_state.rs @@ -79,7 +79,13 @@ where 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, + )); } } diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index cb63e611348..bc74527bf16 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -62,6 +62,12 @@ pub trait ElementSnapshot: Sized { /// called if `has_attrs()` returns true. 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 /// only be called if `has_attrs()` returns true. fn each_class<F>(&self, _: F) @@ -270,7 +276,10 @@ where } 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 { @@ -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 { match self.snapshot() { Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity), diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 3487d7b9353..e094be281b4 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -11,6 +11,7 @@ use crate::selector_parser::SelectorImpl; use selectors::matching::matches_compound_selector_from; use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext}; use selectors::parser::{Combinator, Component, Selector}; +use selectors::OpaqueElement; use smallvec::SmallVec; use std::fmt; @@ -127,6 +128,12 @@ enum InvalidationKind { #[derive(Clone)] pub struct Invalidation<'a> { 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. /// /// 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> { /// 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 { selector, + scope, offset, matched_by_any_previous: false, } @@ -471,6 +483,47 @@ where any_descendant } + fn invalidate_parts_in_shadow_tree( + &mut self, + shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot, + invalidations: &[Invalidation<'b>], + ) -> bool { + debug_assert!(!invalidations.is_empty()); + + let mut any = false; + let mut sibling_invalidations = InvalidationVector::new(); + + 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( + element, + invalidations, + &mut sibling_invalidations, + DescendantInvalidationKind::Part, + ); + debug_assert!( + sibling_invalidations.is_empty(), + "::part() shouldn't have sibling combinators to the right, \ + this makes no sense! {:?}", + sibling_invalidations + ); + } + + if let Some(shadow) = element.shadow_root() { + if element.exports_any_part() { + any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations) + } + } + } + + any + } + fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool { if invalidations.is_empty() { return false; @@ -481,23 +534,7 @@ where None => return false, }; - let mut any = false; - let mut sibling_invalidations = InvalidationVector::new(); - for element in shadow.parts() { - any |= self.invalidate_child( - *element, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Part, - ); - debug_assert!( - sibling_invalidations.is_empty(), - "::part() shouldn't have sibling combinators to the right, \ - this makes no sense! {:?}", - sibling_invalidations - ); - } - any + self.invalidate_parts_in_shadow_tree(shadow, invalidations) } fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool { @@ -721,12 +758,17 @@ where self.element, invalidation, invalidation_kind ); - let matching_result = matches_compound_selector_from( - &invalidation.selector, - invalidation.offset, - self.processor.matching_context(), - &self.element, - ); + let matching_result = { + let context = self.processor.matching_context(); + context.current_host = invalidation.scope; + + matches_compound_selector_from( + &invalidation.selector, + invalidation.offset, + context, + &self.element, + ) + }; let mut invalidated_self = false; let mut matched = false; @@ -809,6 +851,7 @@ where let next_invalidation = Invalidation { selector: invalidation.selector, + scope: invalidation.scope, offset: next_combinator_offset + 1, matched_by_any_previous: false, }; diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index 30521fb8c00..e678413218c 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -158,7 +158,11 @@ where // 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 // 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"); // We can't just return here because there may also be attribute // changes as well that imply additional hints for siblings. @@ -453,6 +457,7 @@ where let invalidation = Invalidation::new( &dependency.selector, + self.matching_context.current_host.clone(), dependency.selector.len() - dependency.selector_offset + 1, ); diff --git a/components/style/matching.rs b/components/style/matching.rs index 958ec00f1d9..01246ed3878 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -137,12 +137,12 @@ trait PrivateMatchMethods: TElement { if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) { let style_attribute = self.style_attribute(); result |= replace_rule_node( - CascadeLevel::StyleAttributeNormal, + CascadeLevel::same_tree_author_normal(), style_attribute, primary_rules, ); result |= replace_rule_node( - CascadeLevel::StyleAttributeImportant, + CascadeLevel::same_tree_author_important(), style_attribute, primary_rules, ); diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 5266ed78356..a667cba09bc 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -15,7 +15,7 @@ use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword}; use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; use crate::properties::CASCADE_PROPERTY; use crate::rule_cache::{RuleCache, RuleCacheConditions}; -use crate::rule_tree::{CascadeLevel, StrongRuleNode}; +use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; use crate::stylesheets::{Origin, PerOrigin}; use servo_arc::Arc; @@ -134,11 +134,15 @@ where let restriction = pseudo.and_then(|p| p.property_restriction()); let iter_declarations = || { 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 guard = match origin { + Origin::Author => guards.author, + Origin::User | Origin::UserAgent => guards.ua_or_user, + }; let declarations = match node.style_source() { Some(source) => source - .read(cascade_level.guard(guards)) + .read(guard) .declaration_importance_iter(), None => DeclarationImportanceIterator::new(&[], &empty), }; @@ -153,14 +157,14 @@ where // longhands are only allowed if they have our // restriction flag set. 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; } } } if declaration_importance == node_importance { - Some((declaration, cascade_level)) + Some((declaration, origin)) } else { None } @@ -223,7 +227,7 @@ pub fn apply_declarations<'a, E, F, I>( where E: TElement, 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_eq!( @@ -242,17 +246,17 @@ where 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 mut builder = CustomPropertiesBuilder::new( inherited_style.custom_properties(), device.environment(), ); - for (declaration, cascade_level) in iter_declarations() { - declarations.push((declaration, cascade_level)); + for (declaration, origin) in iter_declarations() { + declarations.push((declaration, origin)); 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( device: &Device, longhand_id: LonghandId, - cascade_level: CascadeLevel, + origin: Origin, pseudo: Option<&PseudoElement>, declaration: &mut Cow<PropertyDeclaration>, ) -> bool { @@ -340,22 +344,15 @@ fn should_ignore_declaration_when_ignoring_document_colors( return false; } - let is_ua_or_user_rule = - matches!(cascade_level.origin(), Origin::User | Origin::UserAgent); + let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent); if is_ua_or_user_rule { return false; } - let is_style_attribute = matches!( - cascade_level, - CascadeLevel::StyleAttributeNormal | CascadeLevel::StyleAttributeImportant - ); - - // 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 { + // Don't override background-color on ::-moz-color-swatch. It is set as an + // author style (via the style attribute), but it's pretty important for it + // to show up for obvious reasons :) + if pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor { return false; } @@ -452,7 +449,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { declarations: I, ) where Phase: CascadePhase, - I: Iterator<Item = (&'decls PropertyDeclaration, CascadeLevel)>, + I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>, { 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(); - for (declaration, cascade_level) in declarations { + for (declaration, origin) in declarations { let declaration_id = declaration.id(); - let origin = cascade_level.origin(); let longhand_id = match declaration_id { PropertyDeclarationId::Longhand(id) => id, PropertyDeclarationId::Custom(..) => continue, @@ -515,7 +511,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { let should_ignore = should_ignore_declaration_when_ignoring_document_colors( self.context.builder.device, longhand_id, - cascade_level, + origin, self.context.builder.pseudo, &mut declaration, ); diff --git a/components/style/properties/data.py b/components/style/properties/data.py index d1876a5ea02..87ec87a7fa5 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -257,10 +257,12 @@ class Longhand(object): def type(): return "longhand" - # For a given logical property return all the physical - # property names corresponding to it. - def all_physical_mapped_properties(self): - assert self.logical + # For a given logical property return all the physical property names + # corresponding to it. + def all_physical_mapped_properties(self, data): + if not self.logical: + return [] + 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)] assert(len(candidates) == 1) @@ -270,7 +272,7 @@ class Longhand(object): else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \ else PHYSICAL_AXES if logical_side in LOGICAL_AXES \ 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] def experimental(self, engine): diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 7783c7eebd3..b6cd741038c 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -46,7 +46,7 @@ use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; use servo_arc::{Arc, RawOffsetArc, UniqueArc}; use std::marker::PhantomData; -use std::mem::{forget, zeroed, ManuallyDrop}; +use std::mem::{forget, MaybeUninit}; use std::{cmp, ops, ptr}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; use crate::values::computed::{Percentage, TransitionProperty}; @@ -218,7 +218,7 @@ impl ComputedValuesInner { unsafe { let mut arc = UniqueArc::<ComputedValues>::new_uninit(); bindings::Gecko_ComputedStyle_Init( - &mut (*arc.as_mut_ptr()).0, + arc.as_mut_ptr() as *mut _, &self, pseudo_ty, ); @@ -621,14 +621,17 @@ def set_gecko_property(ffi_name, expr): impl ${style_struct.gecko_struct_name} { #[allow(dead_code, unused_variables)] pub fn default(document: &structs::Document) -> Arc<Self> { - let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(unsafe { zeroed() }) }); 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}( - &mut *Arc::get_mut(&mut result).unwrap().gecko, + result.as_mut_ptr() as *mut _, document, ); + UniqueArc::assume_init(result).shareable() } - result } } 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} { fn clone(&self) -> Self { unsafe { - let mut result = ${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(zeroed()) }; - Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut *result.gecko, &*self.gecko); - result + let mut result = MaybeUninit::<Self>::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_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &*self.gecko); + result.assume_init() } } } diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs index 8db62bf56d6..2542ee5e376 100644 --- a/components/style/properties/longhands/inherited_box.mako.rs +++ b/components/style/properties/longhands/inherited_box.mako.rs @@ -51,6 +51,7 @@ ${helpers.single_keyword( "mixed upright sideways", engines="gecko", gecko_aliases="sideways-right=sideways", + gecko_enum_prefix="StyleTextOrientation", animation_value_type="none", spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation", )} diff --git a/components/style/properties/longhands/inherited_svg.mako.rs b/components/style/properties/longhands/inherited_svg.mako.rs index bbb7eba1c5b..1d75df08bd8 100644 --- a/components/style/properties/longhands/inherited_svg.mako.rs +++ b/components/style/properties/longhands/inherited_svg.mako.rs @@ -25,6 +25,7 @@ ${helpers.single_keyword( engines="gecko", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty", + gecko_enum_prefix="StyleTextAnchor", )} // Section 11 - Painting: Filling, Stroking and Marker Symbols @@ -80,6 +81,7 @@ ${helpers.single_keyword( engines="gecko", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty", + gecko_enum_prefix = "StyleShapeRendering", )} ${helpers.predefined_type( @@ -107,6 +109,7 @@ ${helpers.single_keyword( engines="gecko", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty", + gecko_enum_prefix = "StyleStrokeLinecap", )} ${helpers.single_keyword( diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index 1c2f5cc2e4a..95595f36316 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -331,6 +331,7 @@ ${helpers.single_keyword( engines="gecko", animation_value_type="discrete", spec="https://drafts.csswg.org/css-images/#propdef-object-fit", + gecko_enum_prefix = "StyleObjectFit", )} ${helpers.predefined_type( diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs index 64702855967..05d22abd9e9 100644 --- a/components/style/properties/longhands/ui.mako.rs +++ b/components/style/properties/longhands/ui.mako.rs @@ -15,6 +15,7 @@ ${helpers.single_keyword( "ime-mode", "auto normal active disabled inactive", engines="gecko", + gecko_enum_prefix="StyleImeMode", gecko_ffi_name="mIMEMode", animation_value_type="discrete", spec="https://drafts.csswg.org/css-ui/#input-method-editor", @@ -58,7 +59,7 @@ ${helpers.single_keyword( "none default menu tooltip sheet", engines="gecko", gecko_ffi_name="mWindowShadow", - gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW", + gecko_enum_prefix="StyleWindowShadow", animation_value_type="discrete", enabled_in="chrome", spec="None (Nonstandard internal property)", diff --git a/components/style/properties/longhands/xul.mako.rs b/components/style/properties/longhands/xul.mako.rs index 0862ce533d4..d5c4011eaf6 100644 --- a/components/style/properties/longhands/xul.mako.rs +++ b/components/style/properties/longhands/xul.mako.rs @@ -64,16 +64,6 @@ ${helpers.single_keyword( 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( "-moz-box-ordinal-group", "Integer", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 37b2319dfd6..0a26ae4555b 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1651,13 +1651,25 @@ impl UnparsedValue { shorthands::${shorthand.ident}::parse_value(&context, input) .map(|longhands| { match longhand_id { + <% seen = set() %> % for property in shorthand.sub_properties: - LonghandId::${property.camel_case} => { - PropertyDeclaration::${property.camel_case}( - longhands.${property.ident} - ) - } + // When animating logical properties, we end up + // 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} + ) + } + <% seen.add(prop.camel_case) %> + % endif % endfor + % endfor + <% del seen %> _ => unreachable!() } }) @@ -1801,8 +1813,8 @@ pub enum CountedUnknownProperty { } impl CountedUnknownProperty { - /// Parse the counted unknown property. - pub fn parse_for_test(property_name: &str) -> Option<Self> { + /// Parse the counted unknown property, for testing purposes only. + pub fn parse_for_testing(property_name: &str) -> Option<Self> { ascii_case_insensitive_phf_map! { unknown_id -> CountedUnknownProperty = { % for property in data.counted_unknown_properties: @@ -1814,6 +1826,7 @@ impl CountedUnknownProperty { } /// Returns the underlying index, used for use counter. + #[inline] pub fn bit(self) -> 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( property_name: &str, use_counters: Option< &UseCounters>, @@ -2176,14 +2196,12 @@ impl PropertyDeclaration { let mut ret = self.clone(); % for prop in data.longhands: - % if prop.logical: - % for physical_property in prop.all_physical_mapped_properties(): - % if data.longhands_by_name[physical_property].specified_type() != prop.specified_type(): + % for physical_property in prop.all_physical_mapped_properties(data): + % if physical_property.specified_type() != prop.specified_type(): <% raise "Logical property %s should share specified value with physical property %s" % \ - (prop.name, physical_property) %> + (prop.name, physical_property.name) %> % endif % endfor - % endif % endfor unsafe { diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs index 7b4ce131922..351b8cec606 100644 --- a/components/style/properties/shorthands/box.mako.rs +++ b/components/style/properties/shorthands/box.mako.rs @@ -464,11 +464,13 @@ ${helpers.two_properties_shorthand( }; // 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 - // either way, so maybe not? - Ok(if zoom.get() == 1.0 { + // [1]: https://bugs.webkit.org/show_bug.cgi?id=18467 + // [2]: https://bugzilla.mozilla.org/show_bug.cgi?id=1593009 + Ok(if zoom.get() == 1.0 || zoom.get() == 0.0 { expanded! { transform: Transform::none(), transform_origin: TransformOrigin::initial_value(), diff --git a/components/style/properties/shorthands/position.mako.rs b/components/style/properties/shorthands/position.mako.rs index 81a54dbfa57..d9cb9da3342 100644 --- a/components/style/properties/shorthands/position.mako.rs +++ b/components/style/properties/shorthands/position.mako.rs @@ -181,8 +181,18 @@ } 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 { 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(" / ")?; self.grid_${kind}_end.to_css(dest) } @@ -247,14 +257,39 @@ } 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 { 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]; - for value in &values { + for value in values.iter().take(trailing_values) { dest.write_str(" / ")?; value.to_css(dest)?; } - Ok(()) } } @@ -301,27 +336,37 @@ % endfor 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 values = vec![]; let mut line_names = vec![]; - let mut names = first_line_names; + line_names.push(first_line_names); + strings.push(string); loop { - line_names.push(names); - strings.push(string); let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default(); values.push(TrackListValue::TrackSize(size)); - names = input.try(parse_line_names).unwrap_or_default(); - if let Ok(v) = input.try(parse_line_names) { - let mut names_vec = names.into_vec(); - names_vec.extend(v.into_iter()); - names = names_vec.into(); - } - - string = match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) { - Ok(s) => s, - _ => { // only the named area determines whether we should bail out - line_names.push(names.into()); + let mut names = input.try(parse_line_names).unwrap_or_default(); + 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(); + names_vec.extend(v.into_iter()); + names = names_vec.into(); + } + line_names.push(names); + }, + Err(e) => { + if more_names.is_ok() { + // 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 }, }; @@ -350,7 +395,7 @@ value } else { - GenericGridTemplateComponent::None + GridTemplateComponent::default() }; Ok(( @@ -399,6 +444,9 @@ W: Write { match *template_areas { GridTemplateAreas::None => { + if template_rows.is_initial() && template_columns.is_initial() { + return GridTemplateComponent::default().to_css(dest); + } template_rows.to_css(dest)?; dest.write_str(" / ")?; template_columns.to_css(dest) @@ -455,8 +503,12 @@ } string.to_css(dest)?; - dest.write_str(" ")?; - value.to_css(dest)?; + + // If the track size is the initial value then it's redundant here. + if !value.is_initial() { + dest.write_str(" ")?; + value.to_css(dest)?; + } } if let Some(names) = names_iter.next() { @@ -503,8 +555,8 @@ context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Longhands, ParseError<'i>> { - let mut temp_rows = GridTemplateComponent::None; - let mut temp_cols = GridTemplateComponent::None; + let mut temp_rows = GridTemplateComponent::default(); + let mut temp_cols = GridTemplateComponent::default(); let mut temp_areas = GridTemplateAreas::None; let mut auto_rows = ImplicitGridTracks::default(); let mut auto_cols = ImplicitGridTracks::default(); @@ -577,8 +629,8 @@ impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { if *self.grid_template_areas != GridTemplateAreas::None || - (*self.grid_template_rows != GridTemplateComponent::None && - *self.grid_template_columns != GridTemplateComponent::None) || + (!self.grid_template_rows.is_initial() && + !self.grid_template_columns.is_initial()) || self.is_grid_template() { return super::grid_template::serialize_grid_template(self.grid_template_rows, self.grid_template_columns, @@ -588,7 +640,7 @@ if self.grid_auto_flow.autoflow == AutoFlow::Column { // It should fail to serialize if other branch of the if condition's values are set. if !self.grid_auto_rows.is_initial() || - *self.grid_template_columns != GridTemplateComponent::None { + !self.grid_template_columns.is_initial() { return Ok(()); } @@ -612,7 +664,7 @@ } else { // It should fail to serialize if other branch of the if condition's values are set. if !self.grid_auto_columns.is_initial() || - *self.grid_template_rows != GridTemplateComponent::None { + !self.grid_template_rows.is_initial() { return Ok(()); } diff --git a/components/style/rule_cache.rs b/components/style/rule_cache.rs index 117f63f9ea2..24039fb94f7 100644 --- a/components/style/rule_cache.rs +++ b/components/style/rule_cache.rs @@ -26,9 +26,7 @@ pub struct RuleCacheConditions { impl RuleCacheConditions { /// Sets the style as depending in the font-size value. pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) { - if let Some(f) = &self.font_size { - debug_assert_eq!(*f, font_size); - } + debug_assert!(self.font_size.map_or(true, |f| f == font_size)); self.font_size = Some(font_size); } diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index f536b80b5c5..ab1fb88d7f2 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -13,6 +13,7 @@ use crate::selector_parser::PseudoElement; use crate::shared_lock::Locked; use crate::stylesheets::Origin; use crate::stylist::{AuthorStylesEnabled, Rule, RuleInclusion, Stylist}; +use crate::Atom; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use servo_arc::ArcBorrow; use smallvec::SmallVec; @@ -79,7 +80,6 @@ where rules: &'a mut ApplicableDeclarationList, context: &'a mut MatchingContext<'b, E::Impl>, flags_setter: &'a mut F, - shadow_cascade_order: ShadowCascadeOrder, matches_user_and_author_rules: bool, matches_document_author_rules: bool, } @@ -132,7 +132,6 @@ where context, flags_setter, rules, - shadow_cascade_order: 0, matches_user_and_author_rules, matches_document_author_rules: matches_user_and_author_rules, } @@ -142,7 +141,7 @@ where let cascade_level = match origin { Origin::UserAgent => CascadeLevel::UANormal, 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); @@ -198,7 +197,6 @@ where ) { debug_assert!(shadow_host.shadow_root().is_some()); self.collect_rules_internal(Some(shadow_host), map, cascade_level); - self.shadow_cascade_order += 1; } #[inline] @@ -212,7 +210,6 @@ where let rule_hash_target = self.rule_hash_target; let rules = &mut self.rules; let flags_setter = &mut self.flags_setter; - let shadow_cascade_order = self.shadow_cascade_order; let start = rules.len(); self.context.with_shadow_host(shadow_host, |context| { map.get_all_matching_rules( @@ -222,16 +219,18 @@ where context, flags_setter, cascade_level, - shadow_cascade_order, ); }); sort_rules_from(rules, start); } - /// Collects the rules for the ::slotted pseudo-element. - fn collect_slotted_rules(&mut self) { + /// Collects the rules for the ::slotted pseudo-element and the :host + /// pseudo-class. + fn collect_host_and_slotted_rules(&mut self) { let mut slots = SmallVec::<[_; 3]>::new(); let mut current = self.rule_hash_target.assigned_slot(); + let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree(); + while let Some(slot) = current { debug_assert!( self.matches_user_and_author_rules, @@ -239,11 +238,16 @@ where ); slots.push(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 // come before the inner rules (and thus have less priority). for slot in slots.iter().rev() { + shadow_cascade_order.inc(); + let shadow = slot.containing_shadow().unwrap(); let data = match shadow.style_data() { Some(d) => d, @@ -253,10 +257,13 @@ where Some(r) => r, None => continue, }; + self.collect_rules_in_shadow_tree( shadow.host(), slotted_rules, - CascadeLevel::InnerShadowNormal, + CascadeLevel::AuthorNormal { + shadow_cascade_order, + }, ); } } @@ -277,12 +284,12 @@ where let cascade_data = containing_shadow.style_data(); let host = containing_shadow.host(); 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. - 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() { Some(s) => s, None => return, @@ -307,7 +314,9 @@ where self.collect_rules_in_shadow_tree( rule_hash_target, host_rules, - CascadeLevel::InnerShadowNormal, + CascadeLevel::AuthorNormal { + shadow_cascade_order, + }, ); } @@ -324,68 +333,89 @@ where return; } - let shadow = match self.rule_hash_target.containing_shadow() { + let mut inner_shadow = match self.rule_hash_target.containing_shadow() { Some(s) => s, None => return, }; - let host = shadow.host(); - let containing_shadow = host.containing_shadow(); - let part_rules = match containing_shadow { - Some(shadow) => shadow - .style_data() - .and_then(|data| data.part_rules(self.pseudo_element)), - None => self - .stylist - .cascade_data() - .borrow_for_origin(Origin::Author) - .part_rules(self.pseudo_element), - }; + let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree(); - // 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 { - let containing_host = containing_shadow.map(|s| s.host()); - let element = self.element; - let rule_hash_target = self.rule_hash_target; - let rules = &mut self.rules; - let flags_setter = &mut self.flags_setter; - let shadow_cascade_order = self.shadow_cascade_order; - let cascade_level = CascadeLevel::SameTreeAuthorNormal; - let start = rules.len(); - self.context.with_shadow_host(containing_host, |context| { - rule_hash_target.each_part(|p| { - if let Some(part_rules) = part_rules.get(p) { - SelectorMap::get_matching_rules( - element, - &part_rules, - rules, - context, - flags_setter, - cascade_level, - shadow_cascade_order, - ); + 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 + .style_data() + .and_then(|data| data.part_rules(self.pseudo_element)), + None => self + .stylist + .cascade_data() + .borrow_for_origin(Origin::Author) + .part_rules(self.pseudo_element), + }; + + if let Some(part_rules) = part_rules { + let containing_host = outer_shadow.map(|s| s.host()); + let element = self.element; + let rules = &mut self.rules; + let flags_setter = &mut self.flags_setter; + let cascade_level = CascadeLevel::AuthorNormal { + shadow_cascade_order, + }; + let start = rules.len(); + self.context.with_shadow_host(containing_host, |context| { + for p in &parts { + if let Some(part_rules) = part_rules.get(p) { + SelectorMap::get_matching_rules( + element, + &part_rules, + rules, + context, + flags_setter, + cascade_level, + ); + } } }); + 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 }); - sort_rules_from(rules, start); } } - fn collect_style_attribute_and_animation_rules(&mut self) { + fn collect_style_attribute(&mut self) { if let Some(sa) = self.style_attribute { self.rules .push(ApplicableDeclarationBlock::from_declarations( sa.clone_arc(), - CascadeLevel::StyleAttributeNormal, + CascadeLevel::same_tree_author_normal(), )); } + } + fn collect_animation_rules(&mut self) { if let Some(so) = self.smil_override { self.rules .push(ApplicableDeclarationBlock::from_declarations( @@ -431,11 +461,11 @@ where if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No { return; } - self.collect_host_rules(); - self.collect_slotted_rules(); + self.collect_host_and_slotted_rules(); self.collect_normal_rules_from_containing_shadow_tree(); self.collect_document_author_rules(); + self.collect_style_attribute(); self.collect_part_rules(); - self.collect_style_attribute_and_animation_rules(); + self.collect_animation_rules(); } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 02ebeed0452..7cb153de375 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -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. const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode; -/// A counter to track how many inner shadow roots rules deep we are. -/// -/// This is used to handle: +/// A counter to track how many shadow root rules deep we are. This is used to +/// handle: /// /// https://drafts.csswg.org/css-scoping/#shadow-cascading /// -/// In particular, it'd be `0` for the innermost shadow host, `1` for the next, -/// and so on. -pub type ShadowCascadeOrder = u8; +/// See the static functions for the meaning of different values. +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)] +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 { /// Construct a new rule tree. @@ -215,26 +260,20 @@ impl RuleTree { guards: &StylesheetGuards, ) -> StrongRuleNode where - I: Iterator<Item = (StyleSource, CascadeLevel, ShadowCascadeOrder)>, + I: Iterator<Item = (StyleSource, CascadeLevel)>, { use self::CascadeLevel::*; let mut current = self.root.clone(); - let mut last_level = current.get().level; let mut found_important = false; - let mut important_style_attr = None; - let mut important_same_tree = SmallVec::<[StyleSource; 4]>::new(); - let mut important_inner_shadow = SmallVec::<[SmallVec<[StyleSource; 4]>; 4]>::new(); - important_inner_shadow.push(SmallVec::new()); + let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new(); let mut important_user = SmallVec::<[StyleSource; 4]>::new(); let mut important_ua = SmallVec::<[StyleSource; 4]>::new(); let mut transition = None; - let mut last_cascade_order = 0; - for (source, level, shadow_cascade_order) in iter { - debug_assert!(level >= last_level, "Not really ordered"); + for (source, level) in iter { debug_assert!(!level.is_important(), "Important levels handled internally"); let any_important = { let pdb = source.read(level.guard(guards)); @@ -244,29 +283,13 @@ impl RuleTree { if any_important { found_important = true; match level { - InnerShadowNormal => { - debug_assert!( - shadow_cascade_order >= last_cascade_order, - "Not really ordered" - ); - 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()) + AuthorNormal { + shadow_cascade_order, + } => { + important_author.push((source.clone(), shadow_cascade_order)); }, - SameTreeAuthorNormal => important_same_tree.push(source.clone()), UANormal => important_ua.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 { current = current.ensure_child(self.root.downgrade(), source, level); } - last_level = level; } // Early-return in the common case of no !important declarations. @@ -298,23 +320,39 @@ impl RuleTree { return current; } - // // Insert important declarations, in order of increasing importance, // followed by any transition rule. // - - for source in important_same_tree.drain() { - current = current.ensure_child(self.root.downgrade(), source, SameTreeAuthorImportant); - } - - if let Some(source) = important_style_attr { - current = current.ensure_child(self.root.downgrade(), source, StyleAttributeImportant); + // Inner shadow wins over same-tree, which wins over outer-shadow. + // + // 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); } - for mut list in important_inner_shadow.drain().rev() { - for source in list.drain() { - current = current.ensure_child(self.root.downgrade(), source, InnerShadowImportant); - } + for (source, shadow_cascade_order) in important_author.drain() { + current = current.ensure_child( + self.root.downgrade(), + source, + AuthorImportant { + shadow_cascade_order: -shadow_cascade_order, + }, + ); } for source in important_user.drain() { @@ -359,11 +397,8 @@ impl RuleTree { I: Iterator<Item = (StyleSource, CascadeLevel)>, { let mut current = from; - let mut last_level = current.get().level; for (source, level) in iter { - debug_assert!(last_level <= level, "Not really ordered"); current = current.ensure_child(self.root.downgrade(), source, level); - last_level = level; } current } @@ -439,7 +474,6 @@ impl RuleTree { guards: &StylesheetGuards, important_rules_changed: &mut bool, ) -> Option<StrongRuleNode> { - debug_assert!(level.is_unique_per_element()); // TODO(emilio): Being smarter with lifetimes we could avoid a bit of // the refcount churn. let mut current = path.clone(); @@ -468,7 +502,11 @@ impl RuleTree { if current.get().level == level { *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 // 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 // in-place mutation of the source, which is, at best, fiddly, // so let's skip it for now. - let current_decls = current - .get() - .source - .as_ref() - .unwrap() - .as_declarations() - .expect("Replacing non-declarations style?"); - let is_here_already = ArcBorrow::ptr_eq(&pdb, ¤t_decls); + let is_here_already = ArcBorrow::ptr_eq(pdb, current_decls); if is_here_already { debug!("Picking the fast path in rule replacement"); return None; } } - current = current.parent().unwrap().clone(); + + if current_decls.is_some() { + 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. // @@ -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. /// -/// 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 /// [2]: https://drafts.csswg.org/css-cascade/#preshint /// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints /// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading #[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)] pub enum CascadeLevel { /// Normal User-Agent rules. - UANormal = 0, + UANormal, /// User normal rules. UserNormal, /// Presentational hints. PresHints, - /// Shadow DOM styles from "inner" shadow trees. - /// - /// See above for why this is needed instead of merging InnerShadowNormal, - /// SameTreeAuthorNormal and StyleAttributeNormal inside something like - /// AuthorNormal. - InnerShadowNormal, - /// Author normal rules from the same tree the element is in. - SameTreeAuthorNormal, - /// Style attribute normal rules. - StyleAttributeNormal, + /// 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: + /// + /// * Zero is the same tree as the element that matched the rule. This + /// is important so that we can optimize style attribute insertions. + /// + /// * The levels are ordered in accordance with + /// https://drafts.csswg.org/css-scoping/#shadow-cascading + shadow_cascade_order: ShadowCascadeOrder, + }, /// SVG SMIL animations. SMILOverride, /// CSS animations and script-generated animations. Animations, - /// Author-supplied important rules from the same tree the element came - /// from. - SameTreeAuthorImportant, - /// Style attribute important rules. - StyleAttributeImportant, - /// Shadow DOM important rules. - InnerShadowImportant, + /// Author-supplied important rules. + AuthorImportant { + /// The order in the shadow tree hierarchy, inverted, so that PartialOrd + /// does the right thing. + shadow_cascade_order: ShadowCascadeOrder, + }, /// User important rules. UserImportant, /// User-agent important rules. UAImportant, /// Transitions - /// - /// NB: If this changes from being last, change from_byte below. Transitions, } impl CascadeLevel { - /// Converts a raw byte to a CascadeLevel. - pub unsafe fn from_byte(byte: u8) -> Self { - debug_assert!(byte <= CascadeLevel::Transitions as u8); - mem::transmute(byte) + /// Pack this cascade level in a single byte. + /// + /// We have 10 levels, which we can represent with 4 bits, and then a + /// 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 @@ -679,16 +775,21 @@ impl CascadeLevel { } } - /// Returns whether this cascade level is unique per element, in which case - /// we can replace the path in the cascade without fear. - pub fn is_unique_per_element(&self) -> bool { - match *self { - CascadeLevel::Transitions | - CascadeLevel::Animations | - CascadeLevel::SMILOverride | - CascadeLevel::StyleAttributeNormal | - CascadeLevel::StyleAttributeImportant => true, - _ => false, + /// Returns the cascade level for author important declarations from the + /// same tree as the element. + #[inline] + pub fn same_tree_author_important() -> Self { + CascadeLevel::AuthorImportant { + shadow_cascade_order: ShadowCascadeOrder::for_same_tree(), + } + } + + /// 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] pub fn is_important(&self) -> bool { match *self { - CascadeLevel::SameTreeAuthorImportant | - CascadeLevel::InnerShadowImportant | - CascadeLevel::StyleAttributeImportant | + CascadeLevel::AuthorImportant { .. } | CascadeLevel::UserImportant | CascadeLevel::UAImportant => true, _ => false, @@ -724,14 +823,10 @@ impl CascadeLevel { CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent, CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User, CascadeLevel::PresHints | - CascadeLevel::InnerShadowNormal | - CascadeLevel::SameTreeAuthorNormal | - CascadeLevel::StyleAttributeNormal | + CascadeLevel::AuthorNormal { .. } | + CascadeLevel::AuthorImportant { .. } | CascadeLevel::SMILOverride | CascadeLevel::Animations | - CascadeLevel::SameTreeAuthorImportant | - CascadeLevel::StyleAttributeImportant | - CascadeLevel::InnerShadowImportant | CascadeLevel::Transitions => Origin::Author, } } @@ -1146,6 +1241,15 @@ impl StrongRuleNode { ) -> StrongRuleNode { 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 read_guard = self.get().children.upgradable_read(); @@ -1448,55 +1552,36 @@ impl StrongRuleNode { } }); - match node.cascade_level() { - // Non-author rules: - CascadeLevel::UANormal | - CascadeLevel::UAImportant | - CascadeLevel::UserNormal | - CascadeLevel::UserImportant => { - for (id, declaration) in longhands { - if properties.contains(id) { - // This property was set by a non-author rule. - // Stop looking for it in this element's rule - // nodes. - properties.remove(id); - - // However, if it is inherited, then it might be - // inherited from an author rule from an - // ancestor element's rule nodes. - if declaration.get_css_wide_keyword() == - Some(CSSWideKeyword::Inherit) - { - have_explicit_ua_inherit = true; - 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; + let is_author = node.cascade_level().origin() == Origin::Author; + for (id, declaration) in longhands { + 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. + // Stop looking for it in this element's rule + // nodes. + properties.remove(id); + + // However, if it is inherited, then it might be + // inherited from an author rule from an + // ancestor element's rule nodes. + if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) { + have_explicit_ua_inherit = true; + inherited_properties.insert(id); + } } } diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index 52b939e0219..cd99852861c 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -10,7 +10,7 @@ use crate::context::QuirksMode; use crate::dom::TElement; use crate::hash::map as hash_map; use crate::hash::{HashMap, HashSet}; -use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder}; +use crate::rule_tree::CascadeLevel; use crate::selector_parser::SelectorImpl; use crate::stylist::Rule; use crate::{Atom, LocalName, Namespace, WeakAtom}; @@ -171,7 +171,6 @@ impl SelectorMap<Rule> { context: &mut MatchingContext<E::Impl>, flags_setter: &mut F, cascade_level: CascadeLevel, - shadow_cascade_order: ShadowCascadeOrder, ) where E: TElement, F: FnMut(&E, ElementSelectorFlags), @@ -190,7 +189,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ); } @@ -203,7 +201,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ) } } @@ -217,7 +214,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ) } }); @@ -230,7 +226,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ) } @@ -242,7 +237,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ) } @@ -253,7 +247,6 @@ impl SelectorMap<Rule> { context, flags_setter, cascade_level, - shadow_cascade_order, ); } @@ -265,7 +258,6 @@ impl SelectorMap<Rule> { context: &mut MatchingContext<E::Impl>, flags_setter: &mut F, cascade_level: CascadeLevel, - shadow_cascade_order: ShadowCascadeOrder, ) where E: TElement, F: FnMut(&E, ElementSelectorFlags), @@ -279,9 +271,7 @@ impl SelectorMap<Rule> { context, flags_setter, ) { - matching_rules.push( - rule.to_applicable_declaration_block(cascade_level, shadow_cascade_order), - ); + matching_rules.push(rule.to_applicable_declaration_block(cascade_level)); } } } diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 4fdd4d4ab37..977a6c59a85 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -173,6 +173,12 @@ impl PseudoElement { 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. #[inline] pub fn is_eager(&self) -> bool { @@ -694,6 +700,14 @@ impl ElementSnapshot for ServoElementSnapshot { 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 { self.get_attr(&ns!(), &local_name!("class")) .map_or(false, |v| { diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index ac528f4fb37..6a3dc75f4d8 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -63,43 +63,38 @@ where { use crate::Atom; - // FIXME(emilio): This should be an actual static. - lazy_static! { - static ref SPECIAL_HTML_ELEMENTS: [Atom; 16] = [ - atom!("br"), - atom!("wbr"), - atom!("meter"), - atom!("progress"), - atom!("canvas"), - atom!("embed"), - atom!("object"), - atom!("audio"), - atom!("iframe"), - atom!("img"), - atom!("video"), - atom!("frame"), - atom!("frameset"), - atom!("input"), - atom!("textarea"), - atom!("select"), - ]; - } + const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [ + atom!("br"), + atom!("wbr"), + atom!("meter"), + atom!("progress"), + atom!("canvas"), + atom!("embed"), + atom!("object"), + atom!("audio"), + atom!("iframe"), + atom!("img"), + atom!("video"), + atom!("frame"), + atom!("frameset"), + atom!("input"), + atom!("textarea"), + atom!("select"), + ]; // https://drafts.csswg.org/css-display/#unbox-svg // // 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 // UA implements this either. - lazy_static! { - static ref SPECIAL_SVG_ELEMENTS: [Atom; 6] = [ - atom!("svg"), - atom!("a"), - atom!("g"), - atom!("use"), - atom!("tspan"), - atom!("textPath"), - ]; - } + const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [ + atom!("svg"), + atom!("a"), + atom!("g"), + atom!("use"), + atom!("tspan"), + atom!("textPath"), + ]; // https://drafts.csswg.org/css-display/#unbox-html if element.is_html_element() { diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 15c60402be4..f5194d7d443 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -124,6 +124,7 @@ impl StylesheetContents { url_data: UrlExtraData, quirks_mode: QuirksMode, ) -> Self { + debug_assert!(rules.is_static()); Self { rules, origin, @@ -144,6 +145,9 @@ impl StylesheetContents { /// Measure heap usage. #[cfg(feature = "gecko")] 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. self.rules.unconditional_shallow_size_of(ops) + self.rules.read_with(guard).size_of(guard, ops) diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index 721634dd9a3..6a522bcdb74 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -178,8 +178,7 @@ impl SupportsCondition { while input.try(Parser::expect_whitespace).is_ok() {} let pos = input.position(); let location = input.current_source_location(); - // FIXME: remove clone() when lifetimes are non-lexical - match input.next()?.clone() { + match *input.next()? { Token::ParenthesisBlock => { let nested = input.try(|input| input.parse_nested_block(parse_condition_or_declaration)); @@ -187,7 +186,8 @@ impl SupportsCondition { return nested; } }, - Token::Function(ident) => { + Token::Function(ref ident) => { + let ident = ident.clone(); let nested = input.try(|input| { input.parse_nested_block(|input| { SupportsCondition::parse_functional(&ident, input) @@ -197,7 +197,7 @@ impl SupportsCondition { 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)?; Ok(SupportsCondition::FutureSyntax( diff --git a/components/style/stylist.rs b/components/style/stylist.rs index ab6fe309f3f..83db2ec4b17 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -18,7 +18,7 @@ use crate::properties::{self, CascadeMode, ComputedValues}; use crate::properties::{AnimationRules, PropertyDeclarationBlock}; use crate::rule_cache::{RuleCache, RuleCacheConditions}; 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_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; @@ -1322,16 +1322,17 @@ impl Stylist { let iter_declarations = || { block .declaration_importance_iter() - .map(|(declaration, importance)| { - debug_assert!(!importance.important()); - (declaration, CascadeLevel::StyleAttributeNormal) - }) + .map(|(declaration, _)| (declaration, Origin::Author)) }; let metrics = get_metrics_provider_for_product(); // We don't bother inserting these declarations in the rule tree, since // 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, _, _>( &self.device, /* pseudo = */ None, @@ -1987,7 +1988,6 @@ impl CascadeData { self.rules_source_order, CascadeLevel::UANormal, selector.specificity(), - 0, )); continue; } @@ -2323,16 +2323,9 @@ impl Rule { pub fn to_applicable_declaration_block( &self, level: CascadeLevel, - shadow_cascade_order: ShadowCascadeOrder, ) -> ApplicableDeclarationBlock { let source = StyleSource::from_rule(self.style_rule.clone()); - ApplicableDeclarationBlock::new( - source, - self.source_order, - level, - self.specificity(), - shadow_cascade_order, - ) + ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity()) } /// Creates a new Rule. diff --git a/components/style/values/animated/transform.rs b/components/style/values/animated/transform.rs index 9ac64c3f2a7..aceb70f6766 100644 --- a/components/style/values/animated/transform.rs +++ b/components/style/values/animated/transform.rs @@ -1249,7 +1249,11 @@ impl Animate for ComputedRotate { match (self, other) { (&Rotate::None, &Rotate::None) => Ok(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( fx, fy, @@ -1258,7 +1262,8 @@ impl Animate for ComputedRotate { )) }, (&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( tx, ty, @@ -1368,8 +1373,7 @@ impl ComputedTranslate { LengthPercentage::zero(), Length::zero(), ), - Translate::Translate3D(tx, ty, tz) => (tx, ty, tz), - Translate::Translate(tx, ty) => (tx, ty, Length::zero()), + Translate::Translate(tx, ty, tz) => (tx, ty, tz), } } } @@ -1379,19 +1383,12 @@ impl Animate for ComputedTranslate { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { match (self, other) { (&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(_, ..)) => { let (from, to) = (self.resolve(), other.resolve()); Ok(Translate::Translate( from.0.animate(&to.0, procedure)?, from.1.animate(&to.1, procedure)?, + from.2.animate(&to.2, procedure)?, )) }, } @@ -1417,8 +1414,7 @@ impl ComputedScale { // Unspecified scales default to 1 match *self { Scale::None => (1.0, 1.0, 1.0), - Scale::Scale3D(sx, sy, sz) => (sx, sy, sz), - Scale::Scale(sx, sy) => (sx, sy, 1.), + Scale::Scale(sx, sy, sz) => (sx, sy, sz), } } } @@ -1428,7 +1424,7 @@ impl Animate for ComputedScale { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { match (self, other) { (&Scale::None, &Scale::None) => Ok(Scale::None), - (&Scale::Scale3D(_, ..), _) | (_, &Scale::Scale3D(_, ..)) => { + (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => { let (from, to) = (self.resolve(), other.resolve()); // For transform lists, we add by appending to the list of // transform functions. However, ComputedScale cannot be @@ -1436,24 +1432,12 @@ impl Animate for ComputedScale { // result here. if procedure == Procedure::Add { // 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)); - } - 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)); + return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2)); } Ok(Scale::Scale( animate_multiplicative_factor(from.0, to.0, procedure)?, animate_multiplicative_factor(from.1, to.1, procedure)?, + animate_multiplicative_factor(from.2, to.2, procedure)?, )) }, } diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs index 807136d98c4..5666e2c5818 100644 --- a/components/style/values/generics/grid.rs +++ b/components/style/values/generics/grid.rs @@ -58,7 +58,7 @@ pub use self::GenericGridLine as GridLine; impl<Integer> GridLine<Integer> where - Integer: Zero, + Integer: PartialEq + Zero, { /// The `auto` value. pub fn auto() -> Self { @@ -73,11 +73,27 @@ where pub fn is_auto(&self) -> bool { 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> where - Integer: ToCss + Zero, + Integer: ToCss + PartialEq + Zero, { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where @@ -261,6 +277,19 @@ pub enum GenericTrackSize<L> { pub use self::GenericTrackSize as TrackSize; 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>` /// /// <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> { 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; 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 { 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. @@ -755,6 +794,9 @@ pub enum GenericGridTemplateComponent<L, I> { pub use self::GenericGridTemplateComponent as GridTemplateComponent; impl<L, I> GridTemplateComponent<L, I> { + /// The initial value. + const INITIAL_VALUE: Self = Self::None; + /// Returns length of the <track-list>s <track-size> pub fn track_list_len(&self) -> usize { match *self { @@ -762,4 +804,16 @@ impl<L, I> GridTemplateComponent<L, I> { _ => 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 + } } diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index 4a8dc7f63fa..3322323e76e 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -700,40 +700,52 @@ where pub enum GenericScale<Number> { /// 'none' None, - /// '<number>{1,2}' - Scale(Number, Number), - /// '<number>{3}' - Scale3D(Number, Number, Number), + /// '<number>{1,3}' + Scale(Number, Number, Number), } 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 where W: fmt::Write, + f32: From<Number>, { match *self { Scale::None => dest.write_str("none"), - Scale::Scale(ref x, ref y) => { + Scale::Scale(ref x, ref y, ref z) => { x.to_css(dest)?; - if x != y { + + let is_3d = f32::from(*z) != 1.0; + if is_3d || x != y { dest.write_char(' ')?; y.to_css(dest)?; } + + if is_3d { + dest.write_char(' ')?; + z.to_css(dest)?; + } 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( Clone, 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 /// be omitted when serializing). /// -/// If a 3d translation is specified, all three values must be serialized. -/// -/// We don't omit the 3rd component even if it is 0px for now, and the -/// related spec issue is https://github.com/w3c/csswg-drafts/issues/3305 +/// 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. +/// https://github.com/w3c/csswg-drafts/issues/3305 /// /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms> pub enum GenericTranslate<LengthPercentage, Length> where LengthPercentage: Zero, + Length: Zero, { /// 'none' None, - /// '<length-percentage>' or '<length-percentage> <length-percentage>' + /// <length-percentage> [ <length-percentage> <length>? ]? Translate( 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; diff --git a/components/style/values/specified/angle.rs b/components/style/values/specified/angle.rs index 47c1a60772b..458f4178e59 100644 --- a/components/style/values/specified/angle.rs +++ b/components/style/values/specified/angle.rs @@ -208,24 +208,34 @@ impl Angle { input: &mut Parser<'i, 't>, allow_unitless_zero: AllowUnitlessZeroAngle, ) -> Result<Self, ParseError<'i>> { - // FIXME: remove clone() when lifetimes are non-lexical - let token = input.next()?.clone(); - match token { + let t = input.next()?; + match *t { Token::Dimension { 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 { 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") => { 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())) } } diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 58d51f8c006..7fd5a250e06 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -109,8 +109,6 @@ pub enum DisplayInside { #[cfg(feature = "gecko")] MozGridLine, #[cfg(feature = "gecko")] - MozStack, - #[cfg(feature = "gecko")] MozDeck, #[cfg(feature = "gecko")] MozGroupbox, @@ -243,8 +241,6 @@ impl Display { #[cfg(feature = "gecko")] pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine); #[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); #[cfg(feature = "gecko")] pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox); @@ -669,8 +665,6 @@ impl Parse for Display { #[cfg(feature = "gecko")] "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine, #[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, #[cfg(feature = "gecko")] "-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::Transform => WillChangeBits::TRANSFORM, #[cfg(feature = "gecko")] - LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale => WillChangeBits::TRANSFORM, + LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => { + WillChangeBits::TRANSFORM + }, _ => WillChangeBits::empty(), }; diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index b101cd69de7..f9feb616fff 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -163,9 +163,8 @@ impl CalcNode { expected_unit: CalcUnit, ) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); - // FIXME: remove early returns when lifetimes are non-lexical match (input.next()?, expected_unit) { - (&Token::Number { value, .. }, _) => return Ok(CalcNode::Number(value)), + (&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)), ( &Token::Dimension { value, ref unit, .. @@ -177,24 +176,18 @@ impl CalcNode { value, ref unit, .. }, CalcUnit::LengthPercentage, - ) => { - return NoCalcLength::parse_dimension(context, value, unit) - .map(CalcNode::Length) - .map_err(|()| { - location.new_custom_error(StyleParseErrorKind::UnspecifiedError) - }); - }, + ) => NoCalcLength::parse_dimension(context, value, unit) + .map(CalcNode::Length) + .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)), ( &Token::Dimension { value, ref unit, .. }, CalcUnit::Angle, ) => { - return Angle::parse_dimension(value, unit, /* from_calc = */ true) + Angle::parse_dimension(value, unit, /* from_calc = */ true) .map(CalcNode::Angle) - .map_err(|()| { - location.new_custom_error(StyleParseErrorKind::UnspecifiedError) - }); + .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) }, ( &Token::Dimension { @@ -202,21 +195,22 @@ impl CalcNode { }, CalcUnit::Time, ) => { - return Time::parse_dimension(value, unit, /* from_calc = */ true) + Time::parse_dimension(value, unit, /* from_calc = */ true) .map(CalcNode::Time) - .map_err(|()| { - location.new_custom_error(StyleParseErrorKind::UnspecifiedError) - }); + .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) }, (&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) | (&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => { - return Ok(CalcNode::Percentage(unit_value)); + Ok(CalcNode::Percentage(unit_value)) + }, + (&Token::ParenthesisBlock, _) => { + input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit)) }, - (&Token::ParenthesisBlock, _) => {}, - (&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {}, - (t, _) => return Err(location.new_unexpected_token_error(t.clone())), + (&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())), } - input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit)) } /// Parse a top-level `calc` expression, with all nested sub-expressions. @@ -236,8 +230,7 @@ impl CalcNode { if input.is_exhausted() { break; // allow trailing whitespace } - // FIXME: remove clone() when lifetimes are non-lexical - match input.next()?.clone() { + match *input.next()? { Token::Delim('+') => { let rhs = Self::parse_product(context, input, expected_unit)?; 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)); 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)); + }, } }, _ => { diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 01e5890d015..4e761f95338 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -36,9 +36,6 @@ pub enum Color { /// A system color #[cfg(feature = "gecko")] 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 #[cfg(feature = "gecko")] InheritFromBodyQuirk, @@ -143,6 +140,8 @@ pub enum SystemColor { Windowframe, Windowtext, MozButtondefault, + MozDefaultColor, + MozDefaultBackgroundColor, MozDialog, MozDialogtext, /// Used to highlight valid regions to drop something onto. @@ -231,6 +230,10 @@ pub enum SystemColor { /// colors. MozNativehyperlinktext, + MozHyperlinktext, + MozActivehyperlinktext, + MozVisitedhyperlinktext, + /// Combobox widgets MozComboboxtext, MozCombobox, @@ -246,24 +249,20 @@ impl SystemColor { #[inline] fn compute(&self, cx: &Context) -> ComputedColor { use crate::gecko_bindings::bindings; - unsafe { - convert_nscolor_to_computedcolor(bindings::Gecko_GetLookAndFeelSystemColor( - *self as i32, - cx.device().document(), - )) - } - } -} -#[cfg(feature = "gecko")] -mod gecko { - #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] - pub enum SpecialColorKeyword { - MozDefaultColor, - MozDefaultBackgroundColor, - MozHyperlinktext, - MozActivehyperlinktext, - MozVisitedhyperlinktext, + let prefs = cx.device().pref_sheet_prefs(); + + convert_nscolor_to_computedcolor(match *self { + SystemColor::MozDefaultColor => prefs.mDefaultColor, + SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor, + SystemColor::MozHyperlinktext => prefs.mLinkColor, + SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor, + SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor, + + _ => 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)) { return Ok(Color::System(system)); } - - if let Ok(c) = input.try(gecko::SpecialColorKeyword::parse) { - return Ok(Color::Special(c)); - } } match e.kind { @@ -401,8 +396,6 @@ impl ToCss for Color { #[cfg(feature = "gecko")] Color::System(system) => system.to_css(dest), #[cfg(feature = "gecko")] - Color::Special(special) => special.to_css(dest), - #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => Ok(()), } } @@ -553,18 +546,6 @@ impl Color { #[cfg(feature = "gecko")] Color::System(system) => system.compute(_context?), #[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 => { ComputedColor::rgba(_context?.device().body_text_color()) }, diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs index 54690b53105..09020e73448 100644 --- a/components/style/values/specified/counters.rs +++ b/components/style/values/specified/counters.rs @@ -63,7 +63,10 @@ fn parse_counters<'i, 't>( let location = input.current_source_location(); let name = match input.next() { 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, }; @@ -147,57 +150,60 @@ impl Parse for Content { continue; } } - // FIXME: remove clone() when lifetimes are non-lexical - match input.next().map(|t| t.clone()) { - Ok(Token::QuotedString(ref value)) => { + match input.next() { + Ok(&Token::QuotedString(ref value)) => { content.push(generics::ContentItem::String( 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, - "counter" => Some(input.parse_nested_block(|input| { + "counter" => input.parse_nested_block(|input| { let location = input.current_source_location(); let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; let style = Content::parse_counter_style(context, input); 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 name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; input.expect_comma()?; let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str(); let style = Content::parse_counter_style(context, input); Ok(generics::ContentItem::Counters(name, separator, style)) - })), + }), #[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)?)) - })), - _ => None - }; - match result { - Some(result) => content.push(result?), - None => { + }), + _ => { + let name = name.clone(); 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, "open-quote" => generics::ContentItem::OpenQuote, "close-quote" => generics::ContentItem::CloseQuote, "no-open-quote" => generics::ContentItem::NoOpenQuote, "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, - 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() { diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 88f5969f52c..c09d5a2c34c 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -1212,15 +1212,15 @@ impl Parse for FontVariantAlternates { parsed_alternates |= $flag; ) ); - while let Ok(_) = input.try(|input| { - // FIXME: remove clone() when lifetimes are non-lexical - match input.next()?.clone() { - Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { - check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); - alternates.push(VariantAlternates::HistoricalForms); - Ok(()) - }, - Token::Function(ref name) => input.parse_nested_block(|i| { + while let Ok(_) = input.try(|input| match *input.next()? { + Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { + check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); + alternates.push(VariantAlternates::HistoricalForms); + Ok(()) + }, + Token::Function(ref name) => { + let name = name.clone(); + input.parse_nested_block(|i| { match_ignore_ascii_case! { &name, "swash" => { check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH); @@ -1270,9 +1270,9 @@ impl Parse for FontVariantAlternates { }, _ => 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() { diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index b75e97aca99..4830aea24d6 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -102,8 +102,8 @@ impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> { ) -> Result<Self, ParseError<'i>> { use style_traits::{Separator, Space}; let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?; - if track_sizes.len() == 1 && track_sizes[0].is_auto() { - //`auto`, which is the initial value, is always represented by an empty slice. + if track_sizes.len() == 1 && track_sizes[0].is_initial() { + // A single track with the initial value is always represented by an empty slice. return Ok(Default::default()); } return Ok(ImplicitGridTracks(track_sizes.into())); diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 7488e631e54..126a4cc69d1 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -192,62 +192,57 @@ impl Parse for Gradient { Radial, } - // FIXME: remove clone() when lifetimes are non-lexical - let func = input.expect_function()?.clone(); - let result = match_ignore_ascii_case! { &func, + let func = input.expect_function()?; + let (shape, repeating, mut compat_mode) = match_ignore_ascii_case! { &func, "linear-gradient" => { - Some((Shape::Linear, false, GradientCompatMode::Modern)) + (Shape::Linear, false, GradientCompatMode::Modern) }, "-webkit-linear-gradient" => { - Some((Shape::Linear, false, GradientCompatMode::WebKit)) + (Shape::Linear, false, GradientCompatMode::WebKit) }, #[cfg(feature = "gecko")] "-moz-linear-gradient" => { - Some((Shape::Linear, false, GradientCompatMode::Moz)) + (Shape::Linear, false, GradientCompatMode::Moz) }, "repeating-linear-gradient" => { - Some((Shape::Linear, true, GradientCompatMode::Modern)) + (Shape::Linear, true, GradientCompatMode::Modern) }, "-webkit-repeating-linear-gradient" => { - Some((Shape::Linear, true, GradientCompatMode::WebKit)) + (Shape::Linear, true, GradientCompatMode::WebKit) }, #[cfg(feature = "gecko")] "-moz-repeating-linear-gradient" => { - Some((Shape::Linear, true, GradientCompatMode::Moz)) + (Shape::Linear, true, GradientCompatMode::Moz) }, "radial-gradient" => { - Some((Shape::Radial, false, GradientCompatMode::Modern)) + (Shape::Radial, false, GradientCompatMode::Modern) }, "-webkit-radial-gradient" => { - Some((Shape::Radial, false, GradientCompatMode::WebKit)) + (Shape::Radial, false, GradientCompatMode::WebKit) }, #[cfg(feature = "gecko")] "-moz-radial-gradient" => { - Some((Shape::Radial, false, GradientCompatMode::Moz)) + (Shape::Radial, false, GradientCompatMode::Moz) }, "repeating-radial-gradient" => { - Some((Shape::Radial, true, GradientCompatMode::Modern)) + (Shape::Radial, true, GradientCompatMode::Modern) }, "-webkit-repeating-radial-gradient" => { - Some((Shape::Radial, true, GradientCompatMode::WebKit)) + (Shape::Radial, true, GradientCompatMode::WebKit) }, #[cfg(feature = "gecko")] "-moz-repeating-radial-gradient" => { - Some((Shape::Radial, true, GradientCompatMode::Moz)) + (Shape::Radial, true, GradientCompatMode::Moz) }, "-webkit-gradient" => { return input.parse_nested_block(|i| { Self::parse_webkit_gradient_argument(context, i) }); }, - _ => None, - }; - - let (shape, repeating, mut compat_mode) = match result { - Some(result) => result, - None => { + _ => { + let func = func.clone(); return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func))); - }, + } }; let (kind, items) = input.parse_nested_block(|i| { diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index c98038bdc68..ebf7746ee11 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -587,39 +587,34 @@ impl Length { num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>> { - // FIXME: remove early returns when lifetimes are non-lexical - { - let location = input.current_source_location(); - let token = input.next()?; - match *token { - Token::Dimension { - value, ref unit, .. - } if num_context.is_ok(context.parsing_mode, value) => { - return NoCalcLength::parse_dimension(context, value, unit) - .map(Length::NoCalc) - .map_err(|()| location.new_unexpected_token_error(token.clone())); - }, - Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { - if value != 0. && - !context.parsing_mode.allows_unitless_lengths() && - !allow_quirks.allowed(context.quirks_mode) - { - return Err( - location.new_custom_error(StyleParseErrorKind::UnspecifiedError) - ); - } - return Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px( - value, - )))); - }, - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - ref token => return Err(location.new_unexpected_token_error(token.clone())), - } + let location = input.current_source_location(); + let token = input.next()?; + match *token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => { + NoCalcLength::parse_dimension(context, value, unit) + .map(Length::NoCalc) + .map_err(|()| location.new_unexpected_token_error(token.clone())) + }, + Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { + if value != 0. && + !context.parsing_mode.allows_unitless_lengths() && + !allow_quirks.allowed(context.quirks_mode) + { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px( + value, + )))) + }, + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input + .parse_nested_block(|input| { + CalcNode::parse_length(context, input, num_context) + .map(|calc| Length::Calc(Box::new(calc))) + }), + ref token => return Err(location.new_unexpected_token_error(token.clone())), } - input.parse_nested_block(|input| { - CalcNode::parse_length(context, input, num_context) - .map(|calc| Length::Calc(Box::new(calc))) - }) } /// Parse a non-negative length @@ -809,44 +804,41 @@ impl LengthPercentage { num_context: AllowedNumericType, allow_quirks: AllowQuirks, ) -> Result<Self, ParseError<'i>> { - // FIXME: remove early returns when lifetimes are non-lexical - { - let location = input.current_source_location(); - let token = input.next()?; - match *token { - Token::Dimension { - value, ref unit, .. - } if num_context.is_ok(context.parsing_mode, value) => { - return NoCalcLength::parse_dimension(context, value, unit) - .map(LengthPercentage::Length) - .map_err(|()| location.new_unexpected_token_error(token.clone())); - }, - Token::Percentage { unit_value, .. } - if num_context.is_ok(context.parsing_mode, unit_value) => + let location = input.current_source_location(); + let token = input.next()?; + match *token { + Token::Dimension { + value, ref unit, .. + } if num_context.is_ok(context.parsing_mode, value) => { + return NoCalcLength::parse_dimension(context, value, unit) + .map(LengthPercentage::Length) + .map_err(|()| location.new_unexpected_token_error(token.clone())); + }, + Token::Percentage { unit_value, .. } + if num_context.is_ok(context.parsing_mode, unit_value) => + { + return Ok(LengthPercentage::Percentage(computed::Percentage( + unit_value, + ))); + } + Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { + if value != 0. && + !context.parsing_mode.allows_unitless_lengths() && + !allow_quirks.allowed(context.quirks_mode) { - return Ok(LengthPercentage::Percentage(computed::Percentage( - unit_value, - ))); + return Err(location.new_unexpected_token_error(token.clone())); + } else { + return Ok(LengthPercentage::Length(NoCalcLength::from_px(value))); } - Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { - if value != 0. && - !context.parsing_mode.allows_unitless_lengths() && - !allow_quirks.allowed(context.quirks_mode) - { - return Err(location.new_unexpected_token_error(token.clone())); - } else { - return Ok(LengthPercentage::Length(NoCalcLength::from_px(value))); - } - }, - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - _ => return Err(location.new_unexpected_token_error(token.clone())), - } + }, + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let calc = input.parse_nested_block(|i| { + CalcNode::parse_length_or_percentage(context, i, num_context) + })?; + Ok(LengthPercentage::Calc(Box::new(calc))) + }, + _ => return Err(location.new_unexpected_token_error(token.clone())), } - - let calc = input.parse_nested_block(|i| { - CalcNode::parse_length_or_percentage(context, i, num_context) - })?; - Ok(LengthPercentage::Calc(Box::new(calc))) } /// Parses allowing the unitless length quirk. diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d10a399dd01..bc7a8203dee 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -136,24 +136,22 @@ fn parse_number_with_clamping_mode<'i, 't>( clamping_mode: AllowedNumericType, ) -> Result<Number, ParseError<'i>> { let location = input.current_source_location(); - // FIXME: remove early returns when lifetimes are non-lexical match *input.next()? { 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), calc_clamping_mode: None, - }); + }) + }, + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?; + Ok(Number { + value: result.min(f32::MAX).max(f32::MIN), + calc_clamping_mode: Some(clamping_mode), + }) }, - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - ref t => return Err(location.new_unexpected_token_error(t.clone())), + ref t => Err(location.new_unexpected_token_error(t.clone())), } - - let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?; - - Ok(Number { - value: result.min(f32::MAX).max(f32::MIN), - calc_clamping_mode: Some(clamping_mode), - }) } /// A CSS `<number>` specified value. @@ -540,19 +538,16 @@ impl Parse for Integer { input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); - - // FIXME: remove early returns when lifetimes are non-lexical match *input.next()? { Token::Number { int_value: Some(v), .. - } => return Ok(Integer::new(v)), - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - ref t => return Err(location.new_unexpected_token_error(t.clone())), + } => Ok(Integer::new(v)), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?; + Ok(Integer::from_calc(result)) + }, + ref t => Err(location.new_unexpected_token_error(t.clone())), } - - let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?; - - Ok(Integer::from_calc(result)) } } diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index 90652b5ed64..2351acecc6a 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -16,11 +16,24 @@ use style_traits::{ParseError, StyleParseErrorKind}; /// The specified value of `offset-path`. 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> { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { + if !is_ray_enabled() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + let mut angle = None; let mut size = None; let mut contain = false; diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs index 64cb02bb424..50ebbb3bcf6 100644 --- a/components/style/values/specified/percentage.rs +++ b/components/style/values/specified/percentage.rs @@ -111,25 +111,25 @@ impl Percentage { num_context: AllowedNumericType, ) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); - // FIXME: remove early returns when lifetimes are non-lexical match *input.next()? { Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => { - return Ok(Percentage::new(unit_value)); - } - Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}, - ref t => return Err(location.new_unexpected_token_error(t.clone())), + Ok(Percentage::new(unit_value)) + }, + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + let result = + input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?; + + // TODO(emilio): -moz-image-rect is the only thing that uses + // the clamping mode... I guess we could disallow it... + Ok(Percentage { + value: result, + calc_clamping_mode: Some(num_context), + }) + }, + ref t => Err(location.new_unexpected_token_error(t.clone())), } - - let result = input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?; - - // TODO(emilio): -moz-image-rect is the only thing that uses - // the clamping mode... I guess we could disallow it... - Ok(Percentage { - value: result, - calc_clamping_mode: Some(num_context), - }) } /// Parses a percentage token, but rejects it if it's negative. diff --git a/components/style/values/specified/time.rs b/components/style/values/specified/time.rs index f46925bc5c1..0006ec45923 100644 --- a/components/style/values/specified/time.rs +++ b/components/style/values/specified/time.rs @@ -83,27 +83,25 @@ impl Time { use style_traits::ParsingMode; 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 // that the ParserMode of the ParserContext allows all numeric // 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 // ParsingMode::DEFAULT directly. - Ok(&Token::Dimension { + Token::Dimension { value, ref unit, .. - }) if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => { - return Time::parse_dimension(value, unit, /* from_calc = */ false).map_err(|()| { - location.new_custom_error(StyleParseErrorKind::UnspecifiedError) - }); + } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => { + Time::parse_dimension(value, unit, /* from_calc = */ false) + .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) }, - Ok(&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)) { - Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time), - _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { + match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) { + Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time), + _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + }, + ref t => return Err(location.new_unexpected_token_error(t.clone())), } } diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index 157c6504856..a61f982cd2d 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -12,7 +12,9 @@ use crate::values::generics::transform::{Matrix, Matrix3D}; use crate::values::specified::position::{ 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 cssparser::Parser; use style_traits::{ParseError, StyleParseErrorKind}; @@ -163,32 +165,32 @@ impl Transform { Ok(generic::TransformOperation::Translate3D(tx, ty, tz)) }, "scale" => { - let sx = Number::parse(context, input)?; + let sx = NumberOrPercentage::parse(context, input)?.to_number(); 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)) } else { Ok(generic::TransformOperation::Scale(sx, sx)) } }, "scalex" => { - let sx = Number::parse(context, input)?; + let sx = NumberOrPercentage::parse(context, input)?.to_number(); Ok(generic::TransformOperation::ScaleX(sx)) }, "scaley" => { - let sy = Number::parse(context, input)?; + let sy = NumberOrPercentage::parse(context, input)?.to_number(); Ok(generic::TransformOperation::ScaleY(sy)) }, "scalez" => { - let sz = Number::parse(context, input)?; + let sz = NumberOrPercentage::parse(context, input)?.to_number(); Ok(generic::TransformOperation::ScaleZ(sz)) }, "scale3d" => { - let sx = Number::parse(context, input)?; + let sx = NumberOrPercentage::parse(context, input)?.to_number(); input.expect_comma()?; - let sy = Number::parse(context, input)?; + let sy = NumberOrPercentage::parse(context, input)?.to_number(); input.expect_comma()?; - let sz = Number::parse(context, input)?; + let sz = NumberOrPercentage::parse(context, input)?.to_number(); Ok(generic::TransformOperation::Scale3D(sx, sy, sz)) }, "rotate" => { @@ -421,17 +423,22 @@ impl Parse for Translate { if let Ok(ty) = input.try(|i| specified::LengthPercentage::parse(context, i)) { if let Ok(tz) = input.try(|i| specified::Length::parse(context, i)) { // '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>' - return Ok(generic::Translate::Translate(tx, ty)); + return Ok(generic::Translate::Translate( + tx, + ty, + specified::Length::zero(), + )); } // 'translate: <length-percentage> ' Ok(generic::Translate::Translate( tx, specified::LengthPercentage::zero(), + specified::Length::zero(), )) } } @@ -440,6 +447,9 @@ impl Parse for Translate { pub type Scale = generic::Scale<Number>; 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>( context: &ParserContext, input: &mut Parser<'i, 't>, @@ -448,18 +458,19 @@ impl Parse for Scale { return Ok(generic::Scale::None); } - let sx = Number::parse(context, input)?; - if let Ok(sy) = input.try(|i| Number::parse(context, i)) { - if let Ok(sz) = input.try(|i| Number::parse(context, i)) { + let sx = NumberOrPercentage::parse(context, input)?.to_number(); + if let Ok(sy) = input.try(|i| NumberOrPercentage::parse(context, i)) { + let sy = sy.to_number(); + if let Ok(sz) = input.try(|i| NumberOrPercentage::parse(context, i)) { // '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>' - return Ok(generic::Scale::Scale(sx, sy)); + return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0))); } // 'scale: <number>' - Ok(generic::Scale::Scale(sx, sx)) + Ok(generic::Scale::Scale(sx, sx, Number::new(1.0))) } } diff --git a/components/style_traits/Cargo.toml b/components/style_traits/Cargo.toml index f3dd0df744f..1f3f3e9ddb1 100644 --- a/components/style_traits/Cargo.toml +++ b/components/style_traits/Cargo.toml @@ -15,7 +15,7 @@ gecko = [] [dependencies] app_units = "0.7" -cssparser = "0.27.1" +cssparser = "0.27" bitflags = "1.0" euclid = "0.20" lazy_static = "1" diff --git a/components/to_shmem/Cargo.toml b/components/to_shmem/Cargo.toml index 71426507647..dd3614111e1 100644 --- a/components/to_shmem/Cargo.toml +++ b/components/to_shmem/Cargo.toml @@ -14,7 +14,7 @@ servo = ["cssparser/serde", "string_cache"] gecko = [] [dependencies] -cssparser = "0.27.1" +cssparser = "0.27" servo_arc = { path = "../servo_arc" } smallbitvec = "2.1.1" smallvec = "0.6.6" diff --git a/tests/unit/style/Cargo.toml b/tests/unit/style/Cargo.toml index f25e4a5b20f..2764c9d0408 100644 --- a/tests/unit/style/Cargo.toml +++ b/tests/unit/style/Cargo.toml @@ -11,7 +11,7 @@ doctest = false [dependencies] app_units = "0.7" -cssparser = "0.27.1" +cssparser = "0.27" euclid = "0.20" html5ever = "0.25" rayon = "1" diff --git a/tests/wpt/metadata/css/css-transforms/animation/rotate-interpolation.html.ini b/tests/wpt/metadata/css/css-transforms/animation/rotate-interpolation.html.ini index e80c4cdd294..eb5fe0b2b88 100644 --- a/tests/wpt/metadata/css/css-transforms/animation/rotate-interpolation.html.ini +++ b/tests/wpt/metadata/css/css-transforms/animation/rotate-interpolation.html.ini @@ -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\]] 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\]] expected: FAIL @@ -821,9 +818,6 @@ [Web Animations: property <rotate> from [100deg\] to [-100deg\] at (0) should be [100deg\]] 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\]] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/animation/scale-interpolation.html.ini b/tests/wpt/metadata/css/css-transforms/animation/scale-interpolation.html.ini index bae37e59cb6..d25ccb121c8 100644 --- a/tests/wpt/metadata/css/css-transforms/animation/scale-interpolation.html.ini +++ b/tests/wpt/metadata/css/css-transforms/animation/scale-interpolation.html.ini @@ -98,9 +98,6 @@ [CSS Transitions: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]] 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\]] expected: FAIL @@ -251,9 +248,6 @@ [Web Animations: property <scale> from [-10 5\] to [10 -5\] at (1) should be [10 -5\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] expected: FAIL @@ -581,15 +545,6 @@ [Web Animations: property <scale> from [1\] to [10 -5 0\] at (2) should be [19 -11 -1\]] 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\]] 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\]] 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\]] expected: FAIL @@ -629,9 +581,6 @@ [CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (-1) should be [3 0\]] 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\]] expected: FAIL @@ -698,9 +647,6 @@ [Web Animations: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]] 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\]] expected: FAIL @@ -752,9 +698,6 @@ [Web Animations: property <scale> from [unset\] to [1.5 1\] at (1) should be [1.5 1\]] 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\]] expected: FAIL @@ -791,9 +734,6 @@ [CSS Animations: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]] 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\]] 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\]] 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\]] 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\]] 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\]] expected: FAIL @@ -908,9 +842,6 @@ [CSS Animations: property <scale> from neutral to [1.5 1\] at (1) should be [1.5 1\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] 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\]] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/animation/translate-interpolation.html.ini b/tests/wpt/metadata/css/css-transforms/animation/translate-interpolation.html.ini index 8f6ece60c55..8987874f01c 100644 --- a/tests/wpt/metadata/css/css-transforms/animation/translate-interpolation.html.ini +++ b/tests/wpt/metadata/css/css-transforms/animation/translate-interpolation.html.ini @@ -74,15 +74,6 @@ [translate interpolation] 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\]] expected: FAIL @@ -125,9 +116,6 @@ [CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (-1) should be [400px 200px 800px\]] 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\]] expected: FAIL @@ -161,9 +149,6 @@ [CSS Transitions with transition: all: property <translate> from [unset\] to [20px\] at (0.75) should be [15px\]] 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\]] expected: FAIL @@ -173,15 +158,9 @@ [Web Animations: property <translate> from [inherit\] to [initial\] at (0) should be [100px 200px 300px\]] 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%\]] 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\]] 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\]] 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\]] expected: FAIL @@ -341,9 +317,6 @@ [CSS Transitions: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]] 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\]] expected: FAIL @@ -437,9 +410,6 @@ [Web Animations: property <translate> from [-100px -50px 100px\] to [0px\] at (-1) should be [-200px -100px 200px\]] 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\]] expected: FAIL @@ -527,9 +497,6 @@ [CSS Animations: property <translate> from neutral to [20px\] at (2) should be [30px\]] 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\]] expected: FAIL @@ -635,9 +602,6 @@ [Web Animations: property <translate> from [inherit\] to [initial\] at (-1) should be [200px 400px 600px\]] 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\]] 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\]] 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\]] expected: FAIL @@ -788,9 +749,6 @@ [CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (0.75) should be [125px 175px 275px\]] 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\]] 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\]] 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\]] 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\]] 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\]] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/parsing/scale-parsing-valid.html.ini b/tests/wpt/metadata/css/css-transforms/parsing/scale-parsing-valid.html.ini deleted file mode 100644 index 14663c5a719..00000000000 --- a/tests/wpt/metadata/css/css-transforms/parsing/scale-parsing-valid.html.ini +++ /dev/null @@ -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 - diff --git a/tests/wpt/metadata/css/css-transforms/parsing/translate-parsing-valid.html.ini b/tests/wpt/metadata/css/css-transforms/parsing/translate-parsing-valid.html.ini index 9bfdefbcc40..2e5706ee01d 100644 --- a/tests/wpt/metadata/css/css-transforms/parsing/translate-parsing-valid.html.ini +++ b/tests/wpt/metadata/css/css-transforms/parsing/translate-parsing-valid.html.ini @@ -8,12 +8,3 @@ [e.style['translate'\] = "100px calc(10px - 10%)" should set the property value] 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 - diff --git a/tests/wpt/metadata/css/css-transforms/transform-scale-percent-001.html.ini b/tests/wpt/metadata/css/css-transforms/transform-scale-percent-001.html.ini new file mode 100644 index 00000000000..4ae92db68d0 --- /dev/null +++ b/tests/wpt/metadata/css/css-transforms/transform-scale-percent-001.html.ini @@ -0,0 +1,2 @@ +[transform-scale-percent-001.html] + expected: FAIL |