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 361556d1fc8..c733234b364 100644 --- a/components/style/gecko/generated/pseudo_element_definition.rs +++ b/components/style/gecko/generated/pseudo_element_definition.rs @@ -1326,6 +1326,48 @@ 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 { + 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. /// 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_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index 59d5e2dade0..d36adf18f94 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -203,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. /// diff --git a/components/style/stylist.rs b/components/style/stylist.rs index ca3510fdc4d..fa909680871 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -817,13 +817,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, @@ -977,7 +985,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 @@ -1024,6 +1033,7 @@ impl Stylist { None, self.quirks_mode, ); + matching_context.pseudo_element_matching_fn = matching_fn; self.push_applicable_declarations( element, @@ -1060,6 +1070,7 @@ impl Stylist { VisitedHandlingMode::RelevantLinkVisited, self.quirks_mode, ); + matching_context.pseudo_element_matching_fn = matching_fn; self.push_applicable_declarations( element, @@ -1287,6 +1298,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, diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 98b7cde6866..bb33a87fd08 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()),