diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index b94ee469ecb..c33e2479734 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -408,7 +408,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + RuleInclusion::All, data.styles.primary(), /* is_probe = */ false, - &ServoMetricsProvider) + &ServoMetricsProvider, + /* matching_func = */ None) .unwrap() .clone() } diff --git a/components/selectors/context.rs b/components/selectors/context.rs index fa6d4428f56..b129e8e1558 100644 --- a/components/selectors/context.rs +++ b/components/selectors/context.rs @@ -109,6 +109,10 @@ where /// The current nesting level of selectors that we're matching. pub nesting_level: usize, + /// An optional hook function for checking whether a pseudo-element + /// should match when matching_mode is ForStatelessPseudoElement. + pub pseudo_element_matching_fn: Option<&'a Fn(&Impl::PseudoElement) -> bool>, + quirks_mode: QuirksMode, classes_and_ids_case_sensitivity: CaseSensitivity, _impl: ::std::marker::PhantomData, @@ -152,6 +156,7 @@ where classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(), scope_element: None, nesting_level: 0, + pseudo_element_matching_fn: None, _impl: ::std::marker::PhantomData, } } diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index e7ae0184c47..f648594db04 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -387,9 +387,20 @@ where if context.nesting_level == 0 && context.matching_mode == MatchingMode::ForStatelessPseudoElement { // Consume the pseudo. - let pseudo = iter.next().unwrap(); - debug_assert!(matches!(*pseudo, Component::PseudoElement(..)), - "Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"); + match *iter.next().unwrap() { + Component::PseudoElement(ref pseudo) => { + if let Some(ref f) = context.pseudo_element_matching_fn { + if !f(pseudo) { + return false; + } + } + } + _ => { + debug_assert!(false, + "Used MatchingMode::ForStatelessPseudoElement \ + in a non-pseudo selector"); + } + } // The only other parser-allowed Component in this sequence is a state // class. We just don't match in that case. diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 3ffab7ccf56..b2414bf1ba4 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -15,6 +15,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue; use gecko_bindings::structs::mozilla::css::URLValue; use gecko_bindings::structs::mozilla::css::URLValueData; use gecko_bindings::structs::mozilla::AnonymousCounterStyle; +use gecko_bindings::structs::mozilla::AtomArray; use gecko_bindings::structs::mozilla::MallocSizeOf; use gecko_bindings::structs::mozilla::OriginFlags; use gecko_bindings::structs::mozilla::UniquePtr; @@ -2945,6 +2946,19 @@ extern "C" { set: RawServoStyleSetBorrowed) -> ServoStyleContextStrong; } +extern "C" { + pub fn Servo_ComputedValues_ResolveXULTreePseudoStyle(element: + RawGeckoElementBorrowed, + pseudo_tag: + *mut nsAtom, + inherited_style: + ServoStyleContextBorrowed, + input_word: + *const AtomArray, + set: + RawServoStyleSetBorrowed) + -> ServoStyleContextStrong; +} extern "C" { pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed, primary_style: ServoStyleContextBorrowed); diff --git a/components/style/gecko/generated/pseudo_element_definition.rs b/components/style/gecko/generated/pseudo_element_definition.rs index 3717059ad19..c733234b364 100644 --- a/components/style/gecko/generated/pseudo_element_definition.rs +++ b/components/style/gecko/generated/pseudo_element_definition.rs @@ -140,29 +140,29 @@ pub enum PseudoElement { /// :-moz-ruby-text-container RubyTextContainer, /// :-moz-tree-column - MozTreeColumn(Box<[String]>), + MozTreeColumn(Box<[Atom]>), /// :-moz-tree-row - MozTreeRow(Box<[String]>), + MozTreeRow(Box<[Atom]>), /// :-moz-tree-separator - MozTreeSeparator(Box<[String]>), + MozTreeSeparator(Box<[Atom]>), /// :-moz-tree-cell - MozTreeCell(Box<[String]>), + MozTreeCell(Box<[Atom]>), /// :-moz-tree-indentation - MozTreeIndentation(Box<[String]>), + MozTreeIndentation(Box<[Atom]>), /// :-moz-tree-line - MozTreeLine(Box<[String]>), + MozTreeLine(Box<[Atom]>), /// :-moz-tree-twisty - MozTreeTwisty(Box<[String]>), + MozTreeTwisty(Box<[Atom]>), /// :-moz-tree-image - MozTreeImage(Box<[String]>), + MozTreeImage(Box<[Atom]>), /// :-moz-tree-cell-text - MozTreeCellText(Box<[String]>), + MozTreeCellText(Box<[Atom]>), /// :-moz-tree-checkbox - MozTreeCheckbox(Box<[String]>), + MozTreeCheckbox(Box<[Atom]>), /// :-moz-tree-progressmeter - MozTreeProgressmeter(Box<[String]>), + MozTreeProgressmeter(Box<[Atom]>), /// :-moz-tree-drop-feedback - MozTreeDropFeedback(Box<[String]>), + MozTreeDropFeedback(Box<[Atom]>), /// :-moz-svg-marker-anon-child MozSVGMarkerAnonChild, /// :-moz-svg-outer-svg-anon-child @@ -185,6 +185,12 @@ pub const EAGER_PSEUDO_COUNT: usize = 4; /// The number of non-functional pseudo-elements. pub const SIMPLE_PSEUDO_COUNT: usize = 71; +/// The number of tree pseudo-elements. +pub const TREE_PSEUDO_COUNT: usize = 12; + +/// The number of all pseudo-elements. +pub const PSEUDO_COUNT: usize = 83; + /// The list of eager pseudos. pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ PseudoElement::Before, @@ -286,162 +292,184 @@ impl PseudoElement { } } - /// Returns an index if the pseudo-element is a simple (non-functional) - /// pseudo. + /// Returns an index of the pseudo-element. #[inline] - pub fn simple_index(&self) -> Option { + pub fn index(&self) -> usize { match *self { - PseudoElement::After => Some(0), - PseudoElement::Before => Some(1), - PseudoElement::Backdrop => Some(2), - PseudoElement::Cue => Some(3), - PseudoElement::FirstLetter => Some(4), - PseudoElement::FirstLine => Some(5), - PseudoElement::MozSelection => Some(6), - PseudoElement::MozFocusInner => Some(7), - PseudoElement::MozFocusOuter => Some(8), - PseudoElement::MozListBullet => Some(9), - PseudoElement::MozListNumber => Some(10), - PseudoElement::MozMathAnonymous => Some(11), - PseudoElement::MozNumberWrapper => Some(12), - PseudoElement::MozNumberText => Some(13), - PseudoElement::MozNumberSpinBox => Some(14), - PseudoElement::MozNumberSpinUp => Some(15), - PseudoElement::MozNumberSpinDown => Some(16), - PseudoElement::MozProgressBar => Some(17), - PseudoElement::MozRangeTrack => Some(18), - PseudoElement::MozRangeProgress => Some(19), - PseudoElement::MozRangeThumb => Some(20), - PseudoElement::MozMeterBar => Some(21), - PseudoElement::MozPlaceholder => Some(22), - PseudoElement::Placeholder => Some(23), - PseudoElement::MozColorSwatch => Some(24), - PseudoElement::MozText => Some(25), - PseudoElement::OofPlaceholder => Some(26), - PseudoElement::FirstLetterContinuation => Some(27), - PseudoElement::MozBlockInsideInlineWrapper => Some(28), - PseudoElement::MozMathMLAnonymousBlock => Some(29), - PseudoElement::MozXULAnonymousBlock => Some(30), - PseudoElement::HorizontalFramesetBorder => Some(31), - PseudoElement::VerticalFramesetBorder => Some(32), - PseudoElement::MozLineFrame => Some(33), - PseudoElement::ButtonContent => Some(34), - PseudoElement::CellContent => Some(35), - PseudoElement::DropDownList => Some(36), - PseudoElement::FieldsetContent => Some(37), - PseudoElement::FramesetBlank => Some(38), - PseudoElement::MozDisplayComboboxControlFrame => Some(39), - PseudoElement::HtmlCanvasContent => Some(40), - PseudoElement::InlineTable => Some(41), - PseudoElement::Table => Some(42), - PseudoElement::TableCell => Some(43), - PseudoElement::TableColGroup => Some(44), - PseudoElement::TableCol => Some(45), - PseudoElement::TableWrapper => Some(46), - PseudoElement::TableRowGroup => Some(47), - PseudoElement::TableRow => Some(48), - PseudoElement::Canvas => Some(49), - PseudoElement::PageBreak => Some(50), - PseudoElement::Page => Some(51), - PseudoElement::PageContent => Some(52), - PseudoElement::PageSequence => Some(53), - PseudoElement::ScrolledContent => Some(54), - PseudoElement::ScrolledCanvas => Some(55), - PseudoElement::ScrolledPageSequence => Some(56), - PseudoElement::ColumnContent => Some(57), - PseudoElement::Viewport => Some(58), - PseudoElement::ViewportScroll => Some(59), - PseudoElement::AnonymousFlexItem => Some(60), - PseudoElement::AnonymousGridItem => Some(61), - PseudoElement::Ruby => Some(62), - PseudoElement::RubyBase => Some(63), - PseudoElement::RubyBaseContainer => Some(64), - PseudoElement::RubyText => Some(65), - PseudoElement::RubyTextContainer => Some(66), - PseudoElement::MozSVGMarkerAnonChild => Some(67), - PseudoElement::MozSVGOuterSVGAnonChild => Some(68), - PseudoElement::MozSVGForeignContent => Some(69), - PseudoElement::MozSVGText => Some(70), - _ => None, + PseudoElement::After => 0, + PseudoElement::Before => 1, + PseudoElement::Backdrop => 2, + PseudoElement::Cue => 3, + PseudoElement::FirstLetter => 4, + PseudoElement::FirstLine => 5, + PseudoElement::MozSelection => 6, + PseudoElement::MozFocusInner => 7, + PseudoElement::MozFocusOuter => 8, + PseudoElement::MozListBullet => 9, + PseudoElement::MozListNumber => 10, + PseudoElement::MozMathAnonymous => 11, + PseudoElement::MozNumberWrapper => 12, + PseudoElement::MozNumberText => 13, + PseudoElement::MozNumberSpinBox => 14, + PseudoElement::MozNumberSpinUp => 15, + PseudoElement::MozNumberSpinDown => 16, + PseudoElement::MozProgressBar => 17, + PseudoElement::MozRangeTrack => 18, + PseudoElement::MozRangeProgress => 19, + PseudoElement::MozRangeThumb => 20, + PseudoElement::MozMeterBar => 21, + PseudoElement::MozPlaceholder => 22, + PseudoElement::Placeholder => 23, + PseudoElement::MozColorSwatch => 24, + PseudoElement::MozText => 25, + PseudoElement::OofPlaceholder => 26, + PseudoElement::FirstLetterContinuation => 27, + PseudoElement::MozBlockInsideInlineWrapper => 28, + PseudoElement::MozMathMLAnonymousBlock => 29, + PseudoElement::MozXULAnonymousBlock => 30, + PseudoElement::HorizontalFramesetBorder => 31, + PseudoElement::VerticalFramesetBorder => 32, + PseudoElement::MozLineFrame => 33, + PseudoElement::ButtonContent => 34, + PseudoElement::CellContent => 35, + PseudoElement::DropDownList => 36, + PseudoElement::FieldsetContent => 37, + PseudoElement::FramesetBlank => 38, + PseudoElement::MozDisplayComboboxControlFrame => 39, + PseudoElement::HtmlCanvasContent => 40, + PseudoElement::InlineTable => 41, + PseudoElement::Table => 42, + PseudoElement::TableCell => 43, + PseudoElement::TableColGroup => 44, + PseudoElement::TableCol => 45, + PseudoElement::TableWrapper => 46, + PseudoElement::TableRowGroup => 47, + PseudoElement::TableRow => 48, + PseudoElement::Canvas => 49, + PseudoElement::PageBreak => 50, + PseudoElement::Page => 51, + PseudoElement::PageContent => 52, + PseudoElement::PageSequence => 53, + PseudoElement::ScrolledContent => 54, + PseudoElement::ScrolledCanvas => 55, + PseudoElement::ScrolledPageSequence => 56, + PseudoElement::ColumnContent => 57, + PseudoElement::Viewport => 58, + PseudoElement::ViewportScroll => 59, + PseudoElement::AnonymousFlexItem => 60, + PseudoElement::AnonymousGridItem => 61, + PseudoElement::Ruby => 62, + PseudoElement::RubyBase => 63, + PseudoElement::RubyBaseContainer => 64, + PseudoElement::RubyText => 65, + PseudoElement::RubyTextContainer => 66, + PseudoElement::MozTreeColumn(..) => 67, + PseudoElement::MozTreeRow(..) => 68, + PseudoElement::MozTreeSeparator(..) => 69, + PseudoElement::MozTreeCell(..) => 70, + PseudoElement::MozTreeIndentation(..) => 71, + PseudoElement::MozTreeLine(..) => 72, + PseudoElement::MozTreeTwisty(..) => 73, + PseudoElement::MozTreeImage(..) => 74, + PseudoElement::MozTreeCellText(..) => 75, + PseudoElement::MozTreeCheckbox(..) => 76, + PseudoElement::MozTreeProgressmeter(..) => 77, + PseudoElement::MozTreeDropFeedback(..) => 78, + PseudoElement::MozSVGMarkerAnonChild => 79, + PseudoElement::MozSVGOuterSVGAnonChild => 80, + PseudoElement::MozSVGForeignContent => 81, + PseudoElement::MozSVGText => 82, } } /// Returns an array of `None` values. /// /// FIXME(emilio): Integer generics can't come soon enough. - pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + pub fn pseudo_none_array() -> [Option; PSEUDO_COUNT] { [ None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None, -None + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None ] } @@ -518,6 +546,26 @@ None PseudoElement::Before | PseudoElement::After | PseudoElement::FirstLine | PseudoElement::FirstLetter) } + /// Whether this pseudo-element is tree pseudo-element. + #[inline] + pub fn is_tree_pseudo_element(&self) -> bool { + match *self { + PseudoElement::MozTreeColumn(..) => true, + PseudoElement::MozTreeRow(..) => true, + PseudoElement::MozTreeSeparator(..) => true, + PseudoElement::MozTreeCell(..) => true, + PseudoElement::MozTreeIndentation(..) => true, + PseudoElement::MozTreeLine(..) => true, + PseudoElement::MozTreeTwisty(..) => true, + PseudoElement::MozTreeImage(..) => true, + PseudoElement::MozTreeCellText(..) => true, + PseudoElement::MozTreeCheckbox(..) => true, + PseudoElement::MozTreeProgressmeter(..) => true, + PseudoElement::MozTreeDropFeedback(..) => true, + _ => false, + } + } + /// Gets the flags associated to this pseudo-element, or 0 if it's an /// anonymous box. pub fn flags(&self) -> u32 { @@ -847,18 +895,18 @@ None PseudoElement::RubyBaseContainer => CSSPseudoElementType_InheritingAnonBox, PseudoElement::RubyText => CSSPseudoElementType_InheritingAnonBox, PseudoElement::RubyTextContainer => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeColumn(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeRow(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeCell(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeLine(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeImage(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeCellText(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType_InheritingAnonBox, - PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType_InheritingAnonBox, + PseudoElement::MozTreeColumn(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeRow(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeSeparator(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeCell(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeIndentation(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeLine(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeTwisty(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeImage(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeCellText(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeCheckbox(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeProgressmeter(..) => CSSPseudoElementType::XULTree, + PseudoElement::MozTreeDropFeedback(..) => CSSPseudoElementType::XULTree, PseudoElement::MozSVGMarkerAnonChild => CSSPseudoElementType_InheritingAnonBox, PseudoElement::MozSVGOuterSVGAnonChild => CSSPseudoElementType_InheritingAnonBox, PseudoElement::MozSVGForeignContent => CSSPseudoElementType_InheritingAnonBox, @@ -871,6 +919,26 @@ None (self.atom().as_ptr(), self.pseudo_type()) } + /// Get the argument list of a tree pseudo-element. + #[inline] + pub fn tree_pseudo_args(&self) -> Option<&[Atom]> { + match *self { + PseudoElement::MozTreeColumn(ref args) => Some(args), + PseudoElement::MozTreeRow(ref args) => Some(args), + PseudoElement::MozTreeSeparator(ref args) => Some(args), + PseudoElement::MozTreeCell(ref args) => Some(args), + PseudoElement::MozTreeIndentation(ref args) => Some(args), + PseudoElement::MozTreeLine(ref args) => Some(args), + PseudoElement::MozTreeTwisty(ref args) => Some(args), + PseudoElement::MozTreeImage(ref args) => Some(args), + PseudoElement::MozTreeCellText(ref args) => Some(args), + PseudoElement::MozTreeCheckbox(ref args) => Some(args), + PseudoElement::MozTreeProgressmeter(ref args) => Some(args), + PseudoElement::MozTreeDropFeedback(ref args) => Some(args), + _ => None, + } + } + /// Construct a pseudo-element from an `Atom`. #[inline] pub fn from_atom(atom: &Atom) -> Option { @@ -1258,6 +1326,48 @@ None None } + /// Construct a tree pseudo-element from atom and args. + #[inline] + pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option { + if atom == &atom!(":-moz-tree-column") { + return Some(PseudoElement::MozTreeColumn(args)); + } + if atom == &atom!(":-moz-tree-row") { + return Some(PseudoElement::MozTreeRow(args)); + } + if atom == &atom!(":-moz-tree-separator") { + return Some(PseudoElement::MozTreeSeparator(args)); + } + if atom == &atom!(":-moz-tree-cell") { + return Some(PseudoElement::MozTreeCell(args)); + } + if atom == &atom!(":-moz-tree-indentation") { + return Some(PseudoElement::MozTreeIndentation(args)); + } + if atom == &atom!(":-moz-tree-line") { + return Some(PseudoElement::MozTreeLine(args)); + } + if atom == &atom!(":-moz-tree-twisty") { + return Some(PseudoElement::MozTreeTwisty(args)); + } + if atom == &atom!(":-moz-tree-image") { + return Some(PseudoElement::MozTreeImage(args)); + } + if atom == &atom!(":-moz-tree-cell-text") { + return Some(PseudoElement::MozTreeCellText(args)); + } + if atom == &atom!(":-moz-tree-checkbox") { + return Some(PseudoElement::MozTreeCheckbox(args)); + } + if atom == &atom!(":-moz-tree-progressmeter") { + return Some(PseudoElement::MozTreeProgressmeter(args)); + } + if atom == &atom!(":-moz-tree-drop-feedback") { + return Some(PseudoElement::MozTreeDropFeedback(args)); + } + None + } + /// Constructs an atom from a string of text, and whether we're in a /// user-agent stylesheet. /// @@ -1636,7 +1746,7 @@ None /// /// Returns `None` if the pseudo-element is not recognized. #[inline] - pub fn tree_pseudo_element(name: &str, args: Box<[String]>) -> Option { + pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option { use std::ascii::AsciiExt; debug_assert!(name.starts_with("-moz-tree-")); let tree_part = &name[10..]; @@ -1768,31 +1878,20 @@ impl ToCss for PseudoElement { PseudoElement::MozSVGForeignContent => dest.write_str(":-moz-svg-foreign-content")?, PseudoElement::MozSVGText => dest.write_str(":-moz-svg-text")?, } - match *self { - PseudoElement::MozTreeColumn(ref args) | - PseudoElement::MozTreeRow(ref args) | - PseudoElement::MozTreeSeparator(ref args) | - PseudoElement::MozTreeCell(ref args) | - PseudoElement::MozTreeIndentation(ref args) | - PseudoElement::MozTreeLine(ref args) | - PseudoElement::MozTreeTwisty(ref args) | - PseudoElement::MozTreeImage(ref args) | - PseudoElement::MozTreeCellText(ref args) | - PseudoElement::MozTreeCheckbox(ref args) | - PseudoElement::MozTreeProgressmeter(ref args) | - PseudoElement::MozTreeDropFeedback(ref args) => { + if let Some(args) = self.tree_pseudo_args() { + if !args.is_empty() { dest.write_char('(')?; let mut iter = args.iter(); if let Some(first) = iter.next() { - serialize_identifier(first, dest)?; + serialize_identifier(&first.to_string(), dest)?; for item in iter { dest.write_str(", ")?; - serialize_identifier(item, dest)?; + serialize_identifier(&item.to_string(), dest)?; } } - dest.write_char(')') + dest.write_char(')')?; } - _ => Ok(()), } + Ok(()) } } diff --git a/components/style/gecko/generated/structs.rs b/components/style/gecko/generated/structs.rs index aadb270144d..9b2dd110f1b 100644 --- a/components/style/gecko/generated/structs.rs +++ b/components/style/gecko/generated/structs.rs @@ -4175,6 +4175,7 @@ pub mod root { pub struct ShortcutKeyCandidate { _unused: [u8; 0], } + pub type AtomArray = root::nsTArray>; /// EventStates is the class used to represent the event states of nsIContent /// instances. These states are calculated by IntrinsicState() and /// ContentStatesChanged() has to be called when one of them changes thus @@ -22385,8 +22386,6 @@ pub mod root { pub struct nsAttrValue { pub mBits: usize, } - pub type nsAttrValue_AtomArray = - root::nsTArray>; pub const nsAttrValue_ValueType_eSVGTypesBegin: root::nsAttrValue_ValueType = nsAttrValue_ValueType::eSVGAngle; diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index fba462171f7..ac18522ce43 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -46,7 +46,7 @@ impl PseudoElement { return PseudoElementCascadeType::Eager } - if self.is_anon_box() { + if self.is_precomputed() { return PseudoElementCascadeType::Precomputed } @@ -137,7 +137,7 @@ impl PseudoElement { /// Whether this pseudo-element is precomputed. #[inline] pub fn is_precomputed(&self) -> bool { - self.is_anon_box() + self.is_anon_box() && !self.is_tree_pseudo_element() } /// Covert non-canonical pseudo-element to canonical one, and keep a diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index eaa178ad06b..d36adf18f94 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -8,7 +8,7 @@ pub enum PseudoElement { % for pseudo in PSEUDOS: /// ${pseudo.value} % if pseudo.is_tree_pseudo_element(): - ${pseudo.capitalized()}(Box<[String]>), + ${pseudo.capitalized()}(Box<[Atom]>), % else: ${pseudo.capitalized()}, % endif @@ -27,6 +27,12 @@ pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)}; /// The number of non-functional pseudo-elements. pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)}; +/// The number of tree pseudo-elements. +pub const TREE_PSEUDO_COUNT: usize = ${len(TREE_PSEUDOS)}; + +/// The number of all pseudo-elements. +pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)}; + /// The list of eager pseudos. pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ % for eager_pseudo_name in EAGER_PSEUDOS: @@ -49,24 +55,22 @@ impl PseudoElement { } } - /// Returns an index if the pseudo-element is a simple (non-functional) - /// pseudo. + /// Returns an index of the pseudo-element. #[inline] - pub fn simple_index(&self) -> Option { + pub fn index(&self) -> usize { match *self { - % for i, pseudo in enumerate(SIMPLE_PSEUDOS): - ${pseudo_element_variant(pseudo)} => Some(${i}), + % for i, pseudo in enumerate(PSEUDOS): + ${pseudo_element_variant(pseudo)} => ${i}, % endfor - _ => None, } } /// Returns an array of `None` values. /// /// FIXME(emilio): Integer generics can't come soon enough. - pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + pub fn pseudo_none_array() -> [Option; PSEUDO_COUNT] { [ - ${",\n".join(["None" for pseudo in SIMPLE_PSEUDOS])} + ${",\n ".join(["None" for pseudo in PSEUDOS])} ] } @@ -90,6 +94,17 @@ impl PseudoElement { ${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))}) } + /// Whether this pseudo-element is tree pseudo-element. + #[inline] + pub fn is_tree_pseudo_element(&self) -> bool { + match *self { + % for pseudo in TREE_PSEUDOS: + ${pseudo_element_variant(pseudo)} => true, + % endfor + _ => false, + } + } + /// Gets the flags associated to this pseudo-element, or 0 if it's an /// anonymous box. pub fn flags(&self) -> u32 { @@ -132,7 +147,7 @@ impl PseudoElement { % if not pseudo.is_anon_box(): PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType::${pseudo.original_ident}, % elif pseudo.is_tree_pseudo_element(): - PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType_InheritingAnonBox, + PseudoElement::${pseudo.capitalized()}(..) => CSSPseudoElementType::XULTree, % elif pseudo.is_inheriting_anon_box(): PseudoElement::${pseudo.capitalized()} => CSSPseudoElementType_InheritingAnonBox, % else: @@ -147,6 +162,17 @@ impl PseudoElement { (self.atom().as_ptr(), self.pseudo_type()) } + /// Get the argument list of a tree pseudo-element. + #[inline] + pub fn tree_pseudo_args(&self) -> Option<<&[Atom]> { + match *self { + % for pseudo in TREE_PSEUDOS: + PseudoElement::${pseudo.capitalized()}(ref args) => Some(args), + % endfor + _ => None, + } + } + /// Construct a pseudo-element from an `Atom`. #[inline] pub fn from_atom(atom: &Atom) -> Option { @@ -177,6 +203,19 @@ impl PseudoElement { None } + /// Construct a tree pseudo-element from atom and args. + #[inline] + pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option { + % for pseudo in PSEUDOS: + % if pseudo.is_tree_pseudo_element(): + if atom == &atom!("${pseudo.value}") { + return Some(PseudoElement::${pseudo.capitalized()}(args)); + } + % endif + % endfor + None + } + /// Constructs an atom from a string of text, and whether we're in a /// user-agent stylesheet. /// @@ -207,7 +246,7 @@ impl PseudoElement { /// /// Returns `None` if the pseudo-element is not recognized. #[inline] - pub fn tree_pseudo_element(name: &str, args: Box<[String]>) -> Option { + pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option { use std::ascii::AsciiExt; debug_assert!(name.starts_with("-moz-tree-")); let tree_part = &name[10..]; @@ -228,21 +267,20 @@ impl ToCss for PseudoElement { ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?, % endfor } - match *self { - ${" |\n ".join("PseudoElement::{}(ref args)".format(pseudo.capitalized()) - for pseudo in TREE_PSEUDOS)} => { + if let Some(args) = self.tree_pseudo_args() { + if !args.is_empty() { dest.write_char('(')?; let mut iter = args.iter(); if let Some(first) = iter.next() { - serialize_identifier(first, dest)?; + serialize_identifier(&first.to_string(), dest)?; for item in iter { dest.write_str(", ")?; - serialize_identifier(item, dest)?; + serialize_identifier(&item.to_string(), dest)?; } } - dest.write_char(')') + dest.write_char(')')?; } - _ => Ok(()), } + Ok(()) } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 0fe124beafa..6f1e08b25da 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -17,7 +17,7 @@ use std::fmt; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{ParseError, StyleParseErrorKind}; -pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, SIMPLE_PSEUDO_COUNT}; +pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; bitflags! { @@ -388,6 +388,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>) -> Result> { PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) + .or_else(|| { + if name.starts_with("-moz-tree-") { + PseudoElement::tree_pseudo_element(&name, Box::new([])) + } else { + None + } + }) .ok_or(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()))) } @@ -401,7 +408,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { loop { let location = parser.current_source_location(); match parser.next() { - Ok(&Token::Ident(ref ident)) => args.push(ident.as_ref().to_owned()), + Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())), Ok(&Token::Comma) => {}, Ok(t) => return Err(location.new_unexpected_token_error(t.clone())), Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => break, diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 55dba1e3a3b..18b854894f6 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -102,16 +102,16 @@ pub enum PseudoElementCascadeType { Precomputed, } -/// A per-functional-pseudo map, from a given pseudo to a `T`. +/// A per-pseudo map, from a given pseudo to a `T`. #[derive(MallocSizeOf)] pub struct PerPseudoElementMap { - entries: [Option; SIMPLE_PSEUDO_COUNT], + entries: [Option; PSEUDO_COUNT], } impl Default for PerPseudoElementMap { fn default() -> Self { Self { - entries: PseudoElement::simple_pseudo_none_array(), + entries: PseudoElement::pseudo_none_array(), } } } @@ -137,11 +137,7 @@ where impl PerPseudoElementMap { /// Get an entry in the map. pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> { - let index = match pseudo.simple_index() { - Some(i) => i, - None => return None, - }; - self.entries[index].as_ref() + self.entries[pseudo.index()].as_ref() } /// Clear this enumerated array. @@ -161,13 +157,8 @@ impl PerPseudoElementMap { /// Set an entry value. /// /// Returns an error if the element is not a simple pseudo. - pub fn set(&mut self, pseudo: &PseudoElement, value: T) -> Result<(), ()> { - let index = match pseudo.simple_index() { - Some(i) => i, - None => return Err(()), - }; - self.entries[index] = Some(value); - Ok(()) + pub fn set(&mut self, pseudo: &PseudoElement, value: T) { + self.entries[pseudo.index()] = Some(value); } /// Get an entry for `pseudo`, or create it with calling `f`. @@ -175,18 +166,15 @@ impl PerPseudoElementMap { &mut self, pseudo: &PseudoElement, f: F, - ) -> Result<&mut T, ()> + ) -> &mut T where F: FnOnce() -> T, { - let index = match pseudo.simple_index() { - Some(i) => i, - None => return Err(()), - }; + let index = pseudo.index(); if self.entries[index].is_none() { self.entries[index] = Some(f()); } - Ok(self.entries[index].as_mut().unwrap()) + self.entries[index].as_mut().unwrap() } /// Get an iterator for the entries. diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 300bf861e6e..586d9c6b009 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -62,9 +62,8 @@ pub enum PseudoElement { ServoInlineAbsolute, } -/// The count of simple (non-functional) pseudo-elements (that is, all -/// pseudo-elements for now). -pub const SIMPLE_PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; +/// The count of all pseudo-elements. +pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; impl ::selectors::parser::PseudoElement for PseudoElement { type Impl = SelectorImpl; @@ -110,12 +109,12 @@ impl PseudoElement { /// An index for this pseudo-element to be indexed in an enumerated array. #[inline] - pub fn simple_index(&self) -> Option { - Some(self.clone() as usize) + pub fn index(&self) -> usize { + self.clone() as usize } - /// An array of `None`, one per simple pseudo-element. - pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + /// An array of `None`, one per pseudo-element. + pub fn pseudo_none_array() -> [Option; PSEUDO_COUNT] { Default::default() } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 20ab38b81b7..038b7ceb888 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -819,13 +819,21 @@ impl Stylist { rule_inclusion: RuleInclusion, parent_style: &ComputedValues, is_probe: bool, - font_metrics: &FontMetricsProvider + font_metrics: &FontMetricsProvider, + matching_fn: Option<&Fn(&PseudoElement) -> bool>, ) -> Option> where E: TElement, { let cascade_inputs = - self.lazy_pseudo_rules(guards, element, pseudo, is_probe, rule_inclusion); + self.lazy_pseudo_rules( + guards, + element, + pseudo, + is_probe, + rule_inclusion, + matching_fn + ); self.compute_pseudo_element_style_with_inputs( &cascade_inputs, pseudo, @@ -979,7 +987,8 @@ impl Stylist { element: &E, pseudo: &PseudoElement, is_probe: bool, - rule_inclusion: RuleInclusion + rule_inclusion: RuleInclusion, + matching_fn: Option<&Fn(&PseudoElement) -> bool>, ) -> CascadeInputs where E: TElement @@ -1026,6 +1035,7 @@ impl Stylist { None, self.quirks_mode, ); + matching_context.pseudo_element_matching_fn = matching_fn; self.push_applicable_declarations( element, @@ -1062,6 +1072,7 @@ impl Stylist { VisitedHandlingMode::RelevantLinkVisited, self.quirks_mode, ); + matching_context.pseudo_element_matching_fn = matching_fn; self.push_applicable_declarations( element, @@ -1289,6 +1300,7 @@ impl Stylist { context.nth_index_cache.as_mut().map(|s| &mut **s), stylist.quirks_mode, ); + matching_context.pseudo_element_matching_fn = context.pseudo_element_matching_fn; map.get_all_matching_rules( element, @@ -1994,19 +2006,13 @@ impl CascadeData { let map = match selector.pseudo_element() { Some(pseudo) if pseudo.is_precomputed() => { - if !selector.is_universal() || - !matches!(origin, Origin::UserAgent) { - // ::-moz-tree selectors may appear in - // non-UA sheets (even though they never - // match). - continue; - } + debug_assert!(selector.is_universal()); + debug_assert!(matches!(origin, Origin::UserAgent)); precomputed_pseudo_element_decls .as_mut() .expect("Expected precomputed declarations for the UA level") .get_or_insert_with(&pseudo.canonical(), Vec::new) - .expect("Unexpected tree pseudo-element?") .push(ApplicableDeclarationBlock::new( StyleSource::Style(locked.clone()), self.rules_source_order, @@ -2023,7 +2029,7 @@ impl CascadeData { let mut map = Box::new(SelectorMap::new()); map.begin_mutation(); map - }).expect("Unexpected tree pseudo-element?") + }) } }; diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index c8a5140fcfd..23cd3f6df85 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -84,6 +84,7 @@ use style::gecko_bindings::structs::{RawServoStyleRule, ServoStyleContextStrong, use style::gecko_bindings::structs::{ServoStyleSheet, SheetParsingMode, nsAtom, nsCSSPropertyID}; use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair}; +use style::gecko_bindings::structs::AtomArray; use style::gecko_bindings::structs::IterationCompositeOperation; use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf; use style::gecko_bindings::structs::OriginFlags; @@ -126,7 +127,7 @@ use style::rule_cache::RuleCacheConditions; use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource}; use style::selector_parser::{PseudoElementCascadeType, SelectorImpl}; use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked}; -use style::string_cache::Atom; +use style::string_cache::{Atom, WeakAtom}; use style::style_adjuster::StyleAdjuster; use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule}; use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule}; @@ -1911,6 +1912,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, inherited_style, &*doc_data, is_probe, + /* matching_func = */ None, ); match style { @@ -1922,6 +1924,66 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, } } +fn debug_atom_array(atoms: &AtomArray) -> String { + let mut result = String::from("["); + for atom in atoms.iter() { + if atom.mRawPtr.is_null() { + result += "(null), "; + } else { + let atom = unsafe { WeakAtom::new(atom.mRawPtr) }; + write!(result, "{}, ", atom).unwrap(); + } + } + result.push(']'); + result +} + +#[no_mangle] +pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle( + element: RawGeckoElementBorrowed, + pseudo_tag: *mut nsAtom, + inherited_style: ServoStyleContextBorrowed, + input_word: *const AtomArray, + raw_data: RawServoStyleSetBorrowed +) -> ServoStyleContextStrong { + let element = GeckoElement(element); + let data = element.borrow_data() + .expect("Calling ResolveXULTreePseudoStyle on unstyled element?"); + + let pseudo = unsafe { + Atom::with(pseudo_tag, |atom| { + PseudoElement::from_tree_pseudo_atom(atom, Box::new([])) + }).expect("ResolveXULTreePseudoStyle with a non-tree pseudo?") + }; + let input_word = unsafe { input_word.as_ref().unwrap() }; + + let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); + + debug!("ResolveXULTreePseudoStyle: {:?} {:?} {}", + element, pseudo, debug_atom_array(input_word)); + + let matching_fn = |pseudo: &PseudoElement| { + let args = pseudo.tree_pseudo_args().expect("Not a tree pseudo-element?"); + args.iter().all(|atom| { + input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr) + }) + }; + + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + get_pseudo_style( + &guard, + element, + &pseudo, + RuleInclusion::All, + &data.styles, + Some(inherited_style), + &*doc_data, + /* is_probe = */ false, + Some(&matching_fn), + ).unwrap().into() +} + #[no_mangle] pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed, style: ServoStyleContextBorrowed) @@ -1965,6 +2027,7 @@ fn get_pseudo_style( inherited_styles: Option<&ComputedValues>, doc_data: &PerDocumentStyleDataImpl, is_probe: bool, + matching_func: Option<&Fn(&PseudoElement) -> bool>, ) -> Option> { let style = match pseudo.cascade_type() { PseudoElementCascadeType::Eager => { @@ -2038,6 +2101,7 @@ fn get_pseudo_style( base, is_probe, &metrics, + matching_func, ) }, }; @@ -3269,6 +3333,7 @@ pub extern "C" fn Servo_ResolveStyleLazily( /* inherited_styles = */ None, &*data, is_probe, + /* matching_func = */ None, ) } None => Some(styles.primary().clone()),