aboutsummaryrefslogtreecommitdiffstats
path: root/components/style
diff options
context:
space:
mode:
Diffstat (limited to 'components/style')
-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
6 files changed, 275 insertions, 167 deletions
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)
}