mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Implement XUL tree pseudo style resolution for stylo.
This commit is contained in:
parent
915890af77
commit
2dc714f0ac
9 changed files with 172 additions and 10 deletions
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue