diff options
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/document.rs | 12 | ||||
-rw-r--r-- | components/script/layout_wrapper.rs | 3 | ||||
-rw-r--r-- | components/style/attr.rs | 2 | ||||
-rw-r--r-- | components/style/dom.rs | 5 | ||||
-rw-r--r-- | components/style/restyle_hints.rs | 327 | ||||
-rw-r--r-- | components/style/selector_impl.rs | 12 | ||||
-rw-r--r-- | components/style/selector_matching.rs | 12 | ||||
-rw-r--r-- | components/style/servo_selector_impl.rs | 84 | ||||
-rwxr-xr-x | ports/geckolib/gecko_bindings/tools/regen.py | 5 | ||||
-rw-r--r-- | ports/geckolib/glue.rs | 22 | ||||
-rw-r--r-- | ports/geckolib/lib.rs | 2 | ||||
-rw-r--r-- | ports/geckolib/snapshot.rs | 149 | ||||
-rw-r--r-- | ports/geckolib/snapshot_helpers.rs | 53 | ||||
-rw-r--r-- | ports/geckolib/wrapper.rs | 94 |
15 files changed, 552 insertions, 233 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 53912f63508..3c13572bf37 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -89,8 +89,7 @@ use string_cache::{Atom, Namespace, QualName}; use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto}; use style::element_state::*; use style::properties::PropertyDeclarationBlock; -use style::restyle_hints::ElementSnapshot; -use style::selector_impl::PseudoElement; +use style::selector_impl::{PseudoElement, ElementSnapshot}; use style::values::specified::Length; use url::Origin as UrlOrigin; use url::Url; diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 37037d8b35a..28edce553ad 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -122,7 +122,7 @@ use std::sync::Arc; use string_cache::{Atom, QualName}; use style::attr::AttrValue; use style::context::ReflowGoal; -use style::restyle_hints::ElementSnapshot; +use style::selector_impl::ElementSnapshot; use style::str::{split_html_space_chars, str_join}; use style::stylesheets::Stylesheet; use time; @@ -1851,7 +1851,10 @@ impl Document { pub fn element_state_will_change(&self, el: &Element) { let mut map = self.modified_elements.borrow_mut(); - let snapshot = map.entry(JS::from_ref(el)).or_insert(ElementSnapshot::new()); + let snapshot = map.entry(JS::from_ref(el)) + .or_insert_with(|| { + ElementSnapshot::new(el.html_element_in_html_document()) + }); if snapshot.state.is_none() { snapshot.state = Some(el.state()); } @@ -1859,7 +1862,10 @@ impl Document { pub fn element_attr_will_change(&self, el: &Element) { let mut map = self.modified_elements.borrow_mut(); - let mut snapshot = map.entry(JS::from_ref(el)).or_insert(ElementSnapshot::new()); + let mut snapshot = map.entry(JS::from_ref(el)) + .or_insert_with(|| { + ElementSnapshot::new(el.html_element_in_html_document()) + }); if snapshot.attrs.is_none() { let attrs = el.attrs() .iter() diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 8f22c369e4c..df44774dd5a 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -60,8 +60,7 @@ use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement use style::element_state::*; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; use style::refcell::{Ref, RefCell, RefMut}; -use style::restyle_hints::ElementSnapshot; -use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl}; +use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, ServoSelectorImpl}; use style::sink::Push; use style::str::is_whitespace; use url::Url; diff --git a/components/style/attr.rs b/components/style/attr.rs index 6ca278d7d2c..af7cfa015f6 100644 --- a/components/style/attr.rs +++ b/components/style/attr.rs @@ -29,7 +29,7 @@ pub enum LengthOrPercentageOrAuto { Length(Au), } -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum AttrValue { String(String), diff --git a/components/style/dom.rs b/components/style/dom.rs index ee40e8a8c3c..5c5f3905d8d 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -11,7 +11,7 @@ use data::PrivateStyleData; use element_state::ElementState; use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock}; use refcell::{Ref, RefMut}; -use restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; +use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use selector_impl::{ElementExt, SelectorImplExt}; use selectors::Element; use selectors::matching::DeclarationBlock; @@ -192,7 +192,8 @@ pub trait TDocument : Sized + Copy + Clone { fn root_node(&self) -> Option<Self::ConcreteNode>; - fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, ElementSnapshot)>; + fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, + <Self::ConcreteElement as ElementExt>::Snapshot)>; } pub trait PresentationalHintsSynthetizer { diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 3af92457d6b..5dcaa147453 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -4,24 +4,21 @@ //! Restyle hints: an optimization to avoid unnecessarily matching selectors. -use attr::{AttrIdentifier, AttrValue}; use element_state::*; -use selector_impl::{SelectorImplExt, TheSelectorImpl, AttrString}; +use selector_impl::{ElementExt, SelectorImplExt, TheSelectorImpl, AttrString}; use selectors::matching::matches_compound_selector; use selectors::parser::{AttrSelector, Combinator, CompoundSelector, SelectorImpl, SimpleSelector}; -use selectors::{Element, MatchAttrGeneric}; -#[cfg(feature = "gecko")] use selectors::MatchAttr; +use selectors::{Element, MatchAttr}; use std::clone::Clone; use std::sync::Arc; -use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; +use string_cache::{Atom, BorrowedAtom, BorrowedNamespace}; /// When the ElementState of an element (like IN_HOVER_STATE) changes, certain /// pseudo-classes (like :hover) may require us to restyle that element, its /// siblings, and/or its descendants. Similarly, when various attributes of an -/// element change, we may also need to restyle things with id, class, and attribute -/// selectors. Doing this conservatively is expensive, and so we use RestyleHints to -/// short-circuit work we know is unnecessary. - +/// element change, we may also need to restyle things with id, class, and +/// attribute selectors. Doing this conservatively is expensive, and so we use +/// RestyleHints to short-circuit work we know is unnecessary. bitflags! { pub flags RestyleHint: u8 { #[doc = "Rerun selector matching on the element."] @@ -35,143 +32,153 @@ bitflags! { } } -/// In order to compute restyle hints, we perform a selector match against a list of partial -/// selectors whose rightmost simple selector may be sensitive to the thing being changed. We -/// do this matching twice, once for the element as it exists now and once for the element as it -/// existed at the time of the last restyle. If the results of the selector match differ, that means -/// that the given partial selector is sensitive to the change, and we compute a restyle hint -/// based on its combinator. +/// In order to compute restyle hints, we perform a selector match against a +/// list of partial selectors whose rightmost simple selector may be sensitive +/// to the thing being changed. We do this matching twice, once for the element +/// as it exists now and once for the element as it existed at the time of the +/// last restyle. If the results of the selector match differ, that means that +/// the given partial selector is sensitive to the change, and we compute a +/// restyle hint based on its combinator. /// -/// In order to run selector matching against the old element state, we generate a wrapper for -/// the element which claims to have the old state. This is the ElementWrapper logic below. +/// In order to run selector matching against the old element state, we generate +/// a wrapper for the element which claims to have the old state. This is the +/// ElementWrapper logic below. /// -/// Gecko does this differently for element states, and passes a mask called mStateMask, which -/// indicates the states that need to be ignored during selector matching. This saves an ElementWrapper -/// allocation and an additional selector match call at the expense of additional complexity inside -/// the selector matching logic. This only works for boolean states though, so we still need to -/// take the ElementWrapper approach for attribute-dependent style. So we do it the same both ways for -/// now to reduce complexity, but it's worth measuring the performance impact (if any) of the -/// mStateMask approach. - -#[derive(Clone)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ElementSnapshot { - pub state: Option<ElementState>, - pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>, +/// Gecko does this differently for element states, and passes a mask called +/// mStateMask, which indicates the states that need to be ignored during +/// selector matching. This saves an ElementWrapper allocation and an additional +/// selector match call at the expense of additional complexity inside the +/// selector matching logic. This only works for boolean states though, so we +/// still need to take the ElementWrapper approach for attribute-dependent +/// style. So we do it the same both ways for now to reduce complexity, but it's +/// worth measuring the performance impact (if any) of the mStateMask approach. +pub trait ElementSnapshot : Sized + MatchAttr { + /// The state of the snapshot, if any. + fn state(&self) -> Option<ElementState>; + + /// If this snapshot contains attribute information. + fn has_attrs(&self) -> bool; + + /// The ID attribute per this snapshot. Should only be called if + /// `has_attrs()` returns true. + fn id_attr(&self) -> Option<Atom>; + + /// Whether this snapshot contains the class `name`. Should only be called + /// if `has_attrs()` returns true. + fn has_class(&self, name: &Atom) -> bool; + + /// A callback that should be called for each class of the snapshot. Should + /// only be called if `has_attrs()` returns true. + fn each_class<F>(&self, F) + where F: FnMut(&Atom); } -impl ElementSnapshot { - pub fn new() -> ElementSnapshot { - EMPTY_SNAPSHOT.clone() - } - - // Gets an attribute matching |namespace| and |name|, if any. Panics if |attrs| is None. - pub fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&AttrValue> { - self.attrs.as_ref().unwrap().iter() - .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace) - .map(|&(_, ref v)| v) - } - - // Gets an attribute matching |name| if any, ignoring namespace. Panics if |attrs| is None. - pub fn get_attr_ignore_ns(&self, name: &Atom) -> Option<&AttrValue> { - self.attrs.as_ref().unwrap().iter() - .find(|&&(ref ident, _)| ident.local_name == *name) - .map(|&(_, ref v)| v) - } -} - -static EMPTY_SNAPSHOT: ElementSnapshot = ElementSnapshot { state: None, attrs: None }; - -// FIXME(bholley): This implementation isn't going to work for geckolib, because -// it's fundamentally based on get_attr/match_attr, which we don't want to support -// that configuration due to the overhead of converting between UTF-16 and UTF-8. -// We'll need to figure something out when we start using restyle hints with -// geckolib, but in the mean time we can just use the trait parameters to -// specialize it to the Servo configuration. struct ElementWrapper<'a, E> - where E: Element<AttrString=AttrString>, - E::Impl: SelectorImplExt { + where E: ElementExt +{ element: E, - snapshot: &'a ElementSnapshot, + snapshot: Option<&'a E::Snapshot>, } impl<'a, E> ElementWrapper<'a, E> - where E: Element<AttrString=AttrString>, - E::Impl: SelectorImplExt { + where E: ElementExt +{ pub fn new(el: E) -> ElementWrapper<'a, E> { - ElementWrapper { element: el, snapshot: &EMPTY_SNAPSHOT } + ElementWrapper { element: el, snapshot: None } } - pub fn new_with_snapshot(el: E, snapshot: &'a ElementSnapshot) -> ElementWrapper<'a, E> { - ElementWrapper { element: el, snapshot: snapshot } + pub fn new_with_snapshot(el: E, snapshot: &'a E::Snapshot) -> ElementWrapper<'a, E> { + ElementWrapper { element: el, snapshot: Some(snapshot) } } } -#[cfg(not(feature = "gecko"))] -impl<'a, E> MatchAttrGeneric for ElementWrapper<'a, E> - where E: Element<AttrString=AttrString>, - E: MatchAttrGeneric, - E::Impl: SelectorImplExt { - fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - use selectors::parser::NamespaceConstraint; - match self.snapshot.attrs { - Some(_) => { - let html = self.is_html_element_in_html_document(); - let local_name = if html { &attr.lower_name } else { &attr.name }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name), - NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name), - }.map_or(false, |v| test(v)) - }, - None => self.element.match_attr(attr, test) - } - } -} - -#[cfg(feature = "gecko")] impl<'a, E> MatchAttr for ElementWrapper<'a, E> - where E: Element<AttrString=AttrString>, - E::Impl: SelectorImplExt { - type AttrString = AttrString; - - fn match_attr_has(&self, _attr: &AttrSelector) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + where E: ElementExt<AttrString=AttrString>, +{ + type AttrString = E::AttrString; + + fn match_attr_has(&self, attr: &AttrSelector) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_has(attr), + _ => self.element.match_attr_has(attr) + } } - fn match_attr_equals(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_equals(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_equals(attr, value), + _ => self.element.match_attr_equals(attr, value) + } } - fn match_attr_equals_ignore_ascii_case(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_equals_ignore_ascii_case(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_equals_ignore_ascii_case(attr, value), + _ => self.element.match_attr_equals_ignore_ascii_case(attr, value) + } } - fn match_attr_includes(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_includes(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_includes(attr, value), + _ => self.element.match_attr_includes(attr, value) + } } - fn match_attr_dash(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_dash(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_dash(attr, value), + _ => self.element.match_attr_dash(attr, value) + } } - fn match_attr_prefix(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_prefix(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_prefix(attr, value), + _ => self.element.match_attr_prefix(attr, value) + } } - fn match_attr_substring(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_substring(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_substring(attr, value), + _ => self.element.match_attr_substring(attr, value) + } } - fn match_attr_suffix(&self, _attr: &AttrSelector, _value: &Self::AttrString) -> bool { - panic!("Not implemented for Gecko - this system will need to be redesigned"); + fn match_attr_suffix(&self, + attr: &AttrSelector, + value: &Self::AttrString) -> bool { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_suffix(attr, value), + _ => self.element.match_attr_suffix(attr, value) + } } } impl<'a, E> Element for ElementWrapper<'a, E> - where E: Element<AttrString=AttrString>, - E: MatchAttrGeneric, - E::Impl: SelectorImplExt { + where E: ElementExt<AttrString=AttrString>, + E::Impl: SelectorImplExt<AttrString=AttrString> { type Impl = E::Impl; fn match_non_ts_pseudo_class(&self, @@ -180,9 +187,9 @@ impl<'a, E> Element for ElementWrapper<'a, E> if flag == ElementState::empty() { self.element.match_non_ts_pseudo_class(pseudo_class) } else { - match self.snapshot.state { - Some(s) => s.contains(flag), - None => self.element.match_non_ts_pseudo_class(pseudo_class) + match self.snapshot.and_then(|s| s.state()) { + Some(snapshot_state) => snapshot_state.contains(flag), + _ => self.element.match_non_ts_pseudo_class(pseudo_class) } } } @@ -190,54 +197,65 @@ impl<'a, E> Element for ElementWrapper<'a, E> fn parent_element(&self) -> Option<Self> { self.element.parent_element().map(ElementWrapper::new) } + fn first_child_element(&self) -> Option<Self> { self.element.first_child_element().map(ElementWrapper::new) } + fn last_child_element(&self) -> Option<Self> { self.element.last_child_element().map(ElementWrapper::new) } + fn prev_sibling_element(&self) -> Option<Self> { self.element.prev_sibling_element().map(ElementWrapper::new) } + fn next_sibling_element(&self) -> Option<Self> { self.element.next_sibling_element().map(ElementWrapper::new) } + fn is_html_element_in_html_document(&self) -> bool { self.element.is_html_element_in_html_document() } + fn get_local_name(&self) -> BorrowedAtom { self.element.get_local_name() } + fn get_namespace(&self) -> BorrowedNamespace { self.element.get_namespace() } + fn get_id(&self) -> Option<Atom> { - match self.snapshot.attrs { - Some(_) => self.snapshot.get_attr(&ns!(), &atom!("id")).map(|value| value.as_atom().clone()), - None => self.element.get_id(), + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.id_attr(), + _ => self.element.get_id() } } + fn has_class(&self, name: &Atom) -> bool { - match self.snapshot.attrs { - Some(_) => self.snapshot.get_attr(&ns!(), &atom!("class")) - .map_or(false, |v| { v.as_tokens().iter().any(|atom| atom == name) }), - None => self.element.has_class(name), + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.has_class(name), + _ => self.element.has_class(name) } } + fn is_empty(&self) -> bool { self.element.is_empty() } + fn is_root(&self) -> bool { self.element.is_root() } - fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) { - match self.snapshot.attrs { - Some(_) => { - if let Some(v) = self.snapshot.get_attr(&ns!(), &atom!("class")) { - for c in v.as_tokens() { callback(c) } - } - } - None => self.element.each_class(callback), + + fn each_class<F>(&self, callback: F) + where F: FnMut(&Atom) { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.each_class(callback), + _ => self.element.each_class(callback) } } } @@ -296,24 +314,24 @@ impl Sensitivities { } } -// Mapping between (partial) CompoundSelectors (and the combinator to their right) -// and the states and attributes they depend on. -// -// In general, for all selectors in all applicable stylesheets of the form: -// -// |a _ b _ c _ d _ e| -// -// Where: -// * |b| and |d| are simple selectors that depend on state (like :hover) or -// attributes (like [attr...], .foo, or #foo). -// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on -// state or attributes. -// -// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|, even -// though those selectors may not appear on their own in any stylesheet. This allows -// us to quickly scan through the dependency sites of all style rules and determine the -// maximum effect that a given state or attribute change may have on the style of -// elements in the document. +/// Mapping between (partial) CompoundSelectors (and the combinator to their +/// right) and the states and attributes they depend on. +/// +/// In general, for all selectors in all applicable stylesheets of the form: +/// +/// |a _ b _ c _ d _ e| +/// +/// Where: +/// * |b| and |d| are simple selectors that depend on state (like :hover) or +/// attributes (like [attr...], .foo, or #foo). +/// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on +/// state or attributes. +/// +/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|, +/// even though those selectors may not appear on their own in any stylesheet. +/// This allows us to quickly scan through the dependency sites of all style +/// rules and determine the maximum effect that a given state or attribute +/// change may have on the style of elements in the document. #[derive(Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] struct Dependency { @@ -368,11 +386,14 @@ impl DependencySet { } impl DependencySet { - pub fn compute_hint<E>(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) - -> RestyleHint - where E: Element<Impl=TheSelectorImpl, AttrString=AttrString> + Clone + MatchAttrGeneric { - let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state); - let attrs_changed = snapshot.attrs.is_some(); + pub fn compute_hint<E>(&self, el: &E, + snapshot: &E::Snapshot, + current_state: ElementState) + -> RestyleHint + where E: ElementExt + Clone + { + let state_changes = snapshot.state().map_or_else(ElementState::empty, |old_state| current_state ^ old_state); + let attrs_changed = snapshot.has_attrs(); let mut hint = RestyleHint::empty(); for dep in &self.deps { if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) { diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs index a95499092cf..64b5725ebd0 100644 --- a/components/style/selector_impl.rs +++ b/components/style/selector_impl.rs @@ -5,6 +5,7 @@ //! The pseudo-classes and pseudo-elements supported by the style system. use element_state::ElementState; +use restyle_hints; use selectors::Element; use selectors::parser::SelectorImpl; use std::fmt::Debug; @@ -13,13 +14,16 @@ use stylesheets::Stylesheet; pub type AttrString = <TheSelectorImpl as SelectorImpl>::AttrString; #[cfg(feature = "servo")] -pub use servo_selector_impl::ServoSelectorImpl; +pub use servo_selector_impl::*; #[cfg(feature = "servo")] -pub use servo_selector_impl::{ServoSelectorImpl as TheSelectorImpl, PseudoElement, NonTSPseudoClass}; +pub use servo_selector_impl::{ServoSelectorImpl as TheSelectorImpl, ServoElementSnapshot as ElementSnapshot}; #[cfg(feature = "gecko")] -pub use gecko_selector_impl::{GeckoSelectorImpl as TheSelectorImpl, PseudoElement, NonTSPseudoClass}; +pub use gecko_selector_impl::*; + +#[cfg(feature = "gecko")] +pub use gecko_selector_impl::{GeckoSelectorImpl as TheSelectorImpl, GeckoElementSnapshot as ElementSnapshot}; /// This function determines if a pseudo-element is eagerly cascaded or not. /// @@ -69,6 +73,8 @@ impl PseudoElementCascadeType { } pub trait ElementExt: Element<Impl=TheSelectorImpl, AttrString=<TheSelectorImpl as SelectorImpl>::AttrString> { + type Snapshot: restyle_hints::ElementSnapshot<AttrString = Self::AttrString> + 'static; + fn is_link(&self) -> bool; } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 1e87ade3f5f..5451bae28bf 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -10,12 +10,12 @@ use error_reporting::StdoutErrorReporter; use keyframes::KeyframesAnimation; use media_queries::{Device, MediaType}; use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues}; -use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet}; -use selector_impl::{SelectorImplExt, TheSelectorImpl, PseudoElement, AttrString}; +use restyle_hints::{RestyleHint, DependencySet}; +use selector_impl::{ElementExt, SelectorImplExt, TheSelectorImpl, PseudoElement, AttrString}; +use selectors::Element; use selectors::bloom::BloomFilter; use selectors::matching::DeclarationBlock as GenericDeclarationBlock; use selectors::matching::{Rule, SelectorMap}; -use selectors::{Element, MatchAttrGeneric}; use sink::Push; use smallvec::VecLike; use std::collections::HashMap; @@ -402,15 +402,15 @@ impl Stylist { } pub fn compute_restyle_hint<E>(&self, element: &E, - snapshot: &ElementSnapshot, + snapshot: &E::Snapshot, // NB: We need to pass current_state as an argument because // selectors::Element doesn't provide access to ElementState // directly, and computing it from the ElementState would be // more expensive than getting it directly from the caller. current_state: ElementState) -> RestyleHint - where E: Element<Impl=TheSelectorImpl, AttrString=AttrString> - + Clone + MatchAttrGeneric { + where E: ElementExt + Clone + { self.state_deps.compute_hint(element, snapshot, current_state) } } diff --git a/components/style/servo_selector_impl.rs b/components/style/servo_selector_impl.rs index d1fdaf0336c..cee046e62f3 100644 --- a/components/style/servo_selector_impl.rs +++ b/components/style/servo_selector_impl.rs @@ -2,13 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use attr::{AttrIdentifier, AttrValue}; use element_state::ElementState; use error_reporting::StdoutErrorReporter; use parser::ParserContextExtraData; +use restyle_hints::ElementSnapshot; use selector_impl::{SelectorImplExt, ElementExt, PseudoElementCascadeType, TheSelectorImpl}; -use selectors::Element; -use selectors::parser::{ParserContext, SelectorImpl}; +use selectors::parser::{AttrSelector, ParserContext, SelectorImpl}; +use selectors::{Element, MatchAttrGeneric}; use std::process; +use string_cache::{Atom, Namespace}; use stylesheets::{Stylesheet, Origin}; use url::Url; use util::opts; @@ -189,7 +192,84 @@ impl SelectorImplExt for ServoSelectorImpl { } } +/// Servo's version of an element snapshot. +#[derive(Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct ServoElementSnapshot { + pub state: Option<ElementState>, + pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>, + pub is_html_element_in_html_document: bool, +} + +impl ServoElementSnapshot { + pub fn new(is_html_element_in_html_document: bool) -> Self { + ServoElementSnapshot { + state: None, + attrs: None, + is_html_element_in_html_document: is_html_element_in_html_document, + } + } + + fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&AttrValue> { + self.attrs.as_ref().unwrap().iter() + .find(|&&(ref ident, _)| ident.local_name == *name && + ident.namespace == *namespace) + .map(|&(_, ref v)| v) + } + + fn get_attr_ignore_ns(&self, name: &Atom) -> Option<&AttrValue> { + self.attrs.as_ref().unwrap().iter() + .find(|&&(ref ident, _)| ident.local_name == *name) + .map(|&(_, ref v)| v) + } +} + +impl ElementSnapshot for ServoElementSnapshot { + fn state(&self) -> Option<ElementState> { + self.state.clone() + } + + fn has_attrs(&self) -> bool { + self.attrs.is_some() + } + + fn id_attr(&self) -> Option<Atom> { + self.get_attr(&ns!(), &atom!("id")).map(|v| v.as_atom().clone()) + } + + fn has_class(&self, name: &Atom) -> bool { + self.get_attr(&ns!(), &atom!("class")) + .map_or(false, |v| v.as_tokens().iter().any(|atom| atom == name)) + } + + fn each_class<F>(&self, mut callback: F) + where F: FnMut(&Atom) + { + if let Some(v) = self.get_attr(&ns!(), &atom!("class")) { + for class in v.as_tokens() { + callback(class); + } + } + } +} + +impl MatchAttrGeneric for ServoElementSnapshot { + fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool + { + use selectors::parser::NamespaceConstraint; + let html = self.is_html_element_in_html_document; + let local_name = if html { &attr.lower_name } else { &attr.name }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => self.get_attr(ns, local_name), + NamespaceConstraint::Any => self.get_attr_ignore_ns(local_name), + }.map_or(false, |v| test(v)) + } +} + impl<E: Element<Impl=TheSelectorImpl, AttrString=String>> ElementExt for E { + type Snapshot = ServoElementSnapshot; + fn is_link(&self) -> bool { self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink) } diff --git a/ports/geckolib/gecko_bindings/tools/regen.py b/ports/geckolib/gecko_bindings/tools/regen.py index 4c3d4e5ac9c..8542a0ec8d1 100755 --- a/ports/geckolib/gecko_bindings/tools/regen.py +++ b/ports/geckolib/gecko_bindings/tools/regen.py @@ -45,6 +45,7 @@ COMPILATION_TARGETS = { "includes": [ "{}/dist/include/nsThemeConstants.h", "{}/dist/include/mozilla/dom/AnimationEffectReadOnlyBinding.h", + "{}/dist/include/mozilla/ServoElementSnapshot.h", ], "files": [ "{}/dist/include/nsStyleStruct.h", @@ -74,6 +75,8 @@ COMPILATION_TARGETS = { "nsDataHashtable.h", "nsCSSScanner.h", "nsTArray", "pair", "SheetParsingMode.h", "StaticPtr.h", "nsProxyRelease.h", "mozilla/dom/AnimationEffectReadOnlyBinding.h", + "nsChangeHint.h", "ServoElementSnapshot.h", + "EventStates.h", "nsAttrValue.h", "nsAttrName.h", "/Types.h", # <- Disallow UnionTypes.h "/utility", # <- Disallow xutility "nsINode.h", # <- For `NodeFlags`. @@ -122,7 +125,7 @@ COMPILATION_TARGETS = { "nsStyleCoord", "nsStyleGradientStop", "nsStyleImageLayers", "nsStyleImageLayers::Layer", "nsStyleImageLayers::LayerType", "nsStyleUnit", "nsStyleUnion", "nsStyleCoord::CalcValue", - "nsStyleCoord::Calc", + "nsStyleCoord::Calc", "nsRestyleHint", "ServoElementSnapshot", "SheetParsingMode", "nsMainThreadPtrHandle", "nsMainThreadPtrHolder", "nscolor", "nsFont", "FontFamilyList", diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 9d8e8ec5129..f38cd8374d4 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -13,7 +13,10 @@ use gecko_bindings::bindings::{RawServoStyleSet, RawServoStyleSheet, ServoComput use gecko_bindings::bindings::{ServoDeclarationBlock, ServoNodeData, ThreadSafePrincipalHolder}; use gecko_bindings::bindings::{ThreadSafeURIHolder, nsHTMLCSSStyleSheet}; use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI}; +use gecko_bindings::structs::ServoElementSnapshot; +use gecko_bindings::structs::nsRestyleHint; use gecko_bindings::structs::{SheetParsingMode, nsIAtom}; +use snapshot::GeckoElementSnapshot; use std::mem::transmute; use std::ptr; use std::slice; @@ -437,3 +440,22 @@ pub extern "C" fn Servo_CSSSupports(property: *const u8, property_length: u32, Err(()) => false, } } + +#[no_mangle] +pub extern "C" fn Servo_ComputeRestyleHint(element: *mut RawGeckoElement, + snapshot: *mut ServoElementSnapshot, + raw_data: *mut RawServoStyleSet) -> nsRestyleHint { + let per_doc_data = unsafe { &mut *(raw_data as *mut PerDocumentStyleData) }; + let snapshot = unsafe { GeckoElementSnapshot::from_raw(snapshot) }; + let element = unsafe { GeckoElement::from_raw(element) }; + + // NB: This involves an FFI call, we can get rid of it easily if needed. + let current_state = element.get_state(); + + let hint = per_doc_data.stylist + .compute_restyle_hint(&element, &snapshot, + current_state); + + // NB: Binary representations match. + unsafe { transmute(hint.bits() as u32) } +} diff --git a/ports/geckolib/lib.rs b/ports/geckolib/lib.rs index 7f9813597c4..81923a9188c 100644 --- a/ports/geckolib/lib.rs +++ b/ports/geckolib/lib.rs @@ -22,6 +22,8 @@ extern crate util; mod context; mod data; +mod snapshot; +mod snapshot_helpers; #[allow(non_snake_case)] pub mod glue; mod traversal; diff --git a/ports/geckolib/snapshot.rs b/ports/geckolib/snapshot.rs new file mode 100644 index 00000000000..60aba7037e9 --- /dev/null +++ b/ports/geckolib/snapshot.rs @@ -0,0 +1,149 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use gecko_bindings::bindings; +use gecko_bindings::structs::ServoElementSnapshot; +use gecko_bindings::structs::ServoElementSnapshotFlags as Flags; +use selectors::parser::AttrSelector; +use snapshot_helpers; +use string_cache::Atom; +use style::element_state::ElementState; +use style::restyle_hints::ElementSnapshot; +use wrapper::AttrSelectorHelpers; + +// NB: This is sound, in some sense, because during computation of restyle hints +// the snapshot is kept alive by the modified elements table. +#[derive(Debug)] +pub struct GeckoElementSnapshot(*mut ServoElementSnapshot); + +impl GeckoElementSnapshot { + #[inline] + pub unsafe fn from_raw(raw: *mut ServoElementSnapshot) -> Self { + GeckoElementSnapshot(raw) + } + + #[inline] + fn is_html_element_in_html_document(&self) -> bool { + unsafe { (*self.0).mIsHTMLElementInHTMLDocument } + } + + #[inline] + fn has_any(&self, flags: Flags) -> bool { + unsafe { ((*self.0).mContains as u8 & flags as u8) != 0 } + } +} + +impl ::selectors::MatchAttr for GeckoElementSnapshot { + type AttrString = Atom; + + fn match_attr_has(&self, attr: &AttrSelector) -> bool { + unsafe { + bindings::Gecko_SnapshotHasAttr(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document())) + } + } + + fn match_attr_equals(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrEquals(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr(), + /* ignoreCase = */ false) + } + } + + fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrEquals(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr(), + /* ignoreCase = */ true) + } + } + fn match_attr_includes(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrIncludes(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr()) + } + } + fn match_attr_dash(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrDashEquals(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr()) + } + } + fn match_attr_prefix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrHasPrefix(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr()) + } + } + fn match_attr_substring(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrHasSubstring(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr()) + } + } + fn match_attr_suffix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { + unsafe { + bindings::Gecko_SnapshotAttrHasSuffix(self.0, + attr.ns_or_null(), + attr.select_name(self.is_html_element_in_html_document()), + value.as_ptr()) + } + } +} + +impl ElementSnapshot for GeckoElementSnapshot { + fn state(&self) -> Option<ElementState> { + if self.has_any(Flags::State) { + Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState as u16 })) + } else { + None + } + } + + #[inline] + fn has_attrs(&self) -> bool { + self.has_any(Flags::Attributes) + } + + fn id_attr(&self) -> Option<Atom> { + let ptr = unsafe { + bindings::Gecko_SnapshotAtomAttrValue(self.0, + atom!("id").as_ptr()) + }; + + if ptr.is_null() { + None + } else { + Some(Atom::from(ptr)) + } + } + + // TODO: share logic with Element::{has_class, each_class}? + fn has_class(&self, name: &Atom) -> bool { + snapshot_helpers::has_class(self.0, + name, + bindings::Gecko_SnapshotClassOrClassList) + } + + fn each_class<F>(&self, callback: F) + where F: FnMut(&Atom) + { + snapshot_helpers::each_class(self.0, + callback, + bindings::Gecko_SnapshotClassOrClassList) + } +} diff --git a/ports/geckolib/snapshot_helpers.rs b/ports/geckolib/snapshot_helpers.rs new file mode 100644 index 00000000000..ee7fdd14421 --- /dev/null +++ b/ports/geckolib/snapshot_helpers.rs @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Element an snapshot common logic. + +use gecko_bindings::structs::nsIAtom; +use std::{ptr, slice}; +use string_cache::Atom; + +pub type ClassOrClassList<T> = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut *mut nsIAtom) -> u32; + +pub fn has_class<T>(item: T, + name: &Atom, + getter: ClassOrClassList<T>) -> bool +{ + unsafe { + let mut class: *mut nsIAtom = ptr::null_mut(); + let mut list: *mut *mut nsIAtom = ptr::null_mut(); + let length = getter(item, &mut class, &mut list); + match length { + 0 => false, + 1 => name.as_ptr() == class, + n => { + let classes = slice::from_raw_parts(list, n as usize); + classes.iter().any(|ptr| name.as_ptr() == *ptr) + } + } + } +} + + +pub fn each_class<F, T>(item: T, + mut callback: F, + getter: ClassOrClassList<T>) + where F: FnMut(&Atom) +{ + unsafe { + let mut class: *mut nsIAtom = ptr::null_mut(); + let mut list: *mut *mut nsIAtom = ptr::null_mut(); + let length = getter(item, &mut class, &mut list); + match length { + 0 => {} + 1 => Atom::with(class, &mut callback), + n => { + let classes = slice::from_raw_parts(list, n as usize); + for c in classes { + Atom::with(*c, &mut callback) + } + } + } + } +} diff --git a/ports/geckolib/wrapper.rs b/ports/geckolib/wrapper.rs index 73bfa8adcc3..d71666975a0 100644 --- a/ports/geckolib/wrapper.rs +++ b/ports/geckolib/wrapper.rs @@ -7,7 +7,6 @@ use gecko_bindings::bindings; use gecko_bindings::bindings::Gecko_ChildrenCount; use gecko_bindings::bindings::Gecko_ClassOrClassList; -use gecko_bindings::bindings::Gecko_GetElementId; use gecko_bindings::bindings::Gecko_GetNodeData; use gecko_bindings::bindings::ServoNodeData; use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement}; @@ -20,7 +19,6 @@ use gecko_bindings::bindings::{Gecko_GetPrevSibling, Gecko_GetPrevSiblingElement use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument}; use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement, Gecko_IsTextNode}; use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink}; -#[allow(unused_imports)] // Used in commented-out code. use gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement, Gecko_SetNodeData}; use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode}; use gecko_bindings::structs::nsIAtom; @@ -30,29 +28,25 @@ use libc::uintptr_t; use selectors::Element; use selectors::matching::DeclarationBlock; use selectors::parser::{AttrSelector, NamespaceConstraint}; +use snapshot::GeckoElementSnapshot; +use snapshot_helpers; use std::marker::PhantomData; use std::ops::BitOr; use std::ptr; -use std::slice; use std::sync::Arc; use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; use style::data::PrivateStyleData; use style::dom::{OpaqueNode, PresentationalHintsSynthetizer}; use style::dom::{TDocument, TElement, TNode, TRestyleDamage, UnsafeNode}; use style::element_state::ElementState; -#[allow(unused_imports)] // Used in commented-out code. use style::error_reporting::StdoutErrorReporter; use style::gecko_selector_impl::{GeckoSelectorImpl, NonTSPseudoClass}; -#[allow(unused_imports)] // Used in commented-out code. use style::parser::ParserContextExtraData; -#[allow(unused_imports)] // Used in commented-out code. use style::properties::{ComputedValues, parse_style_attribute}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; use style::refcell::{Ref, RefCell, RefMut}; -use style::restyle_hints::ElementSnapshot; use style::selector_impl::ElementExt; use style::sink::Push; -#[allow(unused_imports)] // Used in commented-out code. use url::Url; pub type NonOpaqueStyleData = *mut RefCell<PrivateStyleData>; @@ -324,7 +318,7 @@ impl<'ld> TDocument for GeckoDocument<'ld> { } } - fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, ElementSnapshot)> { + fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, GeckoElementSnapshot)> { unimplemented!() /* let elements = unsafe { self.document.drain_modified_elements() }; @@ -497,48 +491,30 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } fn get_id(&self) -> Option<Atom> { - unsafe { - let ptr = Gecko_GetElementId(self.element); - if ptr.is_null() { - None - } else { - Some(Atom::from(ptr)) - } + let ptr = unsafe { + bindings::Gecko_AtomAttrValue(self.element, + atom!("id").as_ptr()) + }; + + if ptr.is_null() { + None + } else { + Some(Atom::from(ptr)) } } fn has_class(&self, name: &Atom) -> bool { - unsafe { - let mut class: *mut nsIAtom = ptr::null_mut(); - let mut list: *mut *mut nsIAtom = ptr::null_mut(); - let length = Gecko_ClassOrClassList(self.element, &mut class, &mut list); - match length { - 0 => false, - 1 => name.as_ptr() == class, - n => { - let classes = slice::from_raw_parts(list, n as usize); - classes.iter().any(|ptr| name.as_ptr() == *ptr) - } - } - } + snapshot_helpers::has_class(self.element, + name, + Gecko_ClassOrClassList) } - fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) { - unsafe { - let mut class: *mut nsIAtom = ptr::null_mut(); - let mut list: *mut *mut nsIAtom = ptr::null_mut(); - let length = Gecko_ClassOrClassList(self.element, &mut class, &mut list); - match length { - 0 => {} - 1 => Atom::with(class, &mut callback), - n => { - let classes = slice::from_raw_parts(list, n as usize); - for c in classes { - Atom::with(*c, &mut callback) - } - } - } - } + fn each_class<F>(&self, callback: F) + where F: FnMut(&Atom) + { + snapshot_helpers::each_class(self.element, + callback, + Gecko_ClassOrClassList) } fn is_html_element_in_html_document(&self) -> bool { @@ -548,9 +524,9 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } } -trait AttrSelectorHelpers { +pub trait AttrSelectorHelpers { fn ns_or_null(&self) -> *mut nsIAtom; - fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom; + fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom; } impl AttrSelectorHelpers for AttrSelector { @@ -561,13 +537,12 @@ impl AttrSelectorHelpers for AttrSelector { } } - fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom { - if el.is_html_element_in_html_document() { + fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom { + if is_html_element_in_html_document { self.lower_name.as_ptr() } else { self.name.as_ptr() } - } } @@ -577,14 +552,14 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_HasAttr(self.element, attr.ns_or_null(), - attr.select_name(self)) + attr.select_name(self.is_html_element_in_html_document())) } } fn match_attr_equals(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool { unsafe { bindings::Gecko_AttrEquals(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr(), /* ignoreCase = */ false) } @@ -593,7 +568,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrEquals(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr(), /* ignoreCase = */ false) } @@ -602,7 +577,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrIncludes(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr()) } } @@ -610,7 +585,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrDashEquals(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr()) } } @@ -618,7 +593,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrHasPrefix(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr()) } } @@ -626,7 +601,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrHasSubstring(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr()) } } @@ -634,13 +609,16 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> { unsafe { bindings::Gecko_AttrHasSuffix(self.element, attr.ns_or_null(), - attr.select_name(self), + attr.select_name(self.is_html_element_in_html_document()), value.as_ptr()) } } } impl<'le> ElementExt for GeckoElement<'le> { + type Snapshot = GeckoElementSnapshot; + + #[inline] fn is_link(&self) -> bool { self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink) } |