diff --git a/Cargo.lock b/Cargo.lock index e9f1f372c5d..6fe4a8d20d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4569,7 +4569,6 @@ dependencies = [ "cssparser 0.25.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.21.0", "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 08fad71b4ea..8d32ec01520 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -57,10 +57,10 @@ use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect}; use style::properties::{style_structs, ComputedValues}; use style::servo::restyle_damage::ServoRestyleDamage; use style::values::computed::effects::SimpleShadow; -use style::values::computed::image::Image as ComputedImage; +use style::values::computed::image::{Image, ImageLayer}; use style::values::computed::{Gradient, LengthOrAuto}; use style::values::generics::background::BackgroundSize; -use style::values::generics::image::{GradientKind, Image, PaintWorklet}; +use style::values::generics::image::{GradientKind, PaintWorklet}; use style::values::specified::ui::CursorKind; use style::values::{Either, RGBA}; use style_traits::ToCss; @@ -726,9 +726,13 @@ impl Fragment { // http://www.w3.org/TR/CSS21/colors.html#background let background = style.get_background(); for (i, background_image) in background.background_image.0.iter().enumerate().rev() { + let background_image = match *background_image { + ImageLayer::None => continue, + ImageLayer::Image(ref image) => image, + }; + match *background_image { - Either::First(_) => {}, - Either::Second(Image::Gradient(ref gradient)) => { + Image::Gradient(ref gradient) => { self.build_display_list_for_background_gradient( state, display_list_section, @@ -738,7 +742,7 @@ impl Fragment { i, ); }, - Either::Second(Image::Url(ref image_url)) => { + Image::Url(ref image_url) => { if let Some(url) = image_url.url() { let webrender_image = state.layout_context.get_webrender_image_for_url( self.node, @@ -757,7 +761,7 @@ impl Fragment { } } }, - Either::Second(Image::PaintWorklet(ref paint_worklet)) => { + Image::PaintWorklet(ref paint_worklet) => { let bounding_box = self.border_box - style.logical_border_width(); let bounding_box_size = bounding_box.size.to_physical(style.writing_mode); let background_size = @@ -790,10 +794,10 @@ impl Fragment { ); } }, - Either::Second(Image::Rect(_)) => { + Image::Rect(_) => { // TODO: Implement `-moz-image-rect` }, - Either::Second(Image::Element(_)) => { + Image::Element(_) => { // TODO: Implement `-moz-element` }, } @@ -978,7 +982,7 @@ impl Fragment { }; DisplayItem::Gradient(CommonDisplayItem::with_data(base, item, stops)) }, - GradientKind::Radial(shape, center, _angle) => { + GradientKind::Radial(shape, center) => { let (gradient, stops) = gradient::radial( style, placement.tile_size, @@ -1115,7 +1119,7 @@ impl Fragment { let border_radius = border::radii(bounds, border_style_struct); let border_widths = border.to_physical(style.writing_mode); - if let Either::Second(ref image) = border_style_struct.border_image_source { + if let ImageLayer::Image(ref image) = border_style_struct.border_image_source { if self .build_display_list_for_border_image( state, @@ -1173,7 +1177,7 @@ impl Fragment { style: &ComputedValues, base: BaseDisplayItem, bounds: Rect, - image: &ComputedImage, + image: &Image, border_width: SideOffsets2D, ) -> Option<()> { let border_style_struct = style.get_border(); @@ -1227,7 +1231,7 @@ impl Fragment { stops = linear_stops; NinePatchBorderSource::Gradient(wr_gradient) }, - GradientKind::Radial(shape, center, _angle) => { + GradientKind::Radial(shape, center) => { let (wr_gradient, radial_stops) = gradient::radial( style, border_image_area, diff --git a/components/layout/display_list/gradient.rs b/components/layout/display_list/gradient.rs index 264bea7fbee..ab56b1d6c9c 100644 --- a/components/layout/display_list/gradient.rs +++ b/components/layout/display_list/gradient.rs @@ -9,7 +9,6 @@ use style::properties::ComputedValues; use style::values::computed::image::{EndingShape, LineDirection}; use style::values::computed::{Angle, GradientItem, LengthPercentage, Percentage, Position}; use style::values::generics::image::{Circle, ColorStop, Ellipse, ShapeExtent}; -use style::values::specified::position::{X, Y}; use webrender_api::{ExtendMode, Gradient, GradientBuilder, GradientStop, RadialGradient}; /// A helper data structure for gradients. @@ -227,15 +226,17 @@ pub fn linear( direction: LineDirection, repeating: bool, ) -> (Gradient, Vec) { + use style::values::specified::position::HorizontalPositionKeyword::*; + use style::values::specified::position::VerticalPositionKeyword::*; let angle = match direction { LineDirection::Angle(angle) => angle.radians(), LineDirection::Horizontal(x) => match x { - X::Left => Angle::from_degrees(270.).radians(), - X::Right => Angle::from_degrees(90.).radians(), + Left => Angle::from_degrees(270.).radians(), + Right => Angle::from_degrees(90.).radians(), }, LineDirection::Vertical(y) => match y { - Y::Top => Angle::from_degrees(0.).radians(), - Y::Bottom => Angle::from_degrees(180.).radians(), + Top => Angle::from_degrees(0.).radians(), + Bottom => Angle::from_degrees(180.).radians(), }, LineDirection::Corner(horizontal, vertical) => { // This the angle for one of the diagonals of the box. Our angle @@ -243,10 +244,10 @@ pub fn linear( // two perpendicular angles. let atan = (size.height.to_f32_px() / size.width.to_f32_px()).atan(); match (horizontal, vertical) { - (X::Right, Y::Bottom) => ::std::f32::consts::PI - atan, - (X::Left, Y::Bottom) => ::std::f32::consts::PI + atan, - (X::Right, Y::Top) => atan, - (X::Left, Y::Top) => -atan, + (Right, Bottom) => ::std::f32::consts::PI - atan, + (Left, Bottom) => ::std::f32::consts::PI + atan, + (Right, Top) => atan, + (Left, Top) => -atan, } }, }; diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index a0c1a8b7a61..35463f69715 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -510,6 +510,10 @@ impl<'le> TElement for ServoLayoutElement<'le> { *self.element.namespace() == ns!(svg) } + fn has_part_attr(&self) -> bool { + false + } + fn style_attribute(&self) -> Option>> { unsafe { (*self.element.style_attribute()) diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 764c7ce6051..1145cb0fd7e 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -132,8 +132,7 @@ use style::selector_parser::{ use style::shared_lock::{Locked, SharedRwLock}; use style::thread_state; use style::values::generics::NonNegative; -use style::values::{computed, specified}; -use style::values::{CSSFloat, Either}; +use style::values::{computed, specified, CSSFloat}; use style::CaseSensitivityExt; use xml5ever::serialize as xmlSerialize; use xml5ever::serialize::SerializeOpts as XmlSerializeOpts; @@ -684,7 +683,10 @@ impl LayoutElementHelpers for LayoutDom { hints.push(from_declaration( shared_lock, PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue( - vec![Either::Second(specified::Image::for_cascade(url.into()))].into(), + vec![specified::ImageLayer::Image(specified::Image::for_cascade( + url.into(), + ))] + .into(), )), )); } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index d64de8f3e5a..a49d3ec465a 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -237,10 +237,17 @@ partial interface CSSStyleDeclaration { [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString counter-reset; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow; + + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflowBlock; + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow-block; + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflowInline; + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow-inline; + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflowX; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow-x; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflowY; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow-y; + [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflowWrap; [CEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString overflow-wrap; diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index c3e733cbeb0..41b83b0c40e 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -96,18 +96,21 @@ impl SelectorBuilder { &mut self, parsed_pseudo: bool, parsed_slotted: bool, + parsed_part: bool, ) -> ThinArc> { // Compute the specificity and flags. - let mut spec = SpecificityAndFlags(specificity(self.simple_selectors.iter())); + let specificity = specificity(self.simple_selectors.iter()); + let mut flags = SelectorFlags::empty(); if parsed_pseudo { - spec.0 |= HAS_PSEUDO_BIT; + flags |= SelectorFlags::HAS_PSEUDO; } - if parsed_slotted { - spec.0 |= HAS_SLOTTED_BIT; + flags |= SelectorFlags::HAS_SLOTTED; } - - self.build_with_specificity_and_flags(spec) + if parsed_part { + flags |= SelectorFlags::HAS_PART; + } + self.build_with_specificity_and_flags(SpecificityAndFlags { specificity, flags }) } /// Builds with an explicit SpecificityAndFlags. This is separated from build() so @@ -188,28 +191,44 @@ fn split_from_end(s: &[T], at: usize) -> (&[T], &[T]) { s.split_at(s.len() - at) } -pub const HAS_PSEUDO_BIT: u32 = 1 << 30; -pub const HAS_SLOTTED_BIT: u32 = 1 << 31; +bitflags! { + /// Flags that indicate at which point of parsing a selector are we. + #[derive(Default, ToShmem)] + pub (crate) struct SelectorFlags : u8 { + const HAS_PSEUDO = 1 << 0; + const HAS_SLOTTED = 1 << 1; + const HAS_PART = 1 << 2; + } +} -/// We use ten bits for each specificity kind (id, class, element), and the two -/// high bits for the pseudo and slotted flags. #[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] -pub struct SpecificityAndFlags(pub u32); +pub struct SpecificityAndFlags { + /// There are two free bits here, since we use ten bits for each specificity + /// kind (id, class, element). + pub(crate) specificity: u32, + /// There's padding after this field due to the size of the flags. + pub(crate) flags: SelectorFlags, +} impl SpecificityAndFlags { #[inline] pub fn specificity(&self) -> u32 { - self.0 & !(HAS_PSEUDO_BIT | HAS_SLOTTED_BIT) + self.specificity } #[inline] pub fn has_pseudo_element(&self) -> bool { - (self.0 & HAS_PSEUDO_BIT) != 0 + self.flags.intersects(SelectorFlags::HAS_PSEUDO) } #[inline] pub fn is_slotted(&self) -> bool { - (self.0 & HAS_SLOTTED_BIT) != 0 + self.flags.intersects(SelectorFlags::HAS_SLOTTED) + } + + #[inline] + pub fn is_part(&self) -> bool { + self.flags.intersects(SelectorFlags::HAS_PART) } } diff --git a/components/selectors/context.rs b/components/selectors/context.rs index 3686513e79d..d159891ff02 100644 --- a/components/selectors/context.rs +++ b/components/selectors/context.rs @@ -279,13 +279,13 @@ where /// Runs F with a given shadow host which is the root of the tree whose /// rules we're matching. #[inline] - pub fn with_shadow_host(&mut self, host: E, f: F) -> R + pub fn with_shadow_host(&mut self, host: Option, f: F) -> R where E: Element, F: FnOnce(&mut Self) -> R, { let original_host = self.current_host.take(); - self.current_host = Some(host.opaque()); + self.current_host = host.map(|h| h.opaque()); let result = f(self); self.current_host = original_host; result diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index f049555730c..982dff793a8 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -6,7 +6,7 @@ use crate::attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace}; use crate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation}; use crate::attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE}; use crate::bloom::BLOOM_HASH_MASK; -use crate::builder::{SelectorBuilder, SpecificityAndFlags}; +use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags}; use crate::context::QuirksMode; use crate::sink::Push; pub use crate::visitor::{SelectorVisitor, Visit}; @@ -601,6 +601,36 @@ impl Selector { self.0.header.header.is_slotted() } + #[inline] + pub fn is_part(&self) -> bool { + self.0.header.header.is_part() + } + + #[inline] + pub fn part(&self) -> Option<&Impl::PartName> { + if !self.is_part() { + return None; + } + + let mut iter = self.iter(); + if self.has_pseudo_element() { + // Skip the pseudo-element. + for _ in &mut iter {} + + let combinator = iter.next_sequence()?; + debug_assert_eq!(combinator, Combinator::PseudoElement); + } + + for component in iter { + if let Component::Part(ref part) = *component { + return Some(part); + } + } + + debug_assert!(false, "is_part() lied somehow?"); + None + } + #[inline] pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> { if !self.has_pseudo_element() { @@ -720,7 +750,12 @@ impl Selector { } /// Creates a Selector from a vec of Components, specified in parse order. Used in tests. - pub fn from_vec(vec: Vec>, specificity_and_flags: u32) -> Self { + #[allow(unused)] + pub(crate) fn from_vec( + vec: Vec>, + specificity: u32, + flags: SelectorFlags, + ) -> Self { let mut builder = SelectorBuilder::default(); for component in vec.into_iter() { if let Some(combinator) = component.as_combinator() { @@ -729,7 +764,7 @@ impl Selector { builder.push_simple_selector(component); } } - let spec = SpecificityAndFlags(specificity_and_flags); + let spec = SpecificityAndFlags { specificity, flags }; Selector(builder.build_with_specificity_and_flags(spec)) } @@ -1445,6 +1480,7 @@ where let mut has_pseudo_element = false; let mut slotted = false; + let mut part = false; 'outer_loop: loop { // Parse a sequence of simple selectors. let state = match parse_compound_selector(parser, input, &mut builder)? { @@ -1461,7 +1497,7 @@ where if state.intersects(SelectorParsingState::AFTER_PSEUDO) { has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT); slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED); - let part = state.intersects(SelectorParsingState::AFTER_PART); + part = state.intersects(SelectorParsingState::AFTER_PART); debug_assert!(has_pseudo_element || slotted || part); break; } @@ -1500,9 +1536,7 @@ where builder.push_combinator(combinator); } - // TODO(emilio): We'll have to flag part() somehow as well, but we need more - // bits! - Ok(Selector(builder.build(has_pseudo_element, slotted))) + Ok(Selector(builder.build(has_pseudo_element, slotted, part))) } impl Selector { @@ -2249,7 +2283,7 @@ where #[cfg(test)] pub mod tests { use super::*; - use crate::builder::HAS_PSEUDO_BIT; + use crate::builder::SelectorFlags; use crate::parser; use cssparser::{serialize_identifier, Parser as CssParser, ParserInput, ToCss}; use std::collections::HashMap; @@ -2532,6 +2566,7 @@ pub mod tests { lower_name: DummyAtom::from("eeÉ"), })], specificity(0, 0, 1), + Default::default(), )])) ); assert_eq!( @@ -2545,6 +2580,7 @@ pub mod tests { }), ], specificity(0, 0, 1), + Default::default(), )])) ); // When the default namespace is not set, *| should be elided. @@ -2557,6 +2593,7 @@ pub mod tests { lower_name: DummyAtom::from("e"), })], specificity(0, 0, 1), + Default::default(), )])) ); // When the default namespace is set, *| should _not_ be elided (as foo @@ -2577,13 +2614,15 @@ pub mod tests { }), ], specificity(0, 0, 1), + Default::default(), )])) ); assert_eq!( parse("*"), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::ExplicitUniversalType], - specificity(0, 0, 0) + specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2594,13 +2633,15 @@ pub mod tests { Component::ExplicitUniversalType, ], specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( parse_expected("*|*", Some("*")), Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::ExplicitUniversalType], - specificity(0, 0, 0) + specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2614,6 +2655,7 @@ pub mod tests { Component::ExplicitUniversalType, ], specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2624,6 +2666,7 @@ pub mod tests { Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())), ], specificity(0, 2, 0), + Default::default(), )])) ); assert_eq!( @@ -2631,6 +2674,7 @@ pub mod tests { Ok(SelectorList::from_vec(vec![Selector::from_vec( vec![Component::ID(DummyAtom::from("bar"))], specificity(1, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2645,6 +2689,7 @@ pub mod tests { Component::ID(DummyAtom::from("bar")), ], specificity(1, 1, 1), + Default::default(), )])) ); assert_eq!( @@ -2660,6 +2705,7 @@ pub mod tests { Component::ID(DummyAtom::from("bar")), ], specificity(1, 1, 1), + Default::default(), )])) ); // Default namespace does not apply to attribute selectors @@ -2673,6 +2719,7 @@ pub mod tests { local_name_lower: DummyAtom::from("foo"), }], specificity(0, 1, 0), + Default::default(), )])) ); assert!(parse_ns("svg|circle", &parser).is_err()); @@ -2690,6 +2737,7 @@ pub mod tests { }), ], specificity(0, 0, 1), + Default::default(), )])) ); assert_eq!( @@ -2700,6 +2748,7 @@ pub mod tests { Component::ExplicitUniversalType, ], specificity(0, 0, 0), + Default::default(), )])) ); // Default namespace does not apply to attribute selectors @@ -2718,6 +2767,7 @@ pub mod tests { }, ], specificity(0, 1, 0), + Default::default(), )])) ); // Default namespace does apply to type selectors @@ -2732,6 +2782,7 @@ pub mod tests { }), ], specificity(0, 0, 1), + Default::default(), )])) ); assert_eq!( @@ -2742,6 +2793,7 @@ pub mod tests { Component::ExplicitUniversalType, ], specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2752,6 +2804,7 @@ pub mod tests { Component::ExplicitUniversalType, ], specificity(0, 0, 0), + Default::default(), )])) ); // Default namespace applies to universal and type selectors inside :not and :matches, @@ -2768,6 +2821,7 @@ pub mod tests { ), ], specificity(0, 1, 0), + Default::default(), )])) ); assert_eq!( @@ -2785,6 +2839,7 @@ pub mod tests { ), ], specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2805,6 +2860,7 @@ pub mod tests { ), ], specificity(0, 0, 1), + Default::default(), )])) ); assert_eq!( @@ -2818,6 +2874,7 @@ pub mod tests { case_sensitivity: ParsedCaseSensitivity::CaseSensitive, }], specificity(0, 1, 0), + Default::default(), )])) ); // https://github.com/mozilla/servo/issues/1723 @@ -2828,7 +2885,8 @@ pub mod tests { Component::Combinator(Combinator::PseudoElement), Component::PseudoElement(PseudoElement::Before), ], - specificity(0, 0, 1) | HAS_PSEUDO_BIT, + specificity(0, 0, 1), + SelectorFlags::HAS_PSEUDO, )])) ); assert_eq!( @@ -2839,7 +2897,8 @@ pub mod tests { Component::PseudoElement(PseudoElement::Before), Component::NonTSPseudoClass(PseudoClass::Hover), ], - specificity(0, 1, 1) | HAS_PSEUDO_BIT, + specificity(0, 1, 1), + SelectorFlags::HAS_PSEUDO, )])) ); assert_eq!( @@ -2851,7 +2910,8 @@ pub mod tests { Component::NonTSPseudoClass(PseudoClass::Hover), Component::NonTSPseudoClass(PseudoClass::Hover), ], - specificity(0, 2, 1) | HAS_PSEUDO_BIT, + specificity(0, 2, 1), + SelectorFlags::HAS_PSEUDO, )])) ); assert!(parse("::before:hover:lang(foo)").is_err()); @@ -2874,7 +2934,8 @@ pub mod tests { Component::Combinator(Combinator::PseudoElement), Component::PseudoElement(PseudoElement::After), ], - specificity(0, 0, 2) | HAS_PSEUDO_BIT, + specificity(0, 0, 2), + SelectorFlags::HAS_PSEUDO, )])) ); assert_eq!( @@ -2886,6 +2947,7 @@ pub mod tests { Component::Class(DummyAtom::from("ok")), ], (1 << 20) + (1 << 10) + (0 << 0), + Default::default(), )])) ); parser.default_ns = None; @@ -2901,6 +2963,7 @@ pub mod tests { .into(), )], specificity(1, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2918,6 +2981,7 @@ pub mod tests { .into(), )], specificity(0, 0, 1), + Default::default(), )])) ); // https://github.com/servo/servo/issues/16017 @@ -2930,6 +2994,7 @@ pub mod tests { .into(), )], specificity(0, 0, 0), + Default::default(), )])) ); assert_eq!( @@ -2944,6 +3009,7 @@ pub mod tests { .into(), )], specificity(0, 0, 0), + Default::default(), )])) ); // *| should be elided if there is no default namespace. @@ -2957,6 +3023,7 @@ pub mod tests { .into(), )], specificity(0, 0, 0), + Default::default(), )])) ); @@ -2972,6 +3039,7 @@ pub mod tests { .into(), )], specificity(0, 0, 0), + Default::default(), )])) ); diff --git a/components/style/dom.rs b/components/style/dom.rs index 04d6a79b384..7d56cc543e7 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -346,6 +346,14 @@ pub trait TShadowRoot: Sized + Copy + Clone + PartialEq { where Self: 'a; + /// Get the list of shadow parts for this shadow root. + fn parts<'a>(&self) -> &[::ConcreteElement] + where + Self: 'a, + { + &[] + } + /// Get a list of elements with a given ID in this shadow root, sorted by /// tree position. /// @@ -512,6 +520,9 @@ pub trait TElement: /// Whether this element has an attribute with a given namespace. fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool; + /// Returns whether this element has a `part` attribute. + fn has_part_attr(&self) -> bool; + /// The ID for this element. fn id(&self) -> Option<&WeakAtom>; @@ -520,6 +531,13 @@ pub trait TElement: where F: FnMut(&Atom); + /// Internal iterator for the part names of this element. + fn each_part(&self, _callback: F) + where + F: FnMut(&Atom), + { + } + /// Whether a given element may generate a pseudo-element. /// /// This is useful to avoid computing, for example, pseudo styles for diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 0be8a81e66a..bf1b93422f0 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -16,18 +16,16 @@ use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue, Matrix4x4Comp use crate::gecko_bindings::structs::{nsStyleImage, nsresult}; use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue}; use crate::stylesheets::RulesMutateError; -use crate::values::computed::image::LineDirection; use crate::values::computed::transform::Matrix3D; use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::{Angle, Gradient, Image}; use crate::values::computed::{Integer, LengthPercentage}; use crate::values::computed::{Length, Percentage, TextAlign}; use crate::values::generics::grid::{TrackListValue, TrackSize}; -use crate::values::generics::image::{CompatMode, Image as GenericImage}; +use crate::values::generics::image::GenericImage; use crate::values::generics::rect::Rect; use crate::Zero; use app_units::Au; -use std::f32::consts::PI; use style_traits::values::specified::AllowedNumericType; impl From for nsStyleCoord_CalcValue { @@ -64,63 +62,11 @@ impl From for CoordDataValue { } } -fn line_direction(horizontal: LengthPercentage, vertical: LengthPercentage) -> LineDirection { - use crate::values::computed::position::Position; - use crate::values::specified::position::{X, Y}; - - let horizontal_percentage = horizontal.as_percentage(); - let vertical_percentage = vertical.as_percentage(); - - let horizontal_as_corner = horizontal_percentage.and_then(|percentage| { - if percentage.0 == 0.0 { - Some(X::Left) - } else if percentage.0 == 1.0 { - Some(X::Right) - } else { - None - } - }); - - let vertical_as_corner = vertical_percentage.and_then(|percentage| { - if percentage.0 == 0.0 { - Some(Y::Top) - } else if percentage.0 == 1.0 { - Some(Y::Bottom) - } else { - None - } - }); - - if let (Some(hc), Some(vc)) = (horizontal_as_corner, vertical_as_corner) { - return LineDirection::Corner(hc, vc); - } - - if let Some(hc) = horizontal_as_corner { - if vertical_percentage == Some(Percentage(0.5)) { - return LineDirection::Horizontal(hc); - } - } - - if let Some(vc) = vertical_as_corner { - if horizontal_percentage == Some(Percentage(0.5)) { - return LineDirection::Vertical(vc); - } - } - - LineDirection::MozPosition( - Some(Position { - horizontal, - vertical, - }), - None, - ) -} - impl nsStyleImage { /// Set a given Servo `Image` value into this `nsStyleImage`. pub fn set(&mut self, image: Image) { match image { - GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient), + GenericImage::Gradient(boxed_gradient) => self.set_gradient(boxed_gradient), GenericImage::Url(ref url) => unsafe { bindings::Gecko_SetLayerImageImageValue(self, url); }, @@ -151,189 +97,9 @@ impl nsStyleImage { } } - // FIXME(emilio): This is really complex, we should use cbindgen for this. - fn set_gradient(&mut self, gradient: Gradient) { - use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER; - use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE; - use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; - use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE; - use crate::values::generics::image::{ - Circle, Ellipse, EndingShape, GradientKind, ShapeExtent, - }; - use crate::values::specified::position::{X, Y}; - - let stop_count = gradient.items.len(); - if stop_count >= ::std::u32::MAX as usize { - warn!("stylo: Prevented overflow due to too many gradient stops"); - return; - } - - let gecko_gradient = match gradient.kind { - GradientKind::Linear(direction) => { - let gecko_gradient = unsafe { - bindings::Gecko_CreateGradient( - structs::NS_STYLE_GRADIENT_SHAPE_LINEAR as u8, - structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as u8, - gradient.repeating, - gradient.compat_mode != CompatMode::Modern, - gradient.compat_mode == CompatMode::Moz, - stop_count as u32, - ) - }; - - match direction { - LineDirection::Angle(angle) => { - // PI radians (180deg) is ignored because it is the default value. - if angle.radians() != PI { - unsafe { - (*gecko_gradient).mAngle.set(angle); - } - } - }, - LineDirection::Horizontal(x) => { - let x = match x { - X::Left => 0.0, - X::Right => 1.0, - }; - - unsafe { - (*gecko_gradient) - .mBgPosX - .set_value(CoordDataValue::Percent(x)); - (*gecko_gradient) - .mBgPosY - .set_value(CoordDataValue::Percent(0.5)); - } - }, - LineDirection::Vertical(y) => { - // Although bottom is the default value, we can not ignore - // it here, because the rendering code of Gecko relies on - // this to behave correctly for legacy mode. - let y = match y { - Y::Top => 0.0, - Y::Bottom => 1.0, - }; - unsafe { - (*gecko_gradient) - .mBgPosX - .set_value(CoordDataValue::Percent(0.5)); - (*gecko_gradient) - .mBgPosY - .set_value(CoordDataValue::Percent(y)); - } - }, - LineDirection::Corner(horiz, vert) => { - let percent_x = match horiz { - X::Left => 0.0, - X::Right => 1.0, - }; - let percent_y = match vert { - Y::Top => 0.0, - Y::Bottom => 1.0, - }; - - unsafe { - (*gecko_gradient) - .mBgPosX - .set_value(CoordDataValue::Percent(percent_x)); - (*gecko_gradient) - .mBgPosY - .set_value(CoordDataValue::Percent(percent_y)); - } - }, - #[cfg(feature = "gecko")] - LineDirection::MozPosition(position, angle) => unsafe { - if let Some(position) = position { - (*gecko_gradient).mBgPosX.set(position.horizontal); - (*gecko_gradient).mBgPosY.set(position.vertical); - } - if let Some(angle) = angle { - (*gecko_gradient).mAngle.set(angle); - } - }, - } - gecko_gradient - }, - GradientKind::Radial(shape, position, angle) => { - let keyword_to_gecko_size = |keyword| match keyword { - ShapeExtent::ClosestSide => CLOSEST_SIDE, - ShapeExtent::FarthestSide => FARTHEST_SIDE, - ShapeExtent::ClosestCorner => CLOSEST_CORNER, - ShapeExtent::FarthestCorner => FARTHEST_CORNER, - ShapeExtent::Contain => CLOSEST_SIDE, - ShapeExtent::Cover => FARTHEST_CORNER, - }; - let (gecko_shape, gecko_size) = match shape { - EndingShape::Circle(ref circle) => { - let size = match *circle { - Circle::Extent(extent) => keyword_to_gecko_size(extent), - _ => structs::NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, - }; - (structs::NS_STYLE_GRADIENT_SHAPE_CIRCULAR as u8, size as u8) - }, - EndingShape::Ellipse(ref ellipse) => { - let size = match *ellipse { - Ellipse::Extent(extent) => keyword_to_gecko_size(extent), - _ => structs::NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE, - }; - ( - structs::NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL as u8, - size as u8, - ) - }, - }; - - let gecko_gradient = unsafe { - bindings::Gecko_CreateGradient( - gecko_shape, - gecko_size, - gradient.repeating, - gradient.compat_mode == CompatMode::Moz, - gradient.compat_mode == CompatMode::Moz, - stop_count as u32, - ) - }; - - // Clear mBgPos field and set mAngle if angle is set. Otherwise clear it. - unsafe { - if let Some(angle) = angle { - (*gecko_gradient).mAngle.set(angle); - } - } - - // Setting radius values depending shape - match shape { - EndingShape::Circle(Circle::Radius(length)) => unsafe { - let au = length.to_i32_au(); - (*gecko_gradient) - .mRadiusX - .set_value(CoordDataValue::Coord(au)); - (*gecko_gradient) - .mRadiusY - .set_value(CoordDataValue::Coord(au)); - }, - EndingShape::Ellipse(Ellipse::Radii(x, y)) => unsafe { - (*gecko_gradient).mRadiusX.set(x); - (*gecko_gradient).mRadiusY.set(y); - }, - _ => {}, - } - unsafe { - (*gecko_gradient).mBgPosX.set(position.horizontal); - (*gecko_gradient).mBgPosY.set(position.vertical); - } - - gecko_gradient - }, - }; - - for (index, item) in gradient.items.into_iter().enumerate() { - let gecko_stop = unsafe { &mut (*gecko_gradient).mStops[index] }; - *gecko_stop = item; - } - + fn set_gradient(&mut self, gradient: Box) { unsafe { - bindings::Gecko_SetGradientImageValue(self, gecko_gradient); + bindings::Gecko_SetGradientImageValue(self, Box::into_raw(gradient)); } } @@ -376,7 +142,8 @@ impl nsStyleImage { } }, nsStyleImageType::eStyleImageType_Gradient => { - Some(GenericImage::Gradient(self.get_gradient())) + let gradient: &Gradient = &**self.__bindgen_anon_1.mGradient.as_ref(); + Some(GenericImage::Gradient(Box::new(gradient.clone()))) }, nsStyleImageType::eStyleImageType_Element => { use crate::gecko_string_cache::Atom; @@ -392,141 +159,6 @@ impl nsStyleImage { .expect("Null image request?"); ComputedImageUrl::from_image_request(image_request) } - - unsafe fn get_gradient(self: &nsStyleImage) -> Box { - use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER; - use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE; - use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; - use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE; - use crate::values::computed::position::Position; - use crate::values::generics::image::{Circle, Ellipse}; - use crate::values::generics::image::{EndingShape, GradientKind, ShapeExtent}; - - let gecko_gradient = bindings::Gecko_GetGradientImageValue(self) - .as_ref() - .unwrap(); - let angle = Angle::from_gecko_style_coord(&gecko_gradient.mAngle); - let horizontal_style = LengthPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosX); - let vertical_style = LengthPercentage::from_gecko_style_coord(&gecko_gradient.mBgPosY); - - let kind = match gecko_gradient.mShape as u32 { - structs::NS_STYLE_GRADIENT_SHAPE_LINEAR => { - let line_direction = match (angle, horizontal_style, vertical_style) { - (Some(a), None, None) => LineDirection::Angle(a), - (None, Some(horizontal), Some(vertical)) => { - line_direction(horizontal, vertical) - }, - (Some(_), Some(horizontal), Some(vertical)) => LineDirection::MozPosition( - Some(Position { - horizontal, - vertical, - }), - angle, - ), - _ => { - debug_assert!( - horizontal_style.is_none() && vertical_style.is_none(), - "Unexpected linear gradient direction" - ); - LineDirection::MozPosition(None, None) - }, - }; - GradientKind::Linear(line_direction) - }, - _ => { - let gecko_size_to_keyword = |gecko_size| { - match gecko_size { - CLOSEST_SIDE => ShapeExtent::ClosestSide, - FARTHEST_SIDE => ShapeExtent::FarthestSide, - CLOSEST_CORNER => ShapeExtent::ClosestCorner, - FARTHEST_CORNER => ShapeExtent::FarthestCorner, - // FIXME: We should support ShapeExtent::Contain and ShapeExtent::Cover. - // But we can't choose those yet since Gecko does not support both values. - // https://bugzilla.mozilla.org/show_bug.cgi?id=1217664 - _ => panic!("Found unexpected gecko_size"), - } - }; - - let shape = match gecko_gradient.mShape as u32 { - structs::NS_STYLE_GRADIENT_SHAPE_CIRCULAR => { - let circle = match gecko_gradient.mSize as u32 { - structs::NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => { - let radius = - Length::from_gecko_style_coord(&gecko_gradient.mRadiusX) - .expect("mRadiusX could not convert to Length"); - debug_assert_eq!( - radius, - Length::from_gecko_style_coord(&gecko_gradient.mRadiusY) - .unwrap() - ); - Circle::Radius(radius) - }, - size => Circle::Extent(gecko_size_to_keyword(size)), - }; - EndingShape::Circle(circle) - }, - structs::NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL => { - let length_percentage_keyword = match gecko_gradient.mSize as u32 { - structs::NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE => match ( - LengthPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusX), - LengthPercentage::from_gecko_style_coord(&gecko_gradient.mRadiusY), - ) { - (Some(x), Some(y)) => Ellipse::Radii(x, y), - _ => { - debug_assert!( - false, - "mRadiusX, mRadiusY could not convert to LengthPercentage" - ); - Ellipse::Radii( - LengthPercentage::zero(), - LengthPercentage::zero(), - ) - }, - }, - size => Ellipse::Extent(gecko_size_to_keyword(size)), - }; - EndingShape::Ellipse(length_percentage_keyword) - }, - _ => panic!("Found unexpected mShape"), - }; - - let position = match (horizontal_style, vertical_style) { - (Some(horizontal), Some(vertical)) => Position { - horizontal, - vertical, - }, - _ => { - debug_assert!( - false, - "mRadiusX, mRadiusY could not convert to LengthPercentage" - ); - Position { - horizontal: LengthPercentage::zero(), - vertical: LengthPercentage::zero(), - } - }, - }; - - GradientKind::Radial(shape, position, angle) - }, - }; - - let items = gecko_gradient.mStops.iter().cloned().collect(); - let compat_mode = if gecko_gradient.mMozLegacySyntax { - CompatMode::Moz - } else if gecko_gradient.mLegacySyntax { - CompatMode::WebKit - } else { - CompatMode::Modern - }; - - Box::new(Gradient { - items, - repeating: gecko_gradient.mRepeating, - kind, - compat_mode, - }) - } } pub mod basic_shape { diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 7538b785858..dffd5acf086 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -116,6 +116,12 @@ impl PseudoElement { *self == PseudoElement::Marker } + /// Whether this pseudo-element is the ::selection pseudo. + #[inline] + pub fn is_selection(&self) -> bool { + *self == PseudoElement::Selection + } + /// Whether this pseudo-element is ::first-letter. #[inline] pub fn is_first_letter(&self) -> bool { diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 18718446c1a..fc27e1a8610 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -5,7 +5,7 @@ //! Gecko-specific bits for selector-parsing. use crate::element_state::{DocumentState, ElementState}; -use crate::gecko_bindings::structs::RawServoSelectorList; +use crate::gecko_bindings::structs::{self, RawServoSelectorList}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::invalidation::element::document_state::InvalidationMatchingData; use crate::selector_parser::{Direction, SelectorParser}; @@ -349,7 +349,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { #[inline] fn parse_host(&self) -> bool { - self.parse_slotted() + true + } + + #[inline] + fn parse_part(&self) -> bool { + self.chrome_rules_enabled() || + unsafe { structs::StaticPrefs_sVarCache_layout_css_shadow_parts_enabled } } fn parse_non_ts_pseudo_class( diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index d294939baeb..cffb78d3e9e 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -211,7 +211,7 @@ impl ElementSnapshot for GeckoElementSnapshot { return; } - snapshot_helpers::each_class(&self.mClass, callback) + snapshot_helpers::each_class_or_part(&self.mClass, callback) } #[inline] diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs index aaa2254dbd7..b8b31bc87dd 100644 --- a/components/style/gecko/snapshot_helpers.rs +++ b/components/style/gecko/snapshot_helpers.rs @@ -107,9 +107,9 @@ pub fn has_class_or_part( } /// Given an item, a callback, and a getter, execute `callback` for each class -/// this `item` has. +/// or part name this `item` has. #[inline(always)] -pub fn each_class(attr: &structs::nsAttrValue, mut callback: F) +pub fn each_class_or_part(attr: &structs::nsAttrValue, mut callback: F) where F: FnMut(&Atom), { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 3ce9e6fe6ab..39e3678eebc 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -185,6 +185,21 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr()) })) } + + #[inline] + fn parts<'a>(&self) -> &[::ConcreteElement] + where + Self: 'a, + { + let slice: &[*const RawGeckoElement] = &*self.0.mParts; + + #[allow(dead_code)] + unsafe fn static_assert() { + mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _); + } + + unsafe { mem::transmute(slice) } + } } /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer. @@ -575,6 +590,9 @@ impl<'le> GeckoElement<'le> { #[inline(always)] fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { + if !self.has_part_attr() { + return None; + } snapshot_helpers::find_attr(self.attrs(), &atom!("part")) } @@ -1330,6 +1348,12 @@ impl<'le> TElement for GeckoElement<'le> { unsafe { bindings::Gecko_HasAttr(self.0, namespace.0.as_ptr(), attr.as_ptr()) } } + #[inline] + fn has_part_attr(&self) -> bool { + self.as_node() + .get_bool_flag(nsINode_BooleanFlag::ElementHasPart) + } + // FIXME(emilio): we should probably just return a reference to the Atom. #[inline] fn id(&self) -> Option<&WeakAtom> { @@ -1349,7 +1373,19 @@ impl<'le> TElement for GeckoElement<'le> { None => return, }; - snapshot_helpers::each_class(attr, callback) + snapshot_helpers::each_class_or_part(attr, callback) + } + + fn each_part(&self, callback: F) + where + F: FnMut(&Atom), + { + let attr = match self.get_part_attr() { + Some(c) => c, + None => return, + }; + + snapshot_helpers::each_class_or_part(attr, callback) } #[inline] diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index c5246733976..a141aef4e8b 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -308,11 +308,6 @@ impl_threadsafe_refcount!( bindings::Gecko_AddRefnsIURIArbitraryThread, bindings::Gecko_ReleasensIURIArbitraryThread ); -impl_threadsafe_refcount!( - structs::mozilla::css::GridTemplateAreasValue, - bindings::Gecko_AddRefGridTemplateAreasValueArbitraryThread, - bindings::Gecko_ReleaseGridTemplateAreasValueArbitraryThread -); impl_threadsafe_refcount!( structs::SharedFontList, bindings::Gecko_AddRefSharedFontListArbitraryThread, @@ -328,6 +323,7 @@ impl_threadsafe_refcount!( unsafe fn addref_atom(atom: *mut structs::nsAtom) { mem::forget(Atom::from_raw(atom)); } + #[inline] unsafe fn release_atom(atom: *mut structs::nsAtom) { let _ = Atom::from_addrefed(atom); diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index 63d6eb6acc4..e0b9cf8d747 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -66,6 +66,8 @@ pub enum DependencyInvalidationKind { Siblings, /// This dependency may affect slotted elements of the element that changed. SlottedElements, + /// This dependency may affect parts of the element that changed. + Parts, } impl Dependency { @@ -98,7 +100,7 @@ impl Dependency { // an eager pseudo, and return only Descendants here if not. Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants, Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements, - Some(Combinator::Part) => unimplemented!("Need to add invalidation for shadow parts"), + Some(Combinator::Part) => DependencyInvalidationKind::Parts, } } } diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index ec1548f4aa3..2889c3e7b9d 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -72,11 +72,15 @@ pub struct DescendantInvalidationLists<'a> { pub dom_descendants: InvalidationVector<'a>, /// Invalidations for slotted children of an element. pub slotted_descendants: InvalidationVector<'a>, + /// Invalidations for ::part()s of an element. + pub parts: InvalidationVector<'a>, } impl<'a> DescendantInvalidationLists<'a> { fn is_empty(&self) -> bool { - self.dom_descendants.is_empty() && self.slotted_descendants.is_empty() + self.dom_descendants.is_empty() && + self.slotted_descendants.is_empty() && + self.parts.is_empty() } } @@ -104,6 +108,8 @@ enum DescendantInvalidationKind { Dom, /// A ::slotted() descendant invalidation. Slotted, + /// A ::part() descendant invalidation. + Part, } /// The kind of invalidation we're processing. @@ -174,9 +180,7 @@ impl<'a> Invalidation<'a> { Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => { InvalidationKind::Descendant(DescendantInvalidationKind::Dom) }, - Combinator::Part => { - unimplemented!("Need to add invalidation for shadow parts"); - }, + Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part), Combinator::SlotAssignment => { InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) }, @@ -472,6 +476,35 @@ where any_descendant } + fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool { + if invalidations.is_empty() { + return false; + } + + let shadow = match self.element.shadow_root() { + Some(s) => s, + None => return false, + }; + + 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 + } + fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool { if invalidations.is_empty() { return false; @@ -598,6 +631,7 @@ where any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants); any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants); + any_descendant |= self.invalidate_parts(&invalidations.parts); any_descendant } @@ -672,7 +706,7 @@ where debug_assert_eq!( descendant_invalidation_kind, DescendantInvalidationKind::Dom, - "Slotted invalidations don't propagate." + "Slotted or part invalidations don't propagate." ); descendant_invalidations.dom_descendants.push(invalidation); } @@ -769,6 +803,19 @@ where if pseudo.is_marker() && self.element.marker_pseudo_element().is_none() { invalidated_self = true; } + + // FIXME: ::selection doesn't generate elements, so the + // regular invalidation doesn't work for it. We store + // the cached selection style holding off the originating + // element, so we need to restyle it in order to invalidate + // it. This is still not quite correct, since nothing + // triggers a repaint necessarily, but matches old Gecko + // behavior, and the ::selection implementation needs to + // change significantly anyway to implement + // https://github.com/w3c/csswg-drafts/issues/2474. + if pseudo.is_selection() { + invalidated_self = true; + } } } @@ -860,6 +907,9 @@ where .dom_descendants .push(next_invalidation); }, + InvalidationKind::Descendant(DescendantInvalidationKind::Part) => { + descendant_invalidations.parts.push(next_invalidation); + }, InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => { descendant_invalidations .slotted_descendants diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index a49bb6306c5..eccc5f60bbf 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -472,6 +472,9 @@ where DependencyInvalidationKind::Siblings => { self.sibling_invalidations.push(invalidation); }, + DependencyInvalidationKind::Parts => { + self.descendant_invalidations.parts.push(invalidation); + }, DependencyInvalidationKind::SlottedElements => { self.descendant_invalidations .slotted_descendants @@ -486,6 +489,7 @@ where match dependency.invalidation_kind() { DependencyInvalidationKind::Element => !self.invalidates_self, DependencyInvalidationKind::SlottedElements => self.element.is_html_slot_element(), + DependencyInvalidationKind::Parts => self.element.shadow_root().is_some(), DependencyInvalidationKind::ElementAndDescendants | DependencyInvalidationKind::Siblings | DependencyInvalidationKind::Descendants => true, diff --git a/components/style/media_queries/media_feature_expression.rs b/components/style/media_queries/media_feature_expression.rs index 3f8eefbfab5..5b5c4f4fad7 100644 --- a/components/style/media_queries/media_feature_expression.rs +++ b/components/style/media_queries/media_feature_expression.rs @@ -298,9 +298,7 @@ impl MediaFeatureExpression { #[cfg(feature = "gecko")] { - if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } && - starts_with_ignore_ascii_case(feature_name, "-webkit-") - { + if starts_with_ignore_ascii_case(feature_name, "-webkit-") { feature_name = &feature_name[8..]; requirements.insert(ParsingRequirements::WEBKIT_PREFIX); if unsafe { diff --git a/components/style/properties/data.py b/components/style/properties/data.py index e771607b6d9..13496104a04 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -10,11 +10,14 @@ PHYSICAL_SIZES = ["width", "height"] LOGICAL_SIZES = ["block-size", "inline-size"] PHYSICAL_CORNERS = ["top-left", "top-right", "bottom-right", "bottom-left"] LOGICAL_CORNERS = ["start-start", "start-end", "end-start", "end-end"] +PHYSICAL_AXES = ["x", "y"] +LOGICAL_AXES = ["inline", "block"] # bool is True when logical ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES] ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [(size, True) for size in LOGICAL_SIZES] ALL_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [(corner, True) for corner in LOGICAL_CORNERS] +ALL_AXES = [(axis, False) for axis in PHYSICAL_AXES] + [(axis, True) for axis in LOGICAL_AXES] SYSTEM_FONT_LONGHANDS = """font_family font_size font_style font_variant_caps font_stretch font_kerning @@ -159,12 +162,14 @@ def parse_property_aliases(alias_list): if alias_list: for alias in alias_list.split(): (name, _, pref) = alias.partition(":") - if name.startswith("-webkit-") and not pref: - pref = "layout.css.prefixes.webkit" result.append((name, pref)) return result +def to_phys(name, logical, physical): + return name.replace(logical, physical).replace("inset-", "") + + class Longhand(object): def __init__(self, style_struct, name, spec=None, animation_value_type=None, keyword=None, predefined_type=None, servo_pref=None, gecko_pref=None, @@ -243,16 +248,16 @@ class Longhand(object): # property names corresponding to it. def all_physical_mapped_properties(self): assert self.logical - logical_side = None - for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS: - if s in self.name: - assert not logical_side - logical_side = s - assert logical_side + 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) + logical_side = candidates[0] + physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES \ else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \ + else PHYSICAL_AXES if logical_side in LOGICAL_AXES \ else LOGICAL_CORNERS - return [self.name.replace(logical_side, physical_side).replace("inset-", "") + return [to_phys(self.name, logical_side, physical_side) for physical_side in physical] def experimental(self, product): @@ -542,10 +547,6 @@ class PropertiesData(object): # See servo/servo#14941. if self.product == "gecko": for (prefix, pref) in property.extra_prefixes: - # All webkit prefixed properties are currently under - # control of this pref in Gecko currently. - if prefix == "webkit" and not pref: - pref = "layout.css.prefixes.webkit" property.alias.append(('-%s-%s' % (prefix, property.name), pref)) def declare_longhand(self, name, products="gecko servo", **kwargs): diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 9f56c42cc2b..fe0a1935e97 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -55,6 +55,7 @@ use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::BorderStyle; use crate::values::computed::font::FontSize; use crate::values::generics::column::ColumnCount; +use crate::values::generics::image::ImageLayer; use crate::values::generics::transform::TransformStyle; use crate::values::generics::url::UrlOrNone; @@ -982,7 +983,7 @@ fn static_assert() { Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); } - if let Either::Second(image) = image { + if let ImageLayer::Image(image) = image { self.gecko.mBorderImageSource.set(image); } } @@ -999,11 +1000,9 @@ fn static_assert() { } pub fn clone_border_image_source(&self) -> longhands::border_image_source::computed_value::T { - use crate::values::None_; - match unsafe { self.gecko.mBorderImageSource.into_image() } { - Some(image) => Either::Second(image), - None => Either::First(None_), + Some(image) => ImageLayer::Image(image), + None => ImageLayer::None, } } @@ -1084,8 +1083,8 @@ fn static_assert() { align-content justify-content align-self justify-self align-items justify-items grid-auto-rows grid-auto-columns - grid-auto-flow grid-template-areas - grid-template-rows grid-template-columns"> + grid-auto-flow grid-template-rows + grid-template-columns"> % for side in SIDES: <% impl_split_style_coord(side.ident, "mOffset", side.index) %> % endfor @@ -1137,20 +1136,29 @@ fn static_assert() { pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) { use crate::gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine}; - let ident = v.ident.as_ref().map_or(&[] as &[_], |ident| ident.0.as_slice()); - self.gecko.${value.gecko}.mLineName.assign(ident); - self.gecko.${value.gecko}.mHasSpan = v.is_span; + let line = &mut self.gecko.${value.gecko}; + line.mLineName.set_move(unsafe { + RefPtr::from_addrefed(match v.ident { + Some(i) => i.0, + None => atom!(""), + }.into_addrefed()) + }); + line.mHasSpan = v.is_span; if let Some(integer) = v.line_num { // clamping the integer between a range - self.gecko.${value.gecko}.mInteger = cmp::max(nsStyleGridLine_kMinLine, - cmp::min(integer, nsStyleGridLine_kMaxLine)); + line.mInteger = cmp::max( + nsStyleGridLine_kMinLine, + cmp::min(integer, nsStyleGridLine_kMaxLine), + ); } } pub fn copy_${value.name}_from(&mut self, other: &Self) { self.gecko.${value.gecko}.mHasSpan = other.gecko.${value.gecko}.mHasSpan; self.gecko.${value.gecko}.mInteger = other.gecko.${value.gecko}.mInteger; - self.gecko.${value.gecko}.mLineName.assign(&*other.gecko.${value.gecko}.mLineName); + unsafe { + self.gecko.${value.gecko}.mLineName.set(&other.gecko.${value.gecko}.mLineName); + } } pub fn reset_${value.name}(&mut self, other: &Self) { @@ -1163,11 +1171,11 @@ fn static_assert() { longhands::${value.name}::computed_value::T { is_span: self.gecko.${value.gecko}.mHasSpan, ident: { - let name = self.gecko.${value.gecko}.mLineName.to_string(); - if name.len() == 0 { + let name = unsafe { Atom::from_raw(self.gecko.${value.gecko}.mLineName.mRawPtr) }; + if name == atom!("") { None } else { - Some(CustomIdent(Atom::from(name))) + Some(CustomIdent(name)) } }, line_num: @@ -1206,20 +1214,21 @@ fn static_assert() { pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) { <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %> use crate::gecko_bindings::structs::{nsTArray, nsStyleGridLine_kMaxLine}; - use nsstring::nsString; use std::usize; use crate::values::CustomIdent; use crate::values::generics::grid::TrackListType::Auto; use crate::values::generics::grid::{GridTemplateComponent, RepeatCount}; #[inline] - fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray) { + fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray>) { unsafe { - bindings::Gecko_ResizeTArrayForStrings(gecko_names, servo_names.len() as u32); + bindings::Gecko_ResizeAtomArray(gecko_names, servo_names.len() as u32); } for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) { - gecko_name.assign(servo_name.0.as_slice()); + gecko_name.set_move(unsafe { + RefPtr::from_addrefed(servo_name.0.clone().into_addrefed()) + }); } } @@ -1257,9 +1266,9 @@ fn static_assert() { auto_track_size = Some(auto_repeat.track_sizes.get(0).unwrap().clone()); } else { unsafe { - bindings::Gecko_ResizeTArrayForStrings( + bindings::Gecko_ResizeAtomArray( &mut value.mRepeatAutoLineNameListBefore, 0); - bindings::Gecko_ResizeTArrayForStrings( + bindings::Gecko_ResizeAtomArray( &mut value.mRepeatAutoLineNameListAfter, 0); } } @@ -1333,7 +1342,6 @@ fn static_assert() { pub fn clone_grid_template_${kind}(&self) -> longhands::grid_template_${kind}::computed_value::T { <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %> use crate::gecko_bindings::structs::nsTArray; - use nsstring::nsString; use crate::values::CustomIdent; use crate::values::generics::grid::{GridTemplateComponent, LineNameList, RepeatCount}; use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat, TrackSize}; @@ -1344,16 +1352,17 @@ fn static_assert() { }; #[inline] - fn to_boxed_customident_slice(gecko_names: &nsTArray) -> Box<[CustomIdent]> { + fn to_boxed_customident_slice(gecko_names: &nsTArray>) -> Box<[CustomIdent]> { let idents: Vec = gecko_names.iter().map(|gecko_name| { - CustomIdent(Atom::from(gecko_name.to_string())) + CustomIdent(unsafe { Atom::from_raw(gecko_name.mRawPtr) }) }).collect(); idents.into_boxed_slice() } #[inline] - fn to_line_names_vec(gecko_line_names: &nsTArray>) - -> Vec> { + fn to_line_names_vec( + gecko_line_names: &nsTArray>>, + ) -> Vec> { gecko_line_names.iter().map(|gecko_names| { to_boxed_customident_slice(gecko_names) }).collect() @@ -1416,89 +1425,6 @@ fn static_assert() { % endfor ${impl_simple_type_with_conversion("grid_auto_flow")} - - pub fn set_grid_template_areas(&mut self, v: values::computed::position::GridTemplateAreas) { - use crate::gecko_bindings::bindings::Gecko_NewGridTemplateAreasValue; - use crate::gecko_bindings::sugar::refptr::UniqueRefPtr; - - let v = match v { - Either::First(areas) => areas, - Either::Second(_) => { - unsafe { self.gecko.mGridTemplateAreas.clear() } - return; - }, - }; - - let mut refptr = unsafe { - UniqueRefPtr::from_addrefed( - Gecko_NewGridTemplateAreasValue(v.0.areas.len() as u32, v.0.strings.len() as u32, v.0.width)) - }; - - for (servo, gecko) in v.0.areas.into_iter().zip(refptr.mNamedAreas.iter_mut()) { - gecko.mName.assign_str(&*servo.name); - gecko.mColumnStart = servo.columns.start; - gecko.mColumnEnd = servo.columns.end; - gecko.mRowStart = servo.rows.start; - gecko.mRowEnd = servo.rows.end; - } - - for (servo, gecko) in v.0.strings.into_iter().zip(refptr.mTemplates.iter_mut()) { - gecko.assign_str(&*servo); - } - - self.gecko.mGridTemplateAreas.set_move(refptr.get()) - } - - pub fn copy_grid_template_areas_from(&mut self, other: &Self) { - unsafe { self.gecko.mGridTemplateAreas.set(&other.gecko.mGridTemplateAreas) } - } - - pub fn reset_grid_template_areas(&mut self, other: &Self) { - self.copy_grid_template_areas_from(other) - } - - pub fn clone_grid_template_areas(&self) -> values::computed::position::GridTemplateAreas { - use std::ops::Range; - use crate::values::None_; - use crate::values::specified::position::{NamedArea, TemplateAreas, TemplateAreasArc}; - - if self.gecko.mGridTemplateAreas.mRawPtr.is_null() { - return Either::Second(None_); - } - - let gecko_grid_template_areas = self.gecko.mGridTemplateAreas.mRawPtr; - let areas = unsafe { - let vec: Vec = - (*gecko_grid_template_areas).mNamedAreas.iter().map(|gecko_name_area| { - let name = gecko_name_area.mName.to_string().into_boxed_str(); - let rows = Range { - start: gecko_name_area.mRowStart, - end: gecko_name_area.mRowEnd - }; - let columns = Range { - start: gecko_name_area.mColumnStart, - end: gecko_name_area.mColumnEnd - }; - NamedArea{ name, rows, columns } - }).collect(); - vec.into_boxed_slice() - }; - - let strings = unsafe { - let vec: Vec> = - (*gecko_grid_template_areas).mTemplates.iter().map(|gecko_template| { - gecko_template.to_string().into_boxed_str() - }).collect(); - vec.into_boxed_slice() - }; - - let width = unsafe { - (*gecko_grid_template_areas).mNColumns - }; - - Either::First(TemplateAreasArc(Arc::new(TemplateAreas{ areas, strings, width }))) - } - <% skip_outline_longhands = " ".join("outline-style outline-width".split() + @@ -2714,22 +2640,20 @@ fn static_assert() { for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { - if let Either::Second(image) = image { + if let ImageLayer::Image(image) = image { geckoimage.mImage.set(image) } } } pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T { - use crate::values::None_; - longhands::${shorthand}_image::computed_value::List( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mImageCount as usize) .map(|ref layer| { match unsafe { layer.mImage.into_image() } { - Some(image) => Either::Second(image), - None => Either::First(None_), + Some(image) => ImageLayer::Image(image), + None => ImageLayer::None, } }).collect() ) diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 800d50c6220..b29c323cf92 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -3,8 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ <%! - from data import Keyword, to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS - from data import LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES + from data import Keyword, to_rust_ident, to_phys, to_camel_case, SYSTEM_FONT_LONGHANDS + from data import (LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES, + PHYSICAL_SIDES, LOGICAL_SIZES, LOGICAL_AXES) %> <%def name="predefined_type(name, type, initial_value, parse_method='parse', @@ -1038,17 +1039,21 @@ side = None size = None corner = None + axis = None maybe_side = [s for s in LOGICAL_SIDES if s in name] maybe_size = [s for s in LOGICAL_SIZES if s in name] maybe_corner = [s for s in LOGICAL_CORNERS if s in name] + maybe_axis = [s for s in LOGICAL_AXES if name.endswith(s)] if len(maybe_side) == 1: side = maybe_side[0] elif len(maybe_size) == 1: size = maybe_size[0] elif len(maybe_corner) == 1: corner = maybe_corner[0] + elif len(maybe_axis) == 1: + axis = maybe_axis[0] def phys_ident(side, phy_side): - return to_rust_ident(name.replace(side, phy_side).replace("inset-", "")) + return to_rust_ident(to_phys(name, side, phy_side)) %> % if side is not None: use crate::logical_geometry::PhysicalSide; @@ -1080,6 +1085,19 @@ } else { ${caller.inner(physical_ident=phys_ident(size, physical_size[0]))} } + % elif axis is not None: + <% + if axis == "inline": + me, other = "x", "y" + else: + assert(axis == "block") + me, other = "y", "x" + %> + if wm.is_vertical() { + ${caller.inner(physical_ident=phys_ident(axis, other))} + } else { + ${caller.inner(physical_ident=phys_ident(axis, me))} + } % else: <% raise Exception("Don't know what to do with logical property %s" % name) %> % endif diff --git a/components/style/properties/longhands/background.mako.rs b/components/style/properties/longhands/background.mako.rs index 9c66e4676f9..e9e866983c0 100644 --- a/components/style/properties/longhands/background.mako.rs +++ b/components/style/properties/longhands/background.mako.rs @@ -22,8 +22,8 @@ ${helpers.predefined_type( ${helpers.predefined_type( "background-image", "ImageLayer", - initial_value="Either::First(None_)", - initial_specified_value="Either::First(None_)", + initial_value="computed::ImageLayer::none()", + initial_specified_value="specified::ImageLayer::none()", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector="True", animation_value_type="discrete", diff --git a/components/style/properties/longhands/border.mako.rs b/components/style/properties/longhands/border.mako.rs index 7909372be17..66544a44858 100644 --- a/components/style/properties/longhands/border.mako.rs +++ b/components/style/properties/longhands/border.mako.rs @@ -107,8 +107,8 @@ ${helpers.single_keyword( ${helpers.predefined_type( "border-image-source", "ImageLayer", - initial_value="Either::First(None_)", - initial_specified_value="Either::First(None_)", + initial_value="computed::ImageLayer::none()", + initial_specified_value="specified::ImageLayer::none()", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector=False, animation_value_type="discrete", diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index f37d5968b24..0a15047038a 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> -<% from data import Keyword, Method, to_rust_ident, to_camel_case%> +<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%> <% data.new_style_struct("Box", inherited=False, @@ -114,27 +114,22 @@ ${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box", // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. // // We allow it to apply to placeholders for UA sheets, which set it !important. -${helpers.predefined_type( - "overflow-x", - "Overflow", - "computed::Overflow::Visible", - animation_value_type="discrete", - flags="APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-x", - needs_context=False, - servo_restyle_damage = "reflow", -)} - -${helpers.predefined_type( - "overflow-y", - "Overflow", - "computed::Overflow::Visible", - animation_value_type="discrete", - flags="APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-y", - needs_context=False, - servo_restyle_damage = "reflow", -)} +% for (axis, logical) in ALL_AXES: + <% full_name = "overflow-{}".format(axis) %> + ${helpers.predefined_type( + full_name, + "Overflow", + "computed::Overflow::Visible", + logical_group="overflow", + logical=logical, + animation_value_type="discrete", + flags="APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-overflow-3/#propdef-{}".format(full_name), + needs_context=False, + servo_restyle_damage = "reflow", + gecko_pref="layout.css.overflow-logical.enabled" if logical else None, + )} +% endfor ${helpers.predefined_type( "overflow-anchor", diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 58331efa6bd..fc934d901eb 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -176,7 +176,7 @@ ${helpers.predefined_type( <%helpers:single_keyword name="white-space" values="normal pre nowrap pre-wrap pre-line" - extra_gecko_values="-moz-pre-space" + extra_gecko_values="break-spaces -moz-pre-space" gecko_enum_prefix="StyleWhiteSpace" needs_conversion="True" animation_value_type="discrete" @@ -291,7 +291,6 @@ ${helpers.predefined_type( "Color", "computed_value::T::currentcolor()", products="gecko", - gecko_pref="layout.css.prefixes.webkit", animation_value_type="AnimatedColor", ignored_when_colors_disabled=True, flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", @@ -306,7 +305,6 @@ ${helpers.predefined_type( products="gecko", animation_value_type="AnimatedColor", ignored_when_colors_disabled=True, - gecko_pref="layout.css.prefixes.webkit", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color", )} @@ -318,7 +316,6 @@ ${helpers.predefined_type( initial_specified_value="specified::BorderSideWidth::zero()", computed_type="crate::values::computed::NonNegativeLength", products="gecko", - gecko_pref="layout.css.prefixes.webkit", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width", animation_value_type="discrete", @@ -374,3 +371,14 @@ ${helpers.single_keyword( products="gecko", spec="Nonstandard", )} + +// text underline offset +${helpers.predefined_type( + "text-underline-offset", + "LengthOrAuto", + "computed::LengthOrAuto::auto()", + products="gecko", + animation_value_type="ComputedValue", + gecko_pref="layout.css.text-underline-offset.enabled", + spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset", +)} diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs index 8e93d03dbb1..9f0e0db9fbc 100644 --- a/components/style/properties/longhands/svg.mako.rs +++ b/components/style/properties/longhands/svg.mako.rs @@ -181,8 +181,8 @@ ${helpers.single_keyword( ${helpers.predefined_type( "mask-image", "ImageLayer", - "Either::First(None_)", - initial_specified_value="Either::First(None_)", + initial_value="computed::ImageLayer::none()", + initial_specified_value="specified::ImageLayer::none()", parse_method="parse_with_cors_anonymous", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", vector=True, diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs index 723e26ae22b..91412d2ee6e 100644 --- a/components/style/properties/longhands/text.mako.rs +++ b/components/style/properties/longhands/text.mako.rs @@ -69,3 +69,13 @@ ${helpers.predefined_type( gecko_pref="layout.css.initial-letter.enabled", spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials", )} + +${helpers.predefined_type( + "text-decoration-width", + "LengthOrAuto", + "computed::LengthOrAuto::auto()", + products="gecko", + animation_value_type="ComputedValue", + gecko_pref="layout.css.text-decoration-width.enabled", + spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property" +)} diff --git a/components/style/properties/shorthands/inherited_text.mako.rs b/components/style/properties/shorthands/inherited_text.mako.rs index 091b5353b33..6c50ea0fb28 100644 --- a/components/style/properties/shorthands/inherited_text.mako.rs +++ b/components/style/properties/shorthands/inherited_text.mako.rs @@ -48,7 +48,6 @@ <%helpers:shorthand name="-webkit-text-stroke" sub_properties="-webkit-text-stroke-width -webkit-text-stroke-color" - gecko_pref="layout.css.prefixes.webkit" products="gecko" derive_serialize="True" spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke"> diff --git a/components/style/properties/shorthands/position.mako.rs b/components/style/properties/shorthands/position.mako.rs index 63298b86ba1..ea2f34b9db8 100644 --- a/components/style/properties/shorthands/position.mako.rs +++ b/components/style/properties/shorthands/position.mako.rs @@ -253,18 +253,17 @@ products="gecko"> use crate::parser::Parse; use servo_arc::Arc; - use crate::values::{Either, None_}; use crate::values::generics::grid::{TrackSize, TrackList, TrackListType}; use crate::values::generics::grid::{TrackListValue, concat_serialize_idents}; use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent}; use crate::values::specified::grid::parse_line_names; - use crate::values::specified::position::{TemplateAreas, TemplateAreasArc}; + use crate::values::specified::position::{GridTemplateAreas, TemplateAreas, TemplateAreasArc}; /// Parsing for `` shorthand (also used by `grid` shorthand). pub fn parse_grid_template<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, - ) -> Result<(GridTemplateComponent, GridTemplateComponent, Either), ParseError<'i>> { + ) -> Result<(GridTemplateComponent, GridTemplateComponent, GridTemplateAreas), ParseError<'i>> { // Other shorthand sub properties also parse the `none` keyword and this shorthand // should know after this keyword there is nothing to parse. Otherwise it gets // confused and rejects the sub properties that contains `none`. @@ -275,13 +274,10 @@ % for keyword, rust_type in keywords.items(): if let Ok(x) = input.try(|i| { if i.try(|i| i.expect_ident_matching("${keyword}")).is_ok() { - if i.is_exhausted() { - return Ok((${rust_type}, - ${rust_type}, - Either::Second(None_))) - } else { + if !i.is_exhausted() { return Err(()); } + return Ok((${rust_type}, ${rust_type}, GridTemplateAreas::None)); } Err(()) }) { @@ -290,7 +286,7 @@ % endfor let first_line_names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice()); - if let Ok(mut string) = input.try(|i| i.expect_string().map(|s| s.as_ref().into())) { + if let Ok(mut 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![]; @@ -305,7 +301,7 @@ names.extend(v.into_vec()); } - string = match input.try(|i| i.expect_string().map(|s| s.as_ref().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_boxed_slice()); @@ -323,7 +319,7 @@ .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?; let template_rows = TrackList { list_type: TrackListType::Normal, - values: values, + values, line_names: line_names.into_boxed_slice(), auto_repeat: None, }; @@ -342,7 +338,7 @@ }; Ok((GenericGridTemplateComponent::TrackList(template_rows), - template_cols, Either::First(TemplateAreasArc(Arc::new(template_areas))))) + template_cols, GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))))) } else { let mut template_rows = GridTemplateComponent::parse(context, input)?; if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows { @@ -356,7 +352,7 @@ } input.expect_delim('/')?; - Ok((template_rows, GridTemplateComponent::parse(context, input)?, Either::Second(None_))) + Ok((template_rows, GridTemplateComponent::parse(context, input)?, GridTemplateAreas::None)) } } @@ -377,18 +373,18 @@ pub fn serialize_grid_template( template_rows: &GridTemplateComponent, template_columns: &GridTemplateComponent, - template_areas: &Either, + template_areas: &GridTemplateAreas, dest: &mut CssWriter, ) -> fmt::Result where W: Write { match *template_areas { - Either::Second(_none) => { + GridTemplateAreas::None => { template_rows.to_css(dest)?; dest.write_str(" / ")?; template_columns.to_css(dest) }, - Either::First(ref areas) => { + GridTemplateAreas::Areas(ref areas) => { // The length of template-area and template-rows values should be equal. if areas.0.strings.len() != template_rows.track_list_len() { return Ok(()); @@ -485,10 +481,9 @@ products="gecko"> use crate::parser::Parse; use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow}; - use crate::values::{Either, None_}; use crate::values::generics::grid::{GridTemplateComponent, TrackListType}; use crate::values::specified::{GenericGridTemplateComponent, TrackSize}; - use crate::values::specified::position::{AutoFlow, GridAutoFlow}; + use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas}; pub fn parse_value<'i, 't>( context: &ParserContext, @@ -496,7 +491,7 @@ ) -> Result> { let mut temp_rows = GridTemplateComponent::None; let mut temp_cols = GridTemplateComponent::None; - let mut temp_areas = Either::Second(None_); + let mut temp_areas = GridTemplateAreas::None; let mut auto_rows = TrackSize::default(); let mut auto_cols = TrackSize::default(); let mut flow = grid_auto_flow::get_initial_value(); @@ -558,7 +553,7 @@ impl<'a> LonghandsToSerialize<'a> { /// Returns true if other sub properties except template-{rows,columns} are initial. fn is_grid_template(&self) -> bool { - *self.grid_template_areas == Either::Second(None_) && + *self.grid_template_areas == GridTemplateAreas::None && *self.grid_auto_rows == TrackSize::default() && *self.grid_auto_columns == TrackSize::default() && *self.grid_auto_flow == grid_auto_flow::get_initial_value() @@ -567,7 +562,7 @@ impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { - if *self.grid_template_areas != Either::Second(None_) || + if *self.grid_template_areas != GridTemplateAreas::None || (*self.grid_template_rows != GridTemplateComponent::None && *self.grid_template_columns != GridTemplateComponent::None) || self.is_grid_template() { diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index 360b8101ef1..f536b80b5c5 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -54,6 +54,11 @@ pub fn containing_shadow_ignoring_svg_use( } } +#[inline] +fn sort_rules_from(rules: &mut ApplicableDeclarationList, start: usize) { + rules[start..].sort_unstable_by_key(|block| (block.specificity, block.source_order())); +} + /// An object that we use with all the intermediate state needed for the /// cascade. /// @@ -146,15 +151,7 @@ where None => return, }; - map.get_all_matching_rules( - self.element, - self.rule_hash_target, - self.rules, - self.context, - self.flags_setter, - cascade_level, - 0, - ); + self.collect_rules_internal(None, map, cascade_level); } fn collect_user_agent_rules(&mut self) { @@ -200,11 +197,23 @@ where cascade_level: CascadeLevel, ) { debug_assert!(shadow_host.shadow_root().is_some()); + self.collect_rules_internal(Some(shadow_host), map, cascade_level); + self.shadow_cascade_order += 1; + } + + #[inline] + fn collect_rules_internal( + &mut self, + shadow_host: Option, + map: &SelectorMap, + cascade_level: CascadeLevel, + ) { 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 start = rules.len(); self.context.with_shadow_host(shadow_host, |context| { map.get_all_matching_rules( element, @@ -216,7 +225,7 @@ where shadow_cascade_order, ); }); - self.shadow_cascade_order += 1; + sort_rules_from(rules, start); } /// Collects the rules for the ::slotted pseudo-element. @@ -310,6 +319,64 @@ where self.collect_stylist_rules(Origin::Author); } + fn collect_part_rules(&mut self) { + if !self.rule_hash_target.has_part_attr() { + return; + } + + let 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), + }; + + // 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, + ); + } + }); + }); + sort_rules_from(rules, start); + } + } + fn collect_style_attribute_and_animation_rules(&mut self) { if let Some(sa) = self.style_attribute { self.rules @@ -368,6 +435,7 @@ where self.collect_slotted_rules(); self.collect_normal_rules_from_containing_shadow_tree(); self.collect_document_author_rules(); + self.collect_part_rules(); self.collect_style_attribute_and_animation_rules(); } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 3b45035f46d..8f255898fc8 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -14,8 +14,7 @@ use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::stylesheets::{Origin, StyleRule}; use crate::thread_state; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; use parking_lot::RwLock; use servo_arc::{Arc, ArcBorrow, ArcUnion, ArcUnionBorrow}; use smallvec::SmallVec; @@ -46,7 +45,6 @@ use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; /// logs from http://logs.glob.uno/?c=mozilla%23servo&s=3+Apr+2017&e=3+Apr+2017#c644094 /// to se a discussion about the different memory orderings used here. #[derive(Debug)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct RuleTree { root: StrongRuleNode, } @@ -74,7 +72,6 @@ impl Drop for RuleTree { } } -#[cfg(feature = "gecko")] impl MallocSizeOf for RuleTree { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { let mut n = 0; @@ -83,20 +80,16 @@ impl MallocSizeOf for RuleTree { while let Some(node) = stack.pop() { n += unsafe { ops.malloc_size_of(node.ptr()) }; - stack.extend(unsafe { - (*node.ptr()) - .children - .read() - .iter() - .map(|(_k, v)| v.clone()) - }); + let children = unsafe { (*node.ptr()).children.read() }; + children.shallow_size_of(ops); + children.each(|c| stack.push(c.clone())); } n } } -#[derive(Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct ChildKey(CascadeLevel, ptr::NonNull<()>); unsafe impl Send for ChildKey {} @@ -382,9 +375,58 @@ impl RuleTree { /// This can only be called when no other threads is accessing this tree. pub unsafe fn maybe_gc(&self) { + #[cfg(debug_assertions)] + self.maybe_dump_stats(); + self.root.maybe_gc(); } + #[cfg(debug_assertions)] + fn maybe_dump_stats(&self) { + use itertools::Itertools; + use std::cell::Cell; + use std::time::{Duration, Instant}; + + if !log_enabled!(log::Level::Trace) { + return; + } + + const RULE_TREE_STATS_INTERVAL: Duration = Duration::from_secs(2); + + thread_local! { + pub static LAST_STATS: Cell = Cell::new(Instant::now()); + }; + + let should_dump = LAST_STATS.with(|s| { + let now = Instant::now(); + if now.duration_since(s.get()) < RULE_TREE_STATS_INTERVAL { + return false; + } + s.set(now); + true + }); + + if !should_dump { + return; + } + + let mut children_count = FxHashMap::default(); + + let mut stack = SmallVec::<[_; 32]>::new(); + stack.push(self.root.clone()); + while let Some(node) = stack.pop() { + let children = node.get().children.read(); + *children_count.entry(children.len()).or_insert(0) += 1; + children.each(|c| stack.push(c.upgrade())); + } + + trace!("Rule tree stats:"); + let counts = children_count.keys().sorted(); + for count in counts { + trace!(" {} - {}", count, children_count[count]); + } + } + /// Replaces a rule in a given level (if present) for another rule. /// /// Returns the resulting node that represents the new path, or None if @@ -706,6 +748,161 @@ impl CascadeLevel { } } +/// The children of a single rule node. +/// +/// We optimize the case of no kids and a single child, since they're by far the +/// most common case and it'd cause a bunch of bloat for no reason. +/// +/// The children remove themselves when they go away, which means that it's ok +/// for us to store weak pointers to them. +enum RuleNodeChildren { + /// There are no kids. + Empty, + /// There's just one kid. This is an extremely common case, so we don't + /// bother allocating a map for it. + One(WeakRuleNode), + /// At least at one point in time there was more than one kid (that is to + /// say, we don't bother re-allocating if children are removed dynamically). + Map(Box>), +} + +impl MallocShallowSizeOf for RuleNodeChildren { + fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + match *self { + RuleNodeChildren::One(..) | RuleNodeChildren::Empty => 0, + RuleNodeChildren::Map(ref m) => { + // Want to account for both the box and the hashmap. + m.shallow_size_of(ops) + (**m).shallow_size_of(ops) + }, + } + } +} + +impl Default for RuleNodeChildren { + fn default() -> Self { + RuleNodeChildren::Empty + } +} + +impl RuleNodeChildren { + /// Executes a given function for each of the children. + fn each(&self, mut f: impl FnMut(&WeakRuleNode)) { + match *self { + RuleNodeChildren::Empty => {}, + RuleNodeChildren::One(ref child) => f(child), + RuleNodeChildren::Map(ref map) => { + for (_key, kid) in map.iter() { + f(kid) + } + }, + } + } + + fn len(&self) -> usize { + match *self { + RuleNodeChildren::Empty => 0, + RuleNodeChildren::One(..) => 1, + RuleNodeChildren::Map(ref map) => map.len(), + } + } + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn get(&self, key: &ChildKey) -> Option<&WeakRuleNode> { + match *self { + RuleNodeChildren::Empty => return None, + RuleNodeChildren::One(ref kid) => { + // We're read-locked, so no need to do refcount stuff, since the + // child is only removed from the main thread, _and_ it'd need + // to write-lock us anyway. + if unsafe { (*kid.ptr()).key() } == *key { + Some(kid) + } else { + None + } + }, + RuleNodeChildren::Map(ref map) => map.get(&key), + } + } + + fn get_or_insert_with( + &mut self, + key: ChildKey, + get_new_child: impl FnOnce() -> StrongRuleNode, + ) -> StrongRuleNode { + let existing_child_key = match *self { + RuleNodeChildren::Empty => { + let new = get_new_child(); + debug_assert_eq!(new.get().key(), key); + *self = RuleNodeChildren::One(new.downgrade()); + return new; + }, + RuleNodeChildren::One(ref weak) => unsafe { + // We're locked necessarily, so it's fine to look at our + // weak-child without refcount-traffic. + let existing_child_key = (*weak.ptr()).key(); + if existing_child_key == key { + return weak.upgrade(); + } + existing_child_key + }, + RuleNodeChildren::Map(ref mut map) => { + return match map.entry(key) { + hash::map::Entry::Occupied(ref occupied) => occupied.get().upgrade(), + hash::map::Entry::Vacant(vacant) => { + let new = get_new_child(); + + debug_assert_eq!(new.get().key(), key); + vacant.insert(new.downgrade()); + + new + }, + }; + }, + }; + + let existing_child = match mem::replace(self, RuleNodeChildren::Empty) { + RuleNodeChildren::One(o) => o, + _ => unreachable!(), + }; + // Two rule-nodes are still a not-totally-uncommon thing, so + // avoid over-allocating entries. + // + // TODO(emilio): Maybe just inline two kids too? + let mut children = Box::new(FxHashMap::with_capacity_and_hasher(2, Default::default())); + children.insert(existing_child_key, existing_child); + + let new = get_new_child(); + debug_assert_eq!(new.get().key(), key); + children.insert(key, new.downgrade()); + + *self = RuleNodeChildren::Map(children); + + new + } + + fn remove(&mut self, key: &ChildKey) -> Option { + match *self { + RuleNodeChildren::Empty => return None, + RuleNodeChildren::One(ref one) => { + if unsafe { (*one.ptr()).key() } != *key { + return None; + } + }, + RuleNodeChildren::Map(ref mut multiple) => { + return multiple.remove(key); + }, + } + + match mem::replace(self, RuleNodeChildren::Empty) { + RuleNodeChildren::One(o) => Some(o), + _ => unreachable!(), + } + } +} + /// A node in the rule tree. pub struct RuleNode { /// The root node. Only the root has no root pointer, for obvious reasons. @@ -731,7 +928,7 @@ pub struct RuleNode { /// The children of a given rule node. Children remove themselves from here /// when they go away. - children: RwLock>, + children: RwLock, /// The next item in the rule tree free list, that starts on the root node. /// @@ -822,8 +1019,14 @@ impl RuleNode { } } - fn key(&self) -> Option { - Some(ChildKey(self.level, self.source.as_ref()?.key())) + fn key(&self) -> ChildKey { + ChildKey( + self.level, + self.source + .as_ref() + .expect("Called key() on the root node") + .key(), + ) } fn is_root(&self) -> bool { @@ -847,7 +1050,7 @@ impl RuleNode { ); if let Some(parent) = self.parent.as_ref() { - let weak = parent.get().children.write().remove(&self.key().unwrap()); + let weak = parent.get().children.write().remove(&self.key()); assert_eq!(weak.unwrap().ptr() as *const _, self as *const _); } } @@ -884,12 +1087,12 @@ impl RuleNode { } let _ = write!(writer, "\n"); - for (_, child) in self.children.read().iter() { + self.children.read().each(|child| { child .upgrade() .get() .dump(guards, writer, indent + INDENT_INCREMENT); - } + }); } } @@ -951,21 +1154,14 @@ impl StrongRuleNode { return child.upgrade(); } - match RwLockUpgradableReadGuard::upgrade(read_guard).entry(key) { - hash::map::Entry::Occupied(ref occupied) => occupied.get().upgrade(), - hash::map::Entry::Vacant(vacant) => { - let new_node = StrongRuleNode::new(Box::new(RuleNode::new( - root, - self.clone(), - source.clone(), - level, - ))); - - vacant.insert(new_node.downgrade()); - - new_node - }, - } + RwLockUpgradableReadGuard::upgrade(read_guard).get_or_insert_with(key, move || { + StrongRuleNode::new(Box::new(RuleNode::new( + root, + self.clone(), + source.clone(), + level, + ))) + }) } /// Raw pointer to the RuleNode diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index afae5450182..52b939e0219 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -182,10 +182,6 @@ impl SelectorMap { let quirks_mode = context.quirks_mode(); - // At the end, we're going to sort the rules that we added, so remember - // where we began. - let init_len = matching_rules_list.len(); - if rule_hash_target.is_root() { SelectorMap::get_matching_rules( element, @@ -259,14 +255,10 @@ impl SelectorMap { cascade_level, shadow_cascade_order, ); - - // Sort only the rules we just added. - matching_rules_list[init_len..] - .sort_unstable_by_key(|block| (block.specificity, block.source_order())); } /// Adds rules in `rules` that match `element` to the `matching_rules` list. - fn get_matching_rules( + pub(crate) fn get_matching_rules( element: E, rules: &[Rule], matching_rules: &mut ApplicableDeclarationList, diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 1ef49c87f9f..4bf3f38822a 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -143,6 +143,12 @@ impl PseudoElement { false } + /// Whether this pseudo-element is the ::selection pseudo. + #[inline] + pub fn is_selection(&self) -> bool { + *self == PseudoElement::Selection + } + /// Whether this pseudo-element is the ::before pseudo. #[inline] pub fn is_before(&self) -> bool { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index a98b8a89eb5..ed9af91af14 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -33,11 +33,11 @@ use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter}; use crate::thread_state::{self, ThreadState}; use crate::{Atom, LocalName, Namespace, WeakAtom}; +use fallible::FallibleVec; use hashglobe::FailedAllocationError; +use malloc_size_of::MallocSizeOf; #[cfg(feature = "gecko")] -use malloc_size_of::MallocUnconditionalShallowSizeOf; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; +use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::bloom::BloomFilter; use selectors::matching::VisitedHandlingMode; @@ -1256,7 +1256,7 @@ impl Stylist { let matches_document_rules = element.each_applicable_non_document_style_rule_data(|data, host| { - matching_context.with_shadow_host(host, |matching_context| { + matching_context.with_shadow_host(Some(host), |matching_context| { data.selectors_for_cache_revalidation.lookup( element, self.quirks_mode, @@ -1641,9 +1641,9 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { /// A set of rules for element and pseudo-elements. #[derive(Debug, Default, MallocSizeOf)] -struct ElementAndPseudoRules { +struct GenericElementAndPseudoRules { /// Rules from stylesheets at this `CascadeData`'s origin. - element_map: SelectorMap, + element_map: Map, /// Rules from stylesheets at this `CascadeData`'s origin that correspond /// to a given pseudo-element. @@ -1651,39 +1651,30 @@ struct ElementAndPseudoRules { /// FIXME(emilio): There are a bunch of wasted entries here in practice. /// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for /// `precomputed_values_for_pseudo`) without duplicating a lot of code. - pseudos_map: PerPseudoElementMap>>, + pseudos_map: PerPseudoElementMap>, } -impl ElementAndPseudoRules { +impl GenericElementAndPseudoRules { #[inline(always)] - fn insert( - &mut self, - rule: Rule, - pseudo_element: Option<&PseudoElement>, - quirks_mode: QuirksMode, - ) -> Result<(), FailedAllocationError> { + fn for_insertion(&mut self, pseudo_element: Option<&PseudoElement>) -> &mut Map { debug_assert!( - pseudo_element.map_or(true, |pseudo| !pseudo.is_precomputed() && - !pseudo.is_unknown_webkit_pseudo_element()) + pseudo_element.map_or(true, |pseudo| { + !pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element() + }), + "Precomputed pseudos should end up in precomputed_pseudo_element_decls, \ + and unknown webkit pseudos should be discarded before getting here" ); - let map = match pseudo_element { + match pseudo_element { None => &mut self.element_map, Some(pseudo) => self .pseudos_map - .get_or_insert_with(pseudo, || Box::new(SelectorMap::new())), - }; - - map.insert(rule, quirks_mode) - } - - fn clear(&mut self) { - self.element_map.clear(); - self.pseudos_map.clear(); + .get_or_insert_with(pseudo, || Box::new(Default::default())), + } } #[inline] - fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { + fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&Map> { match pseudo { Some(pseudo) => self.pseudos_map.get(pseudo).map(|p| &**p), None => Some(&self.element_map), @@ -1703,6 +1694,26 @@ impl ElementAndPseudoRules { } } +type ElementAndPseudoRules = GenericElementAndPseudoRules>; +type PartMap = PrecomputedHashMap>; +type PartElementAndPseudoRules = GenericElementAndPseudoRules; + +impl ElementAndPseudoRules { + // TODO(emilio): Should we retain storage of these? + fn clear(&mut self) { + self.element_map.clear(); + self.pseudos_map.clear(); + } +} + +impl PartElementAndPseudoRules { + // TODO(emilio): Should we retain storage of these? + fn clear(&mut self) { + self.element_map.clear(); + self.pseudos_map.clear(); + } +} + /// Data resulting from performing the CSS cascade that is specific to a given /// origin. /// @@ -1727,6 +1738,12 @@ pub struct CascadeData { /// containing style scopes starting from the closest assigned slot. slotted_rules: Option>, + /// The data coming from ::part() pseudo-element rules. + /// + /// We need to store them separately because an element needs to match + /// ::part() pseudo-element rules in different shadow roots. + part_rules: Option>, + /// The invalidation map for these rules. invalidation_map: InvalidationMap, @@ -1786,6 +1803,7 @@ impl CascadeData { normal_rules: ElementAndPseudoRules::default(), host_rules: None, slotted_rules: None, + part_rules: None, invalidation_map: InvalidationMap::new(), attribute_dependencies: PrecomputedHashSet::default(), state_dependencies: ElementState::empty(), @@ -1876,6 +1894,12 @@ impl CascadeData { self.slotted_rules.as_ref().and_then(|d| d.rules(pseudo)) } + /// Returns the parts rule map for a given pseudo-element. + #[inline] + pub fn part_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&PartMap> { + self.part_rules.as_ref().and_then(|d| d.rules(pseudo)) + } + /// Collects all the applicable media query results into `results`. /// /// This duplicates part of the logic in `add_stylesheet`, which is @@ -2005,20 +2029,33 @@ impl CascadeData { } } - // NOTE(emilio): It's fine to look at :host and then at - // ::slotted(..), since :host::slotted(..) could never - // possibly match, as is not a valid shadow host. - let rules = if selector.is_featureless_host_selector_or_pseudo_element() { - self.host_rules - .get_or_insert_with(|| Box::new(Default::default())) - } else if selector.is_slotted() { - self.slotted_rules + // Part is special, since given it doesn't have any + // selectors inside, it's not worth using a whole + // SelectorMap for it. + if let Some(part) = selector.part() { + self.part_rules .get_or_insert_with(|| Box::new(Default::default())) + .for_insertion(pseudo_element) + .try_entry(part.clone())? + .or_insert_with(SmallVec::new) + .try_push(rule)?; } else { - &mut self.normal_rules - }; - - rules.insert(rule, pseudo_element, quirks_mode)?; + // NOTE(emilio): It's fine to look at :host and then at + // ::slotted(..), since :host::slotted(..) could never + // possibly match, as is not a valid shadow host. + let rules = + if selector.is_featureless_host_selector_or_pseudo_element() { + self.host_rules + .get_or_insert_with(|| Box::new(Default::default())) + } else if selector.is_slotted() { + self.slotted_rules + .get_or_insert_with(|| Box::new(Default::default())) + } else { + &mut self.normal_rules + } + .for_insertion(pseudo_element); + rules.insert(rule, quirks_mode)?; + } } self.rules_source_order += 1; }, @@ -2184,6 +2221,9 @@ impl CascadeData { if let Some(ref mut slotted_rules) = self.slotted_rules { slotted_rules.clear(); } + if let Some(ref mut part_rules) = self.part_rules { + part_rules.clear(); + } if let Some(ref mut host_rules) = self.host_rules { host_rules.clear(); } @@ -2212,6 +2252,9 @@ impl CascadeData { if let Some(ref slotted_rules) = self.slotted_rules { slotted_rules.add_size_of(ops, sizes); } + if let Some(ref part_rules) = self.part_rules { + part_rules.add_size_of(ops, sizes); + } if let Some(ref host_rules) = self.host_rules { host_rules.add_size_of(ops, sizes); } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 8bc57ada8b7..69d0e463150 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -693,8 +693,7 @@ fn notify_paint_worklet(context: &StyleContext, data: &ElementData) where E: TElement, { - use crate::values::generics::image::Image; - use crate::values::Either; + use crate::values::generics::image::{GenericImageLayer, Image}; use style_traits::ToCss; // We speculatively evaluate any paint worklets during styling. @@ -704,7 +703,7 @@ where if let Some(ref values) = data.styles.primary { for image in &values.get_background().background_image.0 { let (name, arguments) = match *image { - Either::Second(Image::PaintWorklet(ref worklet)) => { + GenericImageLayer::Image(Image::PaintWorklet(ref worklet)) => { (&worklet.name, &worklet.arguments) }, _ => continue, diff --git a/components/style/values/computed/basic_shape.rs b/components/style/values/computed/basic_shape.rs index 9a245aa77f1..27a091a1115 100644 --- a/components/style/values/computed/basic_shape.rs +++ b/components/style/values/computed/basic_shape.rs @@ -10,8 +10,6 @@ use crate::values::computed::url::ComputedUrl; use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage}; use crate::values::generics::basic_shape as generic; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; /// A computed alias for FillRule. pub use crate::values::generics::basic_shape::FillRule; @@ -42,34 +40,3 @@ pub type Ellipse = /// The computed value of `ShapeRadius` pub type ShapeRadius = generic::GenericShapeRadius; - -impl ToCss for Circle { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("circle(")?; - self.radius.to_css(dest)?; - dest.write_str(" at ")?; - self.position.to_css(dest)?; - dest.write_str(")") - } -} - -impl ToCss for Ellipse { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("ellipse(")?; - if (self.semiaxis_x, self.semiaxis_y) != Default::default() { - self.semiaxis_x.to_css(dest)?; - dest.write_str(" ")?; - self.semiaxis_y.to_css(dest)?; - dest.write_str(" ")?; - } - dest.write_str("at ")?; - self.position.to_css(dest)?; - dest.write_str(")") - } -} diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index 70ecad6a04e..67b432d5ffd 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -11,44 +11,41 @@ use crate::values::computed::position::Position; use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::{Angle, Color, Context}; use crate::values::computed::{Length, LengthPercentage, NumberOrPercentage, ToComputedValue}; -use crate::values::generics::image::{self as generic, CompatMode}; +use crate::values::generics::image::{self as generic, GradientCompatMode}; use crate::values::specified::image::LineDirection as SpecifiedLineDirection; -use crate::values::specified::position::{X, Y}; -use crate::values::{Either, None_}; +use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword}; use std::f32::consts::PI; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; /// A computed image layer. -pub type ImageLayer = Either; +pub type ImageLayer = generic::GenericImageLayer; /// Computed values for an image according to CSS-IMAGES. /// -pub type Image = generic::Image; +pub type Image = generic::GenericImage; /// Computed values for a CSS gradient. /// pub type Gradient = - generic::Gradient; + generic::GenericGradient; /// A computed gradient kind. pub type GradientKind = - generic::GradientKind; + generic::GenericGradientKind; /// A computed gradient line direction. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)] +#[repr(C, u8)] pub enum LineDirection { /// An angle. Angle(Angle), /// A horizontal direction. - Horizontal(X), + Horizontal(HorizontalPositionKeyword), /// A vertical direction. - Vertical(Y), + Vertical(VerticalPositionKeyword), /// A corner. - Corner(X, Y), - /// A Position and an Angle for legacy `-moz-` prefixed gradient. - #[cfg(feature = "gecko")] - MozPosition(Option, Option), + Corner(HorizontalPositionKeyword, VerticalPositionKeyword), } /// A computed radial gradient ending shape. @@ -64,69 +61,45 @@ pub type ColorStop = generic::ColorStop; pub type MozImageRect = generic::MozImageRect; impl generic::LineDirection for LineDirection { - fn points_downwards(&self, compat_mode: CompatMode) -> bool { + fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool { match *self { LineDirection::Angle(angle) => angle.radians() == PI, - LineDirection::Vertical(Y::Bottom) if compat_mode == CompatMode::Modern => true, - LineDirection::Vertical(Y::Top) if compat_mode != CompatMode::Modern => true, - LineDirection::Corner(..) => false, - #[cfg(feature = "gecko")] - LineDirection::MozPosition( - Some(Position { - ref vertical, - ref horizontal, - }), - None, - ) => { - // `50% 0%` is the default value for line direction. - horizontal.as_percentage().map_or(false, |p| p.0 == 0.5) && - vertical.as_percentage().map_or(false, |p| p.0 == 0.0) + LineDirection::Vertical(VerticalPositionKeyword::Bottom) => { + compat_mode == GradientCompatMode::Modern + }, + LineDirection::Vertical(VerticalPositionKeyword::Top) => { + compat_mode != GradientCompatMode::Modern }, _ => false, } } - fn to_css(&self, dest: &mut CssWriter, compat_mode: CompatMode) -> fmt::Result + fn to_css(&self, dest: &mut CssWriter, compat_mode: GradientCompatMode) -> fmt::Result where W: Write, { match *self { LineDirection::Angle(ref angle) => angle.to_css(dest), LineDirection::Horizontal(x) => { - if compat_mode == CompatMode::Modern { + if compat_mode == GradientCompatMode::Modern { dest.write_str("to ")?; } x.to_css(dest) }, LineDirection::Vertical(y) => { - if compat_mode == CompatMode::Modern { + if compat_mode == GradientCompatMode::Modern { dest.write_str("to ")?; } y.to_css(dest) }, LineDirection::Corner(x, y) => { - if compat_mode == CompatMode::Modern { + if compat_mode == GradientCompatMode::Modern { dest.write_str("to ")?; } x.to_css(dest)?; dest.write_str(" ")?; y.to_css(dest) }, - #[cfg(feature = "gecko")] - LineDirection::MozPosition(position, angle) => { - let mut need_space = false; - if let Some(position) = position { - position.to_css(dest)?; - need_space = true; - } - if let Some(angle) = angle { - if need_space { - dest.write_str(" ")?; - } - angle.to_css(dest)?; - } - Ok(()) - }, } } } @@ -142,13 +115,6 @@ impl ToComputedValue for SpecifiedLineDirection { SpecifiedLineDirection::Horizontal(x) => LineDirection::Horizontal(x), SpecifiedLineDirection::Vertical(y) => LineDirection::Vertical(y), SpecifiedLineDirection::Corner(x, y) => LineDirection::Corner(x, y), - #[cfg(feature = "gecko")] - SpecifiedLineDirection::MozPosition(ref position, ref angle) => { - LineDirection::MozPosition( - position.to_computed_value(context), - angle.to_computed_value(context), - ) - }, } } @@ -160,13 +126,6 @@ impl ToComputedValue for SpecifiedLineDirection { LineDirection::Horizontal(x) => SpecifiedLineDirection::Horizontal(x), LineDirection::Vertical(y) => SpecifiedLineDirection::Vertical(y), LineDirection::Corner(x, y) => SpecifiedLineDirection::Corner(x, y), - #[cfg(feature = "gecko")] - LineDirection::MozPosition(ref position, ref angle) => { - SpecifiedLineDirection::MozPosition( - ToComputedValue::from_computed_value(position), - ToComputedValue::from_computed_value(angle), - ) - }, } } } diff --git a/components/style/values/generics/background.rs b/components/style/values/generics/background.rs index b64fccb6051..d9b6624595d 100644 --- a/components/style/values/generics/background.rs +++ b/components/style/values/generics/background.rs @@ -6,13 +6,6 @@ use crate::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto}; -fn width_and_height_are_auto( - width: &LengthPercentageOrAuto, - height: &LengthPercentageOrAuto, -) -> bool { - width.is_auto() && height.is_auto() -} - /// A generic value for the `background-size` property. #[derive( Animate, @@ -37,10 +30,7 @@ pub enum GenericBackgroundSize { /// Explicit width. width: GenericLengthPercentageOrAuto, /// Explicit height. - /// NOTE(emilio): We should probably simplify all these in case `width` - /// and `height` are the same, but all other browsers agree on only - /// special-casing `auto`. - #[css(contextual_skip_if = "width_and_height_are_auto")] + #[css(skip_if = "GenericLengthPercentageOrAuto::is_auto")] height: GenericLengthPercentageOrAuto, }, /// `cover` diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index 8c63d064817..d91fa58eb0d 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -378,6 +378,48 @@ where } } +impl ToCss for Circle +where + GenericPosition: ToCss, + NonNegativeLengthPercentage: ToCss + PartialEq, +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("circle(")?; + if self.radius != Default::default() { + self.radius.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + self.position.to_css(dest)?; + dest.write_str(")") + } +} + +impl ToCss for Ellipse +where + GenericPosition: ToCss, + NonNegativeLengthPercentage: ToCss + PartialEq, +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + dest.write_str("ellipse(")?; + if self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default() { + self.semiaxis_x.to_css(dest)?; + dest.write_str(" ")?; + self.semiaxis_y.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + self.position.to_css(dest)?; + dest.write_str(")") + } +} + impl Default for ShapeRadius { #[inline] fn default() -> Self { diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 1442ce604a2..c739820ae03 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -13,13 +13,44 @@ use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; +/// An | (for background-image, for example). +#[derive( + Clone, + Debug, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +pub enum GenericImageLayer { + /// The `none` value. + None, + /// The `` value. + Image(Image), +} + +pub use self::GenericImageLayer as ImageLayer; + +impl ImageLayer { + /// Returns `none`. + #[inline] + pub fn none() -> Self { + ImageLayer::None + } +} + /// An [image]. /// /// [image]: https://drafts.csswg.org/css-images/#image-values #[derive( Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] -pub enum Image { +#[repr(C, u8)] +pub enum GenericImage { /// A `` image. Url(ImageUrl), /// A `` image. Gradients are rather large, and not nearly as @@ -36,23 +67,29 @@ pub enum Image { PaintWorklet(PaintWorklet), } +pub use self::GenericImage as Image; + /// A CSS gradient. /// #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] -pub struct Gradient { +#[repr(C)] +pub struct GenericGradient { /// Gradients can be linear or radial. - pub kind: GradientKind, + pub kind: GenericGradientKind, /// The color stops and interpolation hints. - pub items: Vec>, + pub items: crate::OwnedSlice>, /// True if this is a repeating gradient. pub repeating: bool, /// Compatibility mode. - pub compat_mode: CompatMode, + pub compat_mode: GradientCompatMode, } +pub use self::GenericGradient as Gradient; + #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] +#[repr(u8)] /// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes. -pub enum CompatMode { +pub enum GradientCompatMode { /// Modern syntax. Modern, /// `-webkit` prefix. @@ -63,48 +100,56 @@ pub enum CompatMode { /// A gradient kind. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] -pub enum GradientKind { +#[repr(C, u8)] +pub enum GenericGradientKind { /// A linear gradient. Linear(LineDirection), /// A radial gradient. - Radial( - EndingShape, - Position, - Option, - ), + Radial(GenericEndingShape, Position), } +pub use self::GenericGradientKind as GradientKind; + /// A radial gradient's ending shape. #[derive( Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] -pub enum EndingShape { +#[repr(C, u8)] +pub enum GenericEndingShape { /// A circular gradient. - Circle(Circle), + Circle(GenericCircle), /// An elliptic gradient. - Ellipse(Ellipse), + Ellipse(GenericEllipse), } +pub use self::GenericEndingShape as EndingShape; + /// A circle shape. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] -pub enum Circle { +#[repr(C, u8)] +pub enum GenericCircle { /// A circle radius. Radius(Length), /// A circle extent. Extent(ShapeExtent), } +pub use self::GenericCircle as Circle; + /// An ellipse shape. #[derive( Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem, )] -pub enum Ellipse { +#[repr(C, u8)] +pub enum GenericEllipse { /// An ellipse pair of radii. Radii(LengthPercentage, LengthPercentage), /// An ellipse extent. Extent(ShapeExtent), } +pub use self::GenericEllipse as Ellipse; + /// #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] @@ -121,6 +166,7 @@ pub enum Ellipse { ToResolvedValue, ToShmem, )] +#[repr(u8)] pub enum ShapeExtent { ClosestSide, FarthestSide, @@ -268,22 +314,21 @@ where } } -impl ToCss for Gradient +impl ToCss for Gradient where D: LineDirection, L: ToCss, LoP: ToCss, P: ToCss, C: ToCss, - A: ToCss, { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { match self.compat_mode { - CompatMode::WebKit => dest.write_str("-webkit-")?, - CompatMode::Moz => dest.write_str("-moz-")?, + GradientCompatMode::WebKit => dest.write_str("-webkit-")?, + GradientCompatMode::Moz => dest.write_str("-moz-")?, _ => {}, } @@ -300,13 +345,13 @@ where direction.to_css(dest, self.compat_mode)?; false }, - GradientKind::Radial(ref shape, ref position, ref angle) => { + GradientKind::Radial(ref shape, ref position) => { let omit_shape = match *shape { EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true, _ => false, }; - if self.compat_mode == CompatMode::Modern { + if self.compat_mode == GradientCompatMode::Modern { if !omit_shape { shape.to_css(dest)?; dest.write_str(" ")?; @@ -315,10 +360,6 @@ where position.to_css(dest)?; } else { position.to_css(dest)?; - if let Some(ref a) = *angle { - dest.write_str(" ")?; - a.to_css(dest)?; - } if !omit_shape { dest.write_str(", ")?; shape.to_css(dest)?; @@ -327,7 +368,7 @@ where false }, }; - for item in &self.items { + for item in &*self.items { if !skip_comma { dest.write_str(", ")?; } @@ -338,7 +379,7 @@ where } } -impl GradientKind { +impl GradientKind { fn label(&self) -> &str { match *self { GradientKind::Linear(..) => "linear", @@ -350,10 +391,10 @@ impl GradientKind { /// The direction of a linear gradient. pub trait LineDirection { /// Whether this direction points towards, and thus can be omitted. - fn points_downwards(&self, compat_mode: CompatMode) -> bool; + fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool; /// Serialises this direction according to the compatibility mode. - fn to_css(&self, dest: &mut CssWriter, compat_mode: CompatMode) -> fmt::Result + fn to_css(&self, dest: &mut CssWriter, compat_mode: GradientCompatMode) -> fmt::Result where W: Write; } diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 4f627ceb07f..7ee33d0580e 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -20,8 +20,7 @@ use crate::values::specified::SVGPathData; use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage}; use crate::Zero; use cssparser::Parser; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::{ParseError, StyleParseErrorKind}; /// A specified alias for FillRule. pub use crate::values::generics::basic_shape::FillRule; @@ -239,23 +238,6 @@ impl Circle { } } -impl ToCss for Circle { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("circle(")?; - if generic::ShapeRadius::ClosestSide != self.radius { - self.radius.to_css(dest)?; - dest.write_str(" ")?; - } - - dest.write_str("at ")?; - self.position.to_css(dest)?; - dest.write_str(")") - } -} - impl Parse for Ellipse { fn parse<'i, 't>( context: &ParserContext, @@ -293,25 +275,6 @@ impl Ellipse { } } -impl ToCss for Ellipse { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("ellipse(")?; - if self.semiaxis_x != ShapeRadius::default() || self.semiaxis_y != ShapeRadius::default() { - self.semiaxis_x.to_css(dest)?; - dest.write_str(" ")?; - self.semiaxis_y.to_css(dest)?; - dest.write_str(" ")?; - } - - dest.write_str("at ")?; - self.position.to_css(dest)?; - dest.write_str(")") - } -} - impl Parse for ShapeRadius { fn parse<'i, 't>( context: &ParserContext, diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 542976d45ba..a64216087d7 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -1129,11 +1129,13 @@ pub enum Appearance { ButtonArrowUp, /// A rectangular button that contains complex content /// like images (e.g. HTML