Implement XUL tree pseudo style resolution for stylo.

This commit is contained in:
Xidorn Quan 2017-10-20 14:08:40 +11:00
parent 915890af77
commit 2dc714f0ac
9 changed files with 172 additions and 10 deletions

View file

@ -408,7 +408,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
RuleInclusion::All, RuleInclusion::All,
data.styles.primary(), data.styles.primary(),
/* is_probe = */ false, /* is_probe = */ false,
&ServoMetricsProvider) &ServoMetricsProvider,
/* matching_func = */ None)
.unwrap() .unwrap()
.clone() .clone()
} }

View file

@ -109,6 +109,10 @@ where
/// The current nesting level of selectors that we're matching. /// The current nesting level of selectors that we're matching.
pub nesting_level: usize, 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, quirks_mode: QuirksMode,
classes_and_ids_case_sensitivity: CaseSensitivity, classes_and_ids_case_sensitivity: CaseSensitivity,
_impl: ::std::marker::PhantomData<Impl>, _impl: ::std::marker::PhantomData<Impl>,
@ -152,6 +156,7 @@ where
classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(), classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
scope_element: None, scope_element: None,
nesting_level: 0, nesting_level: 0,
pseudo_element_matching_fn: None,
_impl: ::std::marker::PhantomData, _impl: ::std::marker::PhantomData,
} }
} }

View file

@ -387,9 +387,20 @@ where
if context.nesting_level == 0 && if context.nesting_level == 0 &&
context.matching_mode == MatchingMode::ForStatelessPseudoElement { context.matching_mode == MatchingMode::ForStatelessPseudoElement {
// Consume the pseudo. // Consume the pseudo.
let pseudo = iter.next().unwrap(); match *iter.next().unwrap() {
debug_assert!(matches!(*pseudo, Component::PseudoElement(..)), Component::PseudoElement(ref pseudo) => {
"Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"); 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 // The only other parser-allowed Component in this sequence is a state
// class. We just don't match in that case. // class. We just don't match in that case.

View file

@ -15,6 +15,7 @@ use gecko_bindings::structs::mozilla::css::ImageValue;
use gecko_bindings::structs::mozilla::css::URLValue; use gecko_bindings::structs::mozilla::css::URLValue;
use gecko_bindings::structs::mozilla::css::URLValueData; use gecko_bindings::structs::mozilla::css::URLValueData;
use gecko_bindings::structs::mozilla::AnonymousCounterStyle; use gecko_bindings::structs::mozilla::AnonymousCounterStyle;
use gecko_bindings::structs::mozilla::AtomArray;
use gecko_bindings::structs::mozilla::MallocSizeOf; use gecko_bindings::structs::mozilla::MallocSizeOf;
use gecko_bindings::structs::mozilla::OriginFlags; use gecko_bindings::structs::mozilla::OriginFlags;
use gecko_bindings::structs::mozilla::UniquePtr; use gecko_bindings::structs::mozilla::UniquePtr;
@ -2945,6 +2946,19 @@ extern "C" {
set: RawServoStyleSetBorrowed) set: RawServoStyleSetBorrowed)
-> ServoStyleContextStrong; -> 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" { extern "C" {
pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed, pub fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
primary_style: ServoStyleContextBorrowed); primary_style: ServoStyleContextBorrowed);

View file

@ -1326,6 +1326,48 @@ impl PseudoElement {
None None
} }
/// Construct a tree pseudo-element from atom and args.
#[inline]
pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
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 /// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet. /// user-agent stylesheet.
/// ///

View file

