aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/trace.rs3
-rw-r--r--components/script/dom/document.rs12
-rw-r--r--components/script/layout_wrapper.rs3
-rw-r--r--components/style/attr.rs2
-rw-r--r--components/style/dom.rs5
-rw-r--r--components/style/restyle_hints.rs327
-rw-r--r--components/style/selector_impl.rs12
-rw-r--r--components/style/selector_matching.rs12
-rw-r--r--components/style/servo_selector_impl.rs84
-rwxr-xr-xports/geckolib/gecko_bindings/tools/regen.py5
-rw-r--r--ports/geckolib/glue.rs22
-rw-r--r--ports/geckolib/lib.rs2
-rw-r--r--ports/geckolib/snapshot.rs149
-rw-r--r--ports/geckolib/snapshot_helpers.rs53
-rw-r--r--ports/geckolib/wrapper.rs94
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)
}