diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 8b168ae2fd5..cf8eb460383 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -65,6 +65,7 @@ reset right sans-serif screen +scroll-position search select serif diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index d393f23c2c4..f0383e871fa 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -757,14 +757,16 @@ impl LayoutThread { Msg::RegisterPaint(name, mut properties, painter) => { debug!("Registering the painter"); let properties = properties.drain(..) - .filter_map(|name| PropertyId::parse(&*name) - .ok().map(|id| (name.clone(), id))) - .filter(|&(_, ref id)| id.as_shorthand().is_err()) + .filter_map(|name| { + let id = PropertyId::parse_enabled_for_all_content(&*name).ok()?; + Some((name.clone(), id)) + }) + .filter(|&(_, ref id)| !id.is_shorthand()) .collect(); let registered_painter = RegisteredPainterImpl { name: name.clone(), - properties: properties, - painter: painter, + properties, + painter, }; self.registered_painters.0.insert(name, registered_painter); }, diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 1e95cd9d27d..e63d5833909 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -312,22 +312,18 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue fn GetPropertyValue(&self, property: DOMString) -> DOMString { - let id = if let Ok(id) = PropertyId::parse(&property) { - id - } else { - // Unkwown property - return DOMString::new() + let id = match PropertyId::parse_enabled_for_all_content(&property) { + Ok(id) => id, + Err(..) => return DOMString::new(), }; self.get_property_value(id) } // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority fn GetPropertyPriority(&self, property: DOMString) -> DOMString { - let id = if let Ok(id) = PropertyId::parse(&property) { - id - } else { - // Unkwown property - return DOMString::new() + let id = match PropertyId::parse_enabled_for_all_content(&property) { + Ok(id) => id, + Err(..) => return DOMString::new(), }; self.owner.with_block(|pdb| { @@ -347,11 +343,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { priority: DOMString) -> ErrorResult { // Step 3 - let id = if let Ok(id) = PropertyId::parse(&property) { - id - } else { - // Unknown property - return Ok(()) + let id = match PropertyId::parse_enabled_for_all_content(&property) { + Ok(id) => id, + Err(..) => return Ok(()), }; self.set_property(id, value, priority) } @@ -364,7 +358,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { } // Step 2 & 3 - let id = match PropertyId::parse(&property) { + let id = match PropertyId::parse_enabled_for_all_content(&property) { Ok(id) => id, Err(..) => return Ok(()), // Unkwown property }; @@ -396,11 +390,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Err(Error::NoModificationAllowed); } - let id = if let Ok(id) = PropertyId::parse(&property) { - id - } else { - // Unkwown property, cannot be there to remove. - return Ok(DOMString::new()) + let id = match PropertyId::parse_enabled_for_all_content(&property) { + Ok(id) => id, + Err(..) => return Ok(DOMString::new()), }; let mut string = String::new(); diff --git a/components/style/dom.rs b/components/style/dom.rs index 0e5da1542d7..42f6e9ba0c0 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -466,12 +466,6 @@ pub trait TElement: &[] } - /// For a given NAC element, return the closest non-NAC ancestor, which is - /// guaranteed to exist. - fn closest_non_native_anonymous_ancestor(&self) -> Option { - unreachable!("Servo doesn't know about NAC"); - } - /// Get this element's style attribute. fn style_attribute(&self) -> Option>>; @@ -657,9 +651,8 @@ pub trait TElement: false } - /// Returns true if this element is native anonymous (only Gecko has native - /// anonymous content). - fn is_native_anonymous(&self) -> bool { + /// Returns true if this element is in a native anonymous subtree. + fn is_in_native_anonymous_subtree(&self) -> bool { false } @@ -794,7 +787,7 @@ pub trait TElement: /// element. fn rule_hash_target(&self) -> Self { if self.implemented_pseudo_element().is_some() { - self.closest_non_native_anonymous_ancestor() + self.pseudo_element_originating_element() .expect("Trying to collect rules for a detached pseudo-element") } else { *self diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 4b8148bb701..9ff92a19e16 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -588,6 +588,20 @@ impl<'le> fmt::Debug for GeckoElement<'le> { } impl<'le> GeckoElement<'le> { + #[inline] + fn closest_anon_subtree_root_parent(&self) -> Option { + debug_assert!(self.is_in_native_anonymous_subtree()); + let mut current = *self; + + loop { + if current.is_root_of_native_anonymous_subtree() { + return current.traversal_parent(); + } + + current = current.traversal_parent()?; + } + } + #[inline] fn may_have_anonymous_children(&self) -> bool { self.as_node() @@ -813,13 +827,6 @@ impl<'le> GeckoElement<'le> { return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0; } - /// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree. - #[inline] - fn is_in_native_anonymous_subtree(&self) -> bool { - use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; - self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0 - } - /// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree. #[inline] fn is_in_anonymous_subtree(&self) -> bool { @@ -1038,13 +1045,11 @@ impl<'le> TElement for GeckoElement<'le> { type TraversalChildrenIterator = GeckoChildrenIterator<'le>; fn inheritance_parent(&self) -> Option { - if self.is_native_anonymous() { - self.closest_non_native_anonymous_ancestor() - } else { - self.as_node() - .flattened_tree_parent() - .and_then(|n| n.as_element()) + if self.implemented_pseudo_element().is_some() { + return self.pseudo_element_originating_element() } + + self.as_node().flattened_tree_parent().and_then(|n| n.as_element()) } fn traversal_children(&self) -> LayoutIterator> { @@ -1174,19 +1179,6 @@ impl<'le> TElement for GeckoElement<'le> { unsafe { bindings::Gecko_DestroyAnonymousContentList(array) }; } - fn closest_non_native_anonymous_ancestor(&self) -> Option { - debug_assert!(self.is_native_anonymous()); - let mut parent = self.traversal_parent()?; - - loop { - if !parent.is_native_anonymous() { - return Some(parent); - } - - parent = parent.traversal_parent()?; - } - } - #[inline] fn as_node(&self) -> Self::ConcreteNode { unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) } @@ -1223,16 +1215,9 @@ impl<'le> TElement for GeckoElement<'le> { unsafe { let slots = self.extended_slots()?; - let base_declaration: &structs::DeclarationBlock = + let declaration: &structs::DeclarationBlock = slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?; - let declaration: &structs::ServoDeclarationBlock = mem::transmute(base_declaration); - - debug_assert_eq!( - &declaration._base as *const structs::DeclarationBlock, - base_declaration as *const structs::DeclarationBlock - ); - let raw: &structs::RawServoDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?; Some( @@ -1354,10 +1339,11 @@ impl<'le> TElement for GeckoElement<'le> { self.state().intersects(ElementState::IN_VISITED_STATE) } + /// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree. #[inline] - fn is_native_anonymous(&self) -> bool { - use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS; - self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0 + fn is_in_native_anonymous_subtree(&self) -> bool { + use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE; + self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0 } #[inline] @@ -1366,7 +1352,7 @@ impl<'le> TElement for GeckoElement<'le> { } fn implemented_pseudo_element(&self) -> Option { - if !self.is_native_anonymous() { + if !self.is_in_native_anonymous_subtree() { return None; } @@ -1915,7 +1901,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn pseudo_element_originating_element(&self) -> Option { debug_assert!(self.implemented_pseudo_element().is_some()); - self.closest_non_native_anonymous_ancestor() + let parent = self.closest_anon_subtree_root_parent()?; + + // FIXME(emilio): Special-case for s + // pseudo-elements, which are nested NAC. Probably nsNumberControlFrame + // should instead inherit from nsTextControlFrame, and then this could + // go away. + if let Some(PseudoElement::MozNumberText) = parent.implemented_pseudo_element() { + debug_assert_eq!( + self.implemented_pseudo_element().unwrap(), + PseudoElement::Placeholder, + "You added a new pseudo, do you really want this?" + ); + return parent.closest_anon_subtree_root_parent(); + } + + Some(parent) } #[inline] diff --git a/components/style/matching.rs b/components/style/matching.rs index 1d5cbf8d375..05630b07fb7 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -678,7 +678,7 @@ pub trait MatchMethods: TElement { let new_primary_style = data.styles.primary.as_ref().unwrap(); let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade; - if self.is_root() && !self.is_native_anonymous() { + if self.is_root() && !self.is_in_native_anonymous_subtree() { let device = context.shared.stylist.device(); let new_font_size = new_primary_style.get_font().clone_font_size(); diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 0263de83fef..a66058caf1e 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -1158,7 +1158,7 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { name: CowRcStr<'i>, input: &mut Parser<'i, 't>, ) -> Result> { - let id = match PropertyId::parse(&name) { + let id = match PropertyId::parse(&name, self.context) { Ok(id) => id, Err(..) => { return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index baa1ed015ee..4b902358866 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3515,77 +3515,27 @@ fn static_assert() { pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_SCROLL; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_TRANSFORM; - use properties::PropertyId; use properties::longhands::will_change::computed_value::T; - fn will_change_bitfield_from_prop_flags(prop: LonghandId) -> u8 { - use properties::PropertyFlags; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_ABSPOS_CB; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_FIXPOS_CB; - use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; - let servo_flags = prop.flags(); - let mut bitfield = 0; - - if servo_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) { - bitfield |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; - } - if servo_flags.contains(PropertyFlags::FIXPOS_CB) { - bitfield |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; - } - if servo_flags.contains(PropertyFlags::ABSPOS_CB) { - bitfield |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; - } - - bitfield as u8 - } - - self.gecko.mWillChangeBitField = 0; - match v { - T::AnimateableFeatures(features) => { + T::AnimateableFeatures { features, bits } => { unsafe { Gecko_ClearWillChange(&mut self.gecko, features.len()); } for feature in features.iter() { - if feature.0 == atom!("scroll-position") { - self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL as u8; - } else if feature.0 == atom!("opacity") { - self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY as u8; - } else if feature.0 == atom!("transform") { - self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM as u8; - } - unsafe { - Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr()); - } - - if let Ok(prop_id) = PropertyId::parse(&feature.0.to_string()) { - match prop_id.as_shorthand() { - Ok(shorthand) => { - for longhand in shorthand.longhands() { - self.gecko.mWillChangeBitField |= - will_change_bitfield_from_prop_flags(longhand); - } - }, - Err(longhand_or_custom) => { - if let PropertyDeclarationId::Longhand(longhand) - = longhand_or_custom { - self.gecko.mWillChangeBitField |= - will_change_bitfield_from_prop_flags(longhand); - } - }, - } + Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr()) } } + + self.gecko.mWillChangeBitField = bits.bits(); }, T::Auto => { unsafe { Gecko_ClearWillChange(&mut self.gecko, 0); } + self.gecko.mWillChangeBitField = 0; }, }; } @@ -3607,6 +3557,7 @@ fn static_assert() { use properties::longhands::will_change::computed_value::T; use gecko_bindings::structs::nsAtom; use values::CustomIdent; + use values::specified::box_::WillChangeBits; if self.gecko.mWillChange.len() == 0 { return T::Auto @@ -3618,7 +3569,10 @@ fn static_assert() { } }).collect(); - T::AnimateableFeatures(custom_idents.into_boxed_slice()) + T::AnimateableFeatures { + features: custom_idents.into_boxed_slice(), + bits: WillChangeBits::from_bits_truncate(self.gecko.mWillChangeBitField), + } } <% impl_shape_source("shape_outside", "mShapeOutside") %> @@ -3626,10 +3580,13 @@ fn static_assert() { pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) { use gecko_bindings::structs::NS_STYLE_CONTAIN_NONE; use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; + use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; + use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; + use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; use properties::longhands::contain::SpecifiedValue; if v.is_empty() { @@ -3641,6 +3598,10 @@ fn static_assert() { self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8; return; } + if v.contains(SpecifiedValue::CONTENT) { + self.gecko.mContain = (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS) as u8; + return; + } let mut bitfield = 0; if v.contains(SpecifiedValue::LAYOUT) { @@ -3652,36 +3613,57 @@ fn static_assert() { if v.contains(SpecifiedValue::PAINT) { bitfield |= NS_STYLE_CONTAIN_PAINT; } + if v.contains(SpecifiedValue::SIZE) { + bitfield |= NS_STYLE_CONTAIN_SIZE; + } self.gecko.mContain = bitfield as u8; } pub fn clone_contain(&self) -> longhands::contain::computed_value::T { use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; + use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; + use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; + use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; use properties::longhands::contain::{self, SpecifiedValue}; let mut servo_flags = contain::computed_value::T::empty(); let gecko_flags = self.gecko.mContain; - if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 && - gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8) != 0 { + if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 { + debug_assert_eq!( + gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8), + NS_STYLE_CONTAIN_ALL_BITS as u8, + "When strict is specified, ALL_BITS should be specified as well" + ); servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS); return servo_flags; } - + if gecko_flags & (NS_STYLE_CONTAIN_CONTENT as u8) != 0 { + debug_assert_eq!( + gecko_flags & (NS_STYLE_CONTAIN_CONTENT_BITS as u8), + NS_STYLE_CONTAIN_CONTENT_BITS as u8, + "When content is specified, CONTENT_BITS should be specified as well" + ); + servo_flags.insert(SpecifiedValue::CONTENT | SpecifiedValue::CONTENT_BITS); + return servo_flags; + } if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 { servo_flags.insert(SpecifiedValue::LAYOUT); } - if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0{ + if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0 { servo_flags.insert(SpecifiedValue::STYLE); } if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 { servo_flags.insert(SpecifiedValue::PAINT); } + if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 { + servo_flags.insert(SpecifiedValue::SIZE); + } return servo_flags; } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 1c282e46b51..f45b7ffe91e 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -2501,82 +2501,83 @@ impl Animate for ComputedScale { /// impl Animate for ComputedTransform { #[inline] - fn animate( - &self, - other_: &Self, - procedure: Procedure, - ) -> Result { - - let animate_equal_lists = |this: &[ComputedTransformOperation], - other: &[ComputedTransformOperation]| - -> Result { - Ok(Transform(this.iter().zip(other) - .map(|(this, other)| this.animate(other, procedure)) - .collect::, _>>()?)) - // If we can't animate for a pair of matched transform lists - // this means we have at least one undecomposable matrix, - // so we should bubble out Err here, and let the caller do - // the fallback procedure. - }; - if self.0.is_empty() && other_.0.is_empty() { - return Ok(Transform(vec![])); - } - - - let this = &self.0; - let other = &other_.0; + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + use std::borrow::Cow; if procedure == Procedure::Add { - let result = this.iter().chain(other).cloned().collect::>(); + let result = self.0.iter().chain(&other.0).cloned().collect::>(); return Ok(Transform(result)); } + // https://drafts.csswg.org/css-transforms-1/#transform-transform-neutral-extend-animation + fn match_operations_if_possible<'a>( + this: &mut Cow<'a, Vec>, + other: &mut Cow<'a, Vec>, + ) -> bool { + if !this.iter().zip(other.iter()).all(|(this, other)| is_matched_operation(this, other)) { + return false; + } - // For matched transform lists. - { if this.len() == other.len() { - let is_matched_transforms = this.iter().zip(other).all(|(this, other)| { - is_matched_operation(this, other) - }); - - if is_matched_transforms { - return animate_equal_lists(this, other); - } + return true; } + + let (shorter, longer) = + if this.len() < other.len() { + (this.to_mut(), other) + } else { + (other.to_mut(), this) + }; + + shorter.reserve(longer.len()); + for op in longer.iter().skip(shorter.len()) { + shorter.push(op.to_animated_zero().unwrap()); + } + + // The resulting operations won't be matched regardless if the + // extended component is already InterpolateMatrix / + // AccumulateMatrix. + // + // Otherwise they should be matching operations all the time. + let already_mismatched = matches!( + longer[0], + TransformOperation::InterpolateMatrix { .. } | + TransformOperation::AccumulateMatrix { .. } + ); + + debug_assert_eq!( + !already_mismatched, + longer.iter().zip(shorter.iter()).all(|(this, other)| is_matched_operation(this, other)), + "ToAnimatedZero should generate matched operations" + ); + + !already_mismatched } - // For mismatched transform lists. - let mut owned_this = this.clone(); - let mut owned_other = other.clone(); + let mut this = Cow::Borrowed(&self.0); + let mut other = Cow::Borrowed(&other.0); - if this.is_empty() { - let this = other_.to_animated_zero()?.0; - if this.iter().zip(other).all(|(this, other)| is_matched_operation(this, other)) { - return animate_equal_lists(&this, other) - } - owned_this = this; - } - if other.is_empty() { - let other = self.to_animated_zero()?.0; - if this.iter().zip(&other).all(|(this, other)| is_matched_operation(this, other)) { - return animate_equal_lists(this, &other) - } - owned_other = other; + if match_operations_if_possible(&mut this, &mut other) { + return Ok(Transform( + this.iter().zip(other.iter()) + .map(|(this, other)| this.animate(other, procedure)) + .collect::, _>>()? + )); } match procedure { Procedure::Add => Err(()), Procedure::Interpolate { progress } => { Ok(Transform(vec![TransformOperation::InterpolateMatrix { - from_list: Transform(owned_this), - to_list: Transform(owned_other), + from_list: Transform(this.into_owned()), + to_list: Transform(other.into_owned()), progress: Percentage(progress as f32), }])) }, Procedure::Accumulate { count } => { Ok(Transform(vec![TransformOperation::AccumulateMatrix { - from_list: Transform(owned_this), - to_list: Transform(owned_other), + from_list: Transform(this.into_owned()), + to_list: Transform(other.into_owned()), count: cmp::min(count, i32::max_value() as u64) as i32, }])) }, diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 026a3ca7936..f8389a19a79 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -593,7 +593,7 @@ ${helpers.predefined_type("contain", "specified::Contain::empty()", animation_value_type="discrete", products="gecko", - flags="FIXPOS_CB", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", gecko_pref="layout.css.contain.enabled", spec="https://drafts.csswg.org/css-contain/#contain-property")} diff --git a/components/style/properties/longhand/svg.mako.rs b/components/style/properties/longhand/svg.mako.rs index 2ec7bcc59a1..6d7bf803f1e 100644 --- a/components/style/properties/longhand/svg.mako.rs +++ b/components/style/properties/longhand/svg.mako.rs @@ -21,8 +21,8 @@ ${helpers.single_keyword("vector-effect", "none non-scaling-stroke", ${helpers.predefined_type( "stop-color", - "RGBAColor", - "RGBA::new(0, 0, 0, 255)", + "Color", + "RGBA::new(0, 0, 0, 255).into()", products="gecko", animation_value_type="AnimatedRGBA", spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty", @@ -37,10 +37,10 @@ ${helpers.predefined_type("stop-opacity", "Opacity", "1.0", ${helpers.predefined_type( "flood-color", - "RGBAColor", - "RGBA::new(0, 0, 0, 255)", + "Color", + "RGBA::new(0, 0, 0, 255).into()", products="gecko", - animation_value_type="AnimatedRGBA", + animation_value_type="AnimatedColor", spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty", )} @@ -50,10 +50,10 @@ ${helpers.predefined_type("flood-opacity", "Opacity", ${helpers.predefined_type( "lighting-color", - "RGBAColor", - "RGBA::new(255, 255, 255, 255)", + "Color", + "RGBA::new(255, 255, 255, 255).into()", products="gecko", - animation_value_type="AnimatedRGBA", + animation_value_type="AnimatedColor", spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty", )} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 81e9a09cbe8..b7bc96973b3 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1422,10 +1422,12 @@ impl UnparsedValue { .and_then(|css| { // As of this writing, only the base URL is used for property // values. + // + // FIXME(emilio): These bits are slightly fishy. let context = ParserContext::new( Origin::Author, &self.url_data, - None, + Some(CssRuleType::Style), ParsingMode::DEFAULT, quirks_mode, ); @@ -1595,7 +1597,7 @@ impl PropertyId { /// Returns a given property from the string `s`. /// /// Returns Err(()) for unknown non-custom properties. - pub fn parse(property_name: &str) -> Result { + fn parse_unchecked(property_name: &str) -> Result { // FIXME(https://github.com/rust-lang/rust/issues/33156): remove this // enum and use PropertyId when stable Rust allows destructors in // statics. @@ -1639,13 +1641,39 @@ impl PropertyId { PropertyId::ShorthandAlias(id, alias) }, None => { - return ::custom_properties::parse_name(property_name).map(|name| { - PropertyId::Custom(::custom_properties::Name::from(name)) - }) + let name = ::custom_properties::parse_name(property_name)?; + PropertyId::Custom(::custom_properties::Name::from(name)) }, }) } + /// Parses a property name, and returns an error if it's unknown or isn't + /// enabled for all content. + #[inline] + pub fn parse_enabled_for_all_content(name: &str) -> Result { + let id = Self::parse_unchecked(name)?; + + if !id.enabled_for_all_content() { + return Err(()); + } + + Ok(id) + } + + + /// Parses a property name, and returns an error if it's unknown or isn't + /// allowed in this context. + #[inline] + pub fn parse(name: &str, context: &ParserContext) -> Result { + let id = Self::parse_unchecked(name)?; + + if !id.allowed_in(context) { + return Err(()); + } + + Ok(id) + } + /// Returns a property id from Gecko's nsCSSPropertyID. #[cfg(feature = "gecko")] #[allow(non_upper_case_globals)] @@ -1972,12 +2000,7 @@ impl PropertyDeclaration { input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>> { assert!(declarations.is_empty()); - - if !id.allowed_in(context) { - return Err(input.new_custom_error( - StyleParseErrorKind::UnknownProperty(name) - )); - } + debug_assert!(id.allowed_in(context), "{:?}", id); let start = input.state(); match id { diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 4a9c6d5408c..314e9877083 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -565,7 +565,7 @@ impl StyleSharingCache { }, }; - if element.is_native_anonymous() { + if element.is_in_native_anonymous_subtree() { debug!("Failing to insert into the cache: NAC"); return; } @@ -656,7 +656,7 @@ impl StyleSharingCache { return None; } - if target.is_native_anonymous() { + if target.is_in_native_anonymous_subtree() { debug!("{:?} Cannot share style: NAC", target.element); return None; } @@ -681,7 +681,7 @@ impl StyleSharingCache { nth_index_cache: &mut NthIndexCache, selector_flags_map: &mut SelectorFlagsMap, ) -> Option { - debug_assert!(!target.is_native_anonymous()); + debug_assert!(!target.is_in_native_anonymous_subtree()); // Check that we have the same parent, or at least that the parents // share styles and permit sharing across their children. The latter diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index ab71cdb9bc0..b31beff7460 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -191,7 +191,9 @@ where // Before doing the cascade, check the sharing cache and see if we can // reuse the style via rule node identity. let may_reuse = - !self.element.is_native_anonymous() && parent_style.is_some() && inputs.rules.is_some(); + !self.element.is_in_native_anonymous_subtree() && + parent_style.is_some() && + inputs.rules.is_some(); if may_reuse { let cached = self.context.thread_local.sharing_cache.lookup_by_rules( diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs index 4de3acbaff5..afe797c12ff 100644 --- a/components/style/stylesheets/keyframes_rule.rs +++ b/components/style/stylesheets/keyframes_rule.rs @@ -620,9 +620,12 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> { name: CowRcStr<'i>, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>> { - let id = PropertyId::parse(&name).map_err(|()| { - input.new_custom_error(StyleParseErrorKind::UnknownProperty(name.clone())) - })?; + let id = match PropertyId::parse(&name, self.context) { + Ok(id) => id, + Err(()) => return Err(input.new_custom_error( + StyleParseErrorKind::UnknownProperty(name.clone()) + )), + }; // TODO(emilio): Shouldn't this use parse_entirely? PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input)?; diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index b3360fa41ab..2d6539c08b4 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -46,7 +46,7 @@ pub use self::media_rule::MediaRule; pub use self::namespace_rule::NamespaceRule; pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; pub use self::page_rule::PageRule; -pub use self::rule_parser::{State, TopLevelRuleParser}; +pub use self::rule_parser::{State, TopLevelRuleParser, InsertRuleContext}; pub use self::rule_list::{CssRules, CssRulesHelpers}; pub use self::rules_iterator::{AllRules, EffectiveRules}; pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator}; @@ -178,12 +178,6 @@ pub enum CssRuleType { Viewport = 15, } -#[allow(missing_docs)] -pub enum SingleRuleParseError { - Syntax, - Hierarchy, -} - #[allow(missing_docs)] pub enum RulesMutateError { Syntax, @@ -192,15 +186,6 @@ pub enum RulesMutateError { InvalidState, } -impl From for RulesMutateError { - fn from(other: SingleRuleParseError) -> Self { - match other { - SingleRuleParseError::Syntax => RulesMutateError::Syntax, - SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest, - } - } -} - impl CssRule { /// Returns the CSSOM rule type of this rule. pub fn rule_type(&self) -> CssRuleType { @@ -236,11 +221,12 @@ impl CssRule { /// Input state is None for a nested rule pub fn parse( css: &str, + insert_rule_context: InsertRuleContext, parent_stylesheet_contents: &StylesheetContents, shared_lock: &SharedRwLock, - state: Option, + state: State, loader: Option<&StylesheetLoader>, - ) -> Result<(Self, State), SingleRuleParseError> { + ) -> Result { let url_data = parent_stylesheet_contents.url_data.read(); let error_reporter = NullReporter; let context = ParserContext::new( @@ -257,28 +243,23 @@ impl CssRule { let mut guard = parent_stylesheet_contents.namespaces.write(); // nested rules are in the body state - let state = state.unwrap_or(State::Body); let mut rule_parser = TopLevelRuleParser { stylesheet_origin: parent_stylesheet_contents.origin, - context: context, + context, error_context: ParserErrorContext { error_reporter: &error_reporter, }, shared_lock: &shared_lock, - loader: loader, - state: state, - had_hierarchy_error: false, + loader, + state, + dom_error: None, namespaces: &mut *guard, + insert_rule_context: Some(insert_rule_context), }; parse_one_rule(&mut input, &mut rule_parser) - .map(|result| (result, rule_parser.state)) .map_err(|_| { - if rule_parser.take_had_hierarchy_error() { - SingleRuleParseError::Hierarchy - } else { - SingleRuleParseError::Syntax - } + rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax) }) } } diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs index 18acdd583bc..35edc22b3da 100644 --- a/components/style/stylesheets/rule_list.rs +++ b/components/style/stylesheets/rule_list.rs @@ -13,7 +13,7 @@ use std::fmt::{self, Write}; use str::CssStringWriter; use stylesheets::{CssRule, RulesMutateError}; use stylesheets::loader::StylesheetLoader; -use stylesheets::rule_parser::State; +use stylesheets::rule_parser::{InsertRuleContext, State}; use stylesheets::stylesheet::StylesheetContents; /// A list of CSS rules. @@ -141,7 +141,7 @@ impl CssRulesHelpers for RawOffsetArc> { nested: bool, loader: Option<&StylesheetLoader>, ) -> Result { - let state = { + let new_rule = { let read_guard = lock.read(); let rules = self.read_with(&read_guard); @@ -151,39 +151,33 @@ impl CssRulesHelpers for RawOffsetArc> { } // Computes the parser state at the given index - if nested { - None + let state = if nested { + State::Body } else if index == 0 { - Some(State::Start) + State::Start } else { - rules.0.get(index - 1).map(CssRule::rule_state) - } - }; + rules.0.get(index - 1).map(CssRule::rule_state).unwrap_or(State::Body) + }; - // Step 3, 4 - // XXXManishearth should we also store the namespace map? - let (new_rule, new_state) = - CssRule::parse(&rule, parent_stylesheet_contents, lock, state, loader)?; + let insert_rule_context = InsertRuleContext { + rule_list: &rules.0, + index, + }; + + // Steps 3, 4, 5, 6 + CssRule::parse( + &rule, + insert_rule_context, + parent_stylesheet_contents, + lock, + state, + loader, + )? + }; { let mut write_guard = lock.write(); let rules = self.write_with(&mut write_guard); - // Step 5 - // Computes the maximum allowed parser state at a given index. - let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state); - if new_state > rev_state { - // We inserted a rule too early, e.g. inserting - // a regular style rule before @namespace rules - return Err(RulesMutateError::HierarchyRequest); - } - - // Step 6 - if let CssRule::Namespace(..) = new_rule { - if !rules.only_ns_or_import() { - return Err(RulesMutateError::InvalidState); - } - } - rules.0.insert(index, new_rule.clone()); } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 93f0eaab650..a446068faac 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -19,7 +19,7 @@ use servo_arc::Arc; use shared_lock::{Locked, SharedRwLock}; use str::starts_with_ignore_ascii_case; use style_traits::{ParseError, StyleParseErrorKind}; -use stylesheets::{CssRule, CssRuleType, CssRules, Origin, StylesheetLoader}; +use stylesheets::{CssRule, CssRuleType, CssRules, Origin, RulesMutateError, StylesheetLoader}; use stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule}; use stylesheets::document_rule::DocumentCondition; @@ -31,6 +31,14 @@ use stylesheets::viewport_rule; use values::{CssUrl, CustomIdent, KeyframesName}; use values::computed::font::FamilyName; +/// The information we need particularly to do CSSOM insertRule stuff. +pub struct InsertRuleContext<'a> { + /// The rule list we're about to insert into. + pub rule_list: &'a [CssRule], + /// The index we're about to get inserted at. + pub index: usize, +} + /// The parser for the top-level rules in a stylesheet. pub struct TopLevelRuleParser<'a, R: 'a> { /// The origin of the stylesheet we're parsing. @@ -51,11 +59,13 @@ pub struct TopLevelRuleParser<'a, R: 'a> { /// Whether we have tried to parse was invalid due to being in the wrong /// place (e.g. an @import rule was found while in the `Body` state). Reset /// to `false` when `take_had_hierarchy_error` is called. - pub had_hierarchy_error: bool, + pub dom_error: Option, /// The namespace map we use for parsing. Needs to start as `Some()`, and /// will be taken out after parsing namespace rules, and that reference will /// be moved to `ParserContext`. pub namespaces: &'a mut Namespaces, + /// The info we need insert a rule in a list. + pub insert_rule_context: Option>, } impl<'b, R> TopLevelRuleParser<'b, R> { @@ -74,14 +84,43 @@ impl<'b, R> TopLevelRuleParser<'b, R> { self.state } - /// Returns whether we previously tried to parse a rule that was invalid - /// due to being in the wrong place (e.g. an @import rule was found after - /// a regular style rule). The state of this flag is reset when this - /// function is called. - pub fn take_had_hierarchy_error(&mut self) -> bool { - let had_hierarchy_error = self.had_hierarchy_error; - self.had_hierarchy_error = false; - had_hierarchy_error + /// Checks whether we can parse a rule that would transition us to + /// `new_state`. + /// + /// This is usually a simple branch, but we may need more bookkeeping if + /// doing `insertRule` from CSSOM. + fn check_state(&mut self, new_state: State) -> bool { + if self.state > new_state { + self.dom_error = Some(RulesMutateError::HierarchyRequest); + return false; + } + + let ctx = match self.insert_rule_context { + Some(ref ctx) => ctx, + None => return true, + }; + + let next_rule_state = match ctx.rule_list.get(ctx.index) { + None => return true, + Some(rule) => rule.rule_state(), + }; + + if new_state > next_rule_state { + self.dom_error = Some(RulesMutateError::HierarchyRequest); + return false; + } + + // If there's anything that isn't a namespace rule (or import rule, but + // we checked that already at the beginning), reject with a + // StateError. + if new_state == State::Namespaces && + ctx.rule_list[ctx.index..].iter().any(|r| !matches!(*r, CssRule::Namespace(..))) + { + self.dom_error = Some(RulesMutateError::InvalidState); + return false; + } + + true } } @@ -151,9 +190,7 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, let location = input.current_source_location(); match_ignore_ascii_case! { &*name, "import" => { - if self.state > State::Imports { - // "@import must be before any rule but @charset" - self.had_hierarchy_error = true; + if !self.check_state(State::Imports) { return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) } @@ -168,9 +205,7 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, return Ok(AtRuleType::WithoutBlock(prelude)); }, "namespace" => { - if self.state > State::Namespaces { - // "@namespace must be before any rule but @charset and @import" - self.had_hierarchy_error = true; + if !self.check_state(State::Namespaces) { return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule)) } @@ -190,12 +225,16 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet // anything left is invalid. "charset" => { - self.had_hierarchy_error = true; + self.dom_error = Some(RulesMutateError::HierarchyRequest); return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule)) } _ => {} } + if !self.check_state(State::Body) { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + AtRuleParser::parse_prelude(&mut self.nested(), name, input) } @@ -229,8 +268,8 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, self.state = State::Imports; CssRule::Import(import_rule) }, - AtRuleNonBlockPrelude::Namespace(prefix, url, location) => { - let opt_prefix = if let Some(prefix) = prefix { + AtRuleNonBlockPrelude::Namespace(prefix, url, source_location) => { + let prefix = if let Some(prefix) = prefix { self.namespaces.prefixes.insert(prefix.clone(), url.clone()); Some(prefix) } else { @@ -240,9 +279,9 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, self.state = State::Namespaces; CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule { - prefix: opt_prefix, - url: url, - source_location: location, + prefix, + url, + source_location, }))) }, } @@ -264,6 +303,10 @@ impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRulePars &mut self, input: &mut Parser<'i, 't>, ) -> Result> { + if !self.check_state(State::Body) { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + QualifiedRuleParser::parse_prelude(&mut self.nested(), input) } @@ -463,27 +506,27 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a ), ))) }, - AtRuleBlockPrelude::Media(media_queries, location) => { + AtRuleBlockPrelude::Media(media_queries, source_location) => { Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule { - media_queries: media_queries, + media_queries, rules: self.parse_nested_rules(input, CssRuleType::Media), - source_location: location, + source_location, })))) }, - AtRuleBlockPrelude::Supports(cond, location) => { + AtRuleBlockPrelude::Supports(condition, source_location) => { let eval_context = ParserContext::new_with_rule_type( self.context, CssRuleType::Style, self.namespaces, ); - let enabled = cond.eval(&eval_context); + let enabled = condition.eval(&eval_context); Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap( SupportsRule { - condition: cond, + condition, rules: self.parse_nested_rules(input, CssRuleType::Supports), - enabled: enabled, - source_location: location, + enabled, + source_location, }, )))) }, @@ -498,7 +541,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a ViewportRule::parse(&context, self.error_context, input)?, )))) }, - AtRuleBlockPrelude::Keyframes(name, prefix, location) => { + AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => { let context = ParserContext::new_with_rule_type( self.context, CssRuleType::Keyframes, @@ -507,19 +550,19 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap( KeyframesRule { - name: name, + name, keyframes: parse_keyframe_list( &context, self.error_context, input, self.shared_lock, ), - vendor_prefix: prefix, - source_location: location, + vendor_prefix, + source_location, }, )))) }, - AtRuleBlockPrelude::Page(location) => { + AtRuleBlockPrelude::Page(source_location) => { let context = ParserContext::new_with_rule_type( self.context, CssRuleType::Page, @@ -530,21 +573,20 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a parse_property_declaration_list(&context, self.error_context, input); Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { block: Arc::new(self.shared_lock.wrap(declarations)), - source_location: location, + source_location, })))) }, - AtRuleBlockPrelude::Document(cond, location) => { - if cfg!(feature = "gecko") { - Ok(CssRule::Document(Arc::new(self.shared_lock.wrap( - DocumentRule { - condition: cond, - rules: self.parse_nested_rules(input, CssRuleType::Document), - source_location: location, - }, - )))) - } else { + AtRuleBlockPrelude::Document(condition, source_location) => { + if !cfg!(feature = "gecko") { unreachable!() } + Ok(CssRule::Document(Arc::new(self.shared_lock.wrap( + DocumentRule { + condition, + rules: self.parse_nested_rules(input, CssRuleType::Document), + source_location, + }, + )))) }, } } @@ -565,13 +607,10 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for NestedRulePa url_data: Some(self.context.url_data), }; - let location = input.current_source_location(); + let source_location = input.current_source_location(); let selectors = SelectorList::parse(&selector_parser, input)?; - Ok(QualifiedRuleParserPrelude { - selectors: selectors, - source_location: location, - }) + Ok(QualifiedRuleParserPrelude { selectors, source_location, }) } fn parse_block<'t>( diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 08c2cb05f7a..b33875a149c 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -363,13 +363,14 @@ impl Stylesheet { let rule_parser = TopLevelRuleParser { stylesheet_origin: origin, - shared_lock: shared_lock, + shared_lock, loader: stylesheet_loader, - context: context, - error_context: error_context, + context, + error_context, state: State::Start, - had_hierarchy_error: false, - namespaces: namespaces, + dom_error: None, + insert_rule_context: None, + namespaces, }; { diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index d94a9a0202a..5e2739d153d 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -315,12 +315,12 @@ impl Declaration { let mut input = ParserInput::new(&self.0); let mut input = Parser::new(&mut input); - input - .parse_entirely(|input| -> Result<(), CssParseError<()>> { + input.parse_entirely(|input| -> Result<(), CssParseError<()>> { let prop = input.expect_ident_cloned().unwrap(); input.expect_colon().unwrap(); - let id = PropertyId::parse(&prop).map_err(|_| input.new_custom_error(()))?; + let id = PropertyId::parse(&prop, context) + .map_err(|_| input.new_custom_error(()))?; let mut declarations = SourcePropertyDeclaration::new(); input.parse_until_before(Delimiter::Bang, |input| { diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 7092101fff7..0bffd39819c 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -433,9 +433,10 @@ pub fn recalc_style_at( if compute_self { child_cascade_requirement = compute_style(traversal_data, context, element, data); - if element.is_native_anonymous() { - // We must always cascade native anonymous subtrees, since they inherit - // styles from their first non-NAC ancestor. + if element.is_in_native_anonymous_subtree() { + // We must always cascade native anonymous subtrees, since they + // may have pseudo-elements underneath that would inherit from the + // closest non-NAC ancestor instead of us. child_cascade_requirement = cmp::max( child_cascade_requirement, ChildCascadeRequirement::MustCascadeChildren, diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index d716e9e6798..dcce3d592ab 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -7,6 +7,7 @@ use Atom; use cssparser::Parser; use parser::{Parse, ParserContext}; +use properties::{LonghandId, PropertyId, PropertyFlags, PropertyDeclarationId}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; @@ -382,7 +383,15 @@ pub enum WillChange { Auto, /// #[css(comma)] - AnimateableFeatures(#[css(iterable)] Box<[CustomIdent]>), + AnimateableFeatures { + /// The features that are supposed to change. + #[css(iterable)] + features: Box<[CustomIdent]>, + /// A bitfield with the kind of change that the value will create, based + /// on the above field. + #[css(skip)] + bits: WillChangeBits, + }, } impl WillChange { @@ -393,10 +402,68 @@ impl WillChange { } } +bitflags! { + /// The change bits that we care about. + /// + /// These need to be in sync with NS_STYLE_WILL_CHANGE_*. + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] + pub struct WillChangeBits: u8 { + /// Whether the stacking context will change. + const STACKING_CONTEXT = 1 << 0; + /// Whether `transform` will change. + const TRANSFORM = 1 << 1; + /// Whether `scroll-position` will change. + const SCROLL = 1 << 2; + /// Whether `opacity` will change. + const OPACITY = 1 << 3; + /// Fixed pos containing block. + const FIXPOS_CB = 1 << 4; + /// Abs pos containing block. + const ABSPOS_CB = 1 << 5; + } +} + +fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { + let mut flags = match longhand { + LonghandId::Opacity => WillChangeBits::OPACITY, + LonghandId::Transform => WillChangeBits::TRANSFORM, + _ => WillChangeBits::empty(), + }; + + let property_flags = longhand.flags(); + if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) { + flags |= WillChangeBits::STACKING_CONTEXT; + } + if property_flags.contains(PropertyFlags::FIXPOS_CB) { + flags |= WillChangeBits::FIXPOS_CB; + } + if property_flags.contains(PropertyFlags::ABSPOS_CB) { + flags |= WillChangeBits::ABSPOS_CB; + } + flags +} + +fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits { + let id = match PropertyId::parse(ident, context) { + Ok(id) => id, + Err(..) => return WillChangeBits::empty(), + }; + + match id.as_shorthand() { + Ok(shorthand) => { + shorthand.longhands().fold(WillChangeBits::empty(), |flags, p| { + flags | change_bits_for_longhand(p) + }) + } + Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand), + Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(), + } +} + impl Parse for WillChange { /// auto | # fn parse<'i, 't>( - _context: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { if input @@ -406,18 +473,28 @@ impl Parse for WillChange { return Ok(WillChange::Auto); } + let mut bits = WillChangeBits::empty(); let custom_idents = input.parse_comma_separated(|i| { let location = i.current_source_location(); - CustomIdent::from_ident( + let parser_ident = i.expect_ident()?; + let ident = CustomIdent::from_ident( location, - i.expect_ident()?, + parser_ident, &["will-change", "none", "all", "auto"], - ) + )?; + + if ident.0 == atom!("scroll-position") { + bits |= WillChangeBits::SCROLL; + } else { + bits |= change_bits_for_maybe_property(&parser_ident, context); + } + Ok(ident) })?; - Ok(WillChange::AnimateableFeatures( - custom_idents.into_boxed_slice(), - )) + Ok(WillChange::AnimateableFeatures { + features: custom_idents.into_boxed_slice(), + bits, + }) } } @@ -525,19 +602,25 @@ pub fn assert_touch_action_matches() { bitflags! { #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] - #[value_info(other_values = "none,strict,layout,style,paint")] + #[value_info(other_values = "none,strict,content,size,layout,style,paint")] /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property pub struct Contain: u8 { + /// 'size' variant, turns on size containment + const SIZE = 0x01; /// `layout` variant, turns on layout containment - const LAYOUT = 0x01; + const LAYOUT = 0x02; /// `style` variant, turns on style containment - const STYLE = 0x02; + const STYLE = 0x04; /// `paint` variant, turns on paint containment - const PAINT = 0x04; + const PAINT = 0x08; /// `strict` variant, turns on all types of containment - const STRICT = 0x8; + const STRICT = 0x10; + /// 'content' variant, turns on style, layout, and paint containment + const CONTENT = 0x20; /// variant with all the bits that contain: strict turns on - const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits; + const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits; + /// variant with all the bits that contain: content turns on + const CONTENT_BITS = Contain::STYLE.bits | Contain::LAYOUT.bits | Contain::PAINT.bits; } } @@ -552,6 +635,9 @@ impl ToCss for Contain { if self.contains(Contain::STRICT) { return dest.write_str("strict"); } + if self.contains(Contain::CONTENT) { + return dest.write_str("content"); + } let mut has_any = false; macro_rules! maybe_write_value { @@ -565,6 +651,7 @@ impl ToCss for Contain { } }; } + maybe_write_value!(Contain::SIZE => "size"); maybe_write_value!(Contain::LAYOUT => "layout"); maybe_write_value!(Contain::STYLE => "style"); maybe_write_value!(Contain::PAINT => "paint"); @@ -583,10 +670,12 @@ impl Parse for Contain { let mut result = Contain::empty(); while let Ok(name) = input.try(|i| i.expect_ident_cloned()) { let flag = match_ignore_ascii_case! { &name, + "size" => Some(Contain::SIZE), "layout" => Some(Contain::LAYOUT), "style" => Some(Contain::STYLE), "paint" => Some(Contain::PAINT), "strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS), + "content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS), "none" if result.is_empty() => return Ok(result), _ => None };