@ -4175,6 +4175,7 @@ pub mod root {
pub struct ShortcutKeyCandidate { pub struct ShortcutKeyCandidate {
_unused: [u8; 0], _unused: [u8; 0],
} }
pub type AtomArray = root::nsTArray<root::RefPtr<root::nsAtom>>;
/// EventStates is the class used to represent the event states of nsIContent /// EventStates is the class used to represent the event states of nsIContent
/// instances. These states are calculated by IntrinsicState() and /// instances. These states are calculated by IntrinsicState() and
/// ContentStatesChanged() has to be called when one of them changes thus /// ContentStatesChanged() has to be called when one of them changes thus
@ -22385,8 +22386,6 @@ pub mod root {
pub struct nsAttrValue { pub struct nsAttrValue {
pub mBits: usize, pub mBits: usize,
} }
pub type nsAttrValue_AtomArray =
root::nsTArray<root::RefPtr<root::nsAtom>>;
pub const nsAttrValue_ValueType_eSVGTypesBegin: pub const nsAttrValue_ValueType_eSVGTypesBegin:
root::nsAttrValue_ValueType = root::nsAttrValue_ValueType =
nsAttrValue_ValueType::eSVGAngle; nsAttrValue_ValueType::eSVGAngle;

View file

@ -203,6 +203,19 @@ impl PseudoElement {
None None
} }
/// Construct a tree pseudo-element from atom and args.
#[inline]
pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
% 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 /// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet. /// user-agent stylesheet.
/// ///

View file

@ -817,13 +817,21 @@ impl Stylist {
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
parent_style: &ComputedValues, parent_style: &ComputedValues,
is_probe: bool, is_probe: bool,
font_metrics: &FontMetricsProvider font_metrics: &FontMetricsProvider,
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> Option<Arc<ComputedValues>> ) -> Option<Arc<ComputedValues>>
where where
E: TElement, E: TElement,
{ {
let cascade_inputs = 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( self.compute_pseudo_element_style_with_inputs(
&cascade_inputs, &cascade_inputs,
pseudo, pseudo,
@ -977,7 +985,8 @@ impl Stylist {
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
is_probe: bool, is_probe: bool,
rule_inclusion: RuleInclusion rule_inclusion: RuleInclusion,
matching_fn: Option<&Fn(&PseudoElement) -> bool>,
) -> CascadeInputs ) -> CascadeInputs
where where
E: TElement E: TElement
@ -1024,6 +1033,7 @@ impl Stylist {
None, None,
self.quirks_mode, self.quirks_mode,
); );
matching_context.pseudo_element_matching_fn = matching_fn;
self.push_applicable_declarations( self.push_applicable_declarations(
element, element,
@ -1060,6 +1070,7 @@ impl Stylist {
VisitedHandlingMode::RelevantLinkVisited, VisitedHandlingMode::RelevantLinkVisited,
self.quirks_mode, self.quirks_mode,
); );
matching_context.pseudo_element_matching_fn = matching_fn;
self.push_applicable_declarations( self.push_applicable_declarations(
element, element,
@ -1287,6 +1298,7 @@ impl Stylist {
context.nth_index_cache.as_mut().map(|s| &mut **s), context.nth_index_cache.as_mut().map(|s| &mut **s),
stylist.quirks_mode, stylist.quirks_mode,
); );
matching_context.pseudo_element_matching_fn = context.pseudo_element_matching_fn;
map.get_all_matching_rules( map.get_all_matching_rules(
element, element,

View file

@ -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::{ServoStyleSheet, SheetParsingMode, nsAtom, nsCSSPropertyID};
use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule}; use style::gecko_bindings::structs::{nsCSSFontFaceRule, nsCSSCounterStyleRule};
use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair}; 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::IterationCompositeOperation;
use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf; use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
use style::gecko_bindings::structs::OriginFlags; use style::gecko_bindings::structs::OriginFlags;
@ -126,7 +127,7 @@ use style::rule_cache::RuleCacheConditions;
use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource}; use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
use style::selector_parser::{PseudoElementCascadeType, SelectorImpl}; use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked}; 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::style_adjuster::StyleAdjuster;
use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule}; use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule}; use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
@ -1911,6 +1912,7 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
inherited_style, inherited_style,
&*doc_data, &*doc_data,
is_probe, is_probe,
/* matching_func = */ None,
); );
match style { 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] #[no_mangle]
pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_SetExplicitStyle(element: RawGeckoElementBorrowed,
style: ServoStyleContextBorrowed) style: ServoStyleContextBorrowed)
@ -1965,6 +2027,7 @@ fn get_pseudo_style(
inherited_styles: Option<&ComputedValues>, inherited_styles: Option<&ComputedValues>,
doc_data: &PerDocumentStyleDataImpl, doc_data: &PerDocumentStyleDataImpl,
is_probe: bool, is_probe: bool,
matching_func: Option<&Fn(&PseudoElement) -> bool>,
) -> Option<Arc<ComputedValues>> { ) -> Option<Arc<ComputedValues>> {
let style = match pseudo.cascade_type() { let style = match pseudo.cascade_type() {
PseudoElementCascadeType::Eager => { PseudoElementCascadeType::Eager => {
@ -2038,6 +2101,7 @@ fn get_pseudo_style(
base, base,
is_probe, is_probe,
&metrics, &metrics,
matching_func,
) )
}, },
}; };
@ -3269,6 +3333,7 @@ pub extern "C" fn Servo_ResolveStyleLazily(
/* inherited_styles = */ None, /* inherited_styles = */ None,
&*data, &*data,
is_probe, is_probe,
/* matching_func = */ None,
) )
} }
None => Some(styles.primary().clone()), None => Some(styles.primary().clone()),