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; - fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, ElementSnapshot)>; + fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, + ::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. +/// 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; -#[derive(Clone)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct ElementSnapshot { - pub state: Option, - pub attrs: Option>, + /// 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; + + /// 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(&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, - 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, - 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, - E: MatchAttrGeneric, - E::Impl: SelectorImplExt { - fn match_attr(&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) +impl<'a, E> MatchAttr for ElementWrapper<'a, E> + where E: ElementExt, +{ + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + match self.snapshot { + Some(snapshot) if snapshot.has_attrs() + => snapshot.match_attr_suffix(attr, value), + _ => self.element.match_attr_suffix(attr, value) } } } -#[cfg(feature = "gecko")] -impl<'a, E> MatchAttr for ElementWrapper<'a, E> - where E: Element, - 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"); - } - - 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_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_includes(&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 { - panic!("Not implemented for Gecko - this system will need to be redesigned"); - } - - 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_substring(&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 { - panic!("Not implemented for Gecko - this system will need to be redesigned"); - } -} - impl<'a, E> Element for ElementWrapper<'a, E> - where E: Element, - E: MatchAttrGeneric, - E::Impl: SelectorImplExt { + where E: ElementExt, + E::Impl: SelectorImplExt { 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.element.parent_element().map(ElementWrapper::new) } + fn first_child_element(&self) -> Option { self.element.first_child_element().map(ElementWrapper::new) } + fn last_child_element(&self) -> Option { self.element.last_child_element().map(ElementWrapper::new) } + fn prev_sibling_element(&self) -> Option { self.element.prev_sibling_element().map(ElementWrapper::new) } + fn next_sibling_element(&self) -> Option { 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 { - 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(&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(&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(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) - -> RestyleHint - where E: Element + 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(&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..923fdb7a75a 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 = ::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}; /// This function determines if a pseudo-element is eagerly cascaded or not. /// @@ -69,6 +73,8 @@ impl PseudoElementCascadeType { } pub trait ElementExt: Element::AttrString> { + type Snapshot: restyle_hints::ElementSnapshot + '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(&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 - + 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, + pub attrs: Option>, + 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 { + self.state.clone() + } + + fn has_attrs(&self) -> bool { + self.attrs.is_some() + } + + fn id_attr(&self) -> Option { + 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(&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(&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> 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/bindings.rs b/ports/geckolib/gecko_bindings/bindings.rs index 76e96a0ac06..2b861dc7069 100644 --- a/ports/geckolib/gecko_bindings/bindings.rs +++ b/ports/geckolib/gecko_bindings/bindings.rs @@ -133,6 +133,8 @@ unsafe impl Sync for nsStyleUnion {} impl HeapSizeOf for nsStyleUnion { fn heap_size_of_children(&self) -> usize { 0 } } use structs::nsStyleCoord_CalcValue as CalcValue; use structs::nsStyleCoord_Calc as Calc; +use structs::nsRestyleHint; +use structs::ServoElementSnapshot; use structs::SheetParsingMode; use structs::nsMainThreadPtrHandle; use structs::nsMainThreadPtrHolder; @@ -185,6 +187,8 @@ extern "C" { pub fn Gecko_LocalName(element: *mut RawGeckoElement) -> *mut nsIAtom; pub fn Gecko_Namespace(element: *mut RawGeckoElement) -> *mut nsIAtom; pub fn Gecko_GetElementId(element: *mut RawGeckoElement) -> *mut nsIAtom; + pub fn Gecko_AtomAttrValue(element: *mut RawGeckoElement, + attribute: *mut nsIAtom) -> *mut nsIAtom; pub fn Gecko_HasAttr(element: *mut RawGeckoElement, ns: *mut nsIAtom, name: *mut nsIAtom) -> bool; pub fn Gecko_AttrEquals(element: *mut RawGeckoElement, ns: *mut nsIAtom, @@ -207,6 +211,36 @@ extern "C" { pub fn Gecko_ClassOrClassList(element: *mut RawGeckoElement, class_: *mut *mut nsIAtom, classList: *mut *mut *mut nsIAtom) -> u32; + pub fn Gecko_SnapshotAtomAttrValue(element: *mut ServoElementSnapshot, + attribute: *mut nsIAtom) + -> *mut nsIAtom; + pub fn Gecko_SnapshotHasAttr(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom) + -> bool; + pub fn Gecko_SnapshotAttrEquals(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom, + str: *mut nsIAtom, ignoreCase: bool) + -> bool; + pub fn Gecko_SnapshotAttrDashEquals(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom, + str: *mut nsIAtom) -> bool; + pub fn Gecko_SnapshotAttrIncludes(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom, + str: *mut nsIAtom) -> bool; + pub fn Gecko_SnapshotAttrHasSubstring(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, + name: *mut nsIAtom, + str: *mut nsIAtom) -> bool; + pub fn Gecko_SnapshotAttrHasPrefix(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom, + str: *mut nsIAtom) -> bool; + pub fn Gecko_SnapshotAttrHasSuffix(element: *mut ServoElementSnapshot, + ns: *mut nsIAtom, name: *mut nsIAtom, + str: *mut nsIAtom) -> bool; + pub fn Gecko_SnapshotClassOrClassList(element: *mut ServoElementSnapshot, + class_: *mut *mut nsIAtom, + classList: *mut *mut *mut nsIAtom) + -> u32; pub fn Gecko_GetServoDeclarationBlock(element: *mut RawGeckoElement) -> *mut ServoDeclarationBlock; pub fn Gecko_GetNodeData(node: *mut RawGeckoNode) -> *mut ServoNodeData; @@ -331,6 +365,10 @@ extern "C" { set: *mut RawServoStyleSet); pub fn Servo_RestyleSubtree(node: *mut RawGeckoNode, set: *mut RawServoStyleSet); + pub fn Servo_ComputeRestyleHint(element: *mut RawGeckoElement, + snapshot: *mut ServoElementSnapshot, + set: *mut RawServoStyleSet) + -> nsRestyleHint; pub fn Servo_StyleWorkerThreadCount() -> u32; pub fn Gecko_Construct_nsStyleFont(ptr: *mut nsStyleFont); pub fn Gecko_CopyConstruct_nsStyleFont(ptr: *mut nsStyleFont, diff --git a/ports/geckolib/gecko_bindings/structs_debug.rs b/ports/geckolib/gecko_bindings/structs_debug.rs index e0763c1bb9c..91731e2e941 100644 --- a/ports/geckolib/gecko_bindings/structs_debug.rs +++ b/ports/geckolib/gecko_bindings/structs_debug.rs @@ -188,10 +188,20 @@ pub const NS_ERROR_MODULE_BASE_OFFSET: ::std::os::raw::c_uint = 69; pub const MOZ_STRING_WITH_OBSOLETE_API: ::std::os::raw::c_uint = 1; pub const NSID_LENGTH: ::std::os::raw::c_uint = 39; pub const NS_NUMBER_OF_FLAGS_IN_REFCNT: ::std::os::raw::c_uint = 2; +pub const _STL_PAIR_H: ::std::os::raw::c_uint = 1; +pub const _GLIBCXX_UTILITY: ::std::os::raw::c_uint = 1; +pub const __cpp_lib_tuple_element_t: ::std::os::raw::c_uint = 201402; +pub const __cpp_lib_tuples_by_type: ::std::os::raw::c_uint = 201304; +pub const __cpp_lib_exchange_function: ::std::os::raw::c_uint = 201304; +pub const __cpp_lib_integer_sequence: ::std::os::raw::c_uint = 201304; +pub const NS_EVENT_STATE_HIGHEST_SERVO_BIT: ::std::os::raw::c_uint = 6; pub const DOM_USER_DATA: ::std::os::raw::c_uint = 1; pub const SMIL_MAPPED_ATTR_ANIMVAL: ::std::os::raw::c_uint = 2; pub const TWIPS_PER_POINT_INT: ::std::os::raw::c_uint = 20; pub const POINTS_PER_INCH_INT: ::std::os::raw::c_uint = 72; +pub const NS_ATTRNAME_NODEINFO_BIT: ::std::os::raw::c_uint = 1; +pub const NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM: ::std::os::raw::c_uint = 12; +pub const NS_ATTRVALUE_INTEGERTYPE_BITS: ::std::os::raw::c_uint = 4; pub const NS_FONT_VARIANT_NORMAL: ::std::os::raw::c_uint = 0; pub const NS_FONT_VARIANT_SMALL_CAPS: ::std::os::raw::c_uint = 1; pub const NS_CORNER_TOP_LEFT_X: ::std::os::raw::c_uint = 0; @@ -2725,6 +2735,12 @@ impl ::std::clone::Clone for nsIExpandedPrincipal { fn clone(&self) -> Self { *self } } #[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Make_integer_sequence<_Tp, _ISeq> { + pub _phantom0: ::std::marker::PhantomData<_Tp>, + pub _phantom1: ::std::marker::PhantomData<_ISeq>, +} +#[repr(C)] #[derive(Debug, Copy)] pub struct nsIURI { pub _base: nsISupports, @@ -2766,6 +2782,26 @@ pub enum nsIRequest_nsIRequest_h_unnamed_7 { impl ::std::clone::Clone for nsIRequest { fn clone(&self) -> Self { *self } } +/** + * EventStates is the class used to represent the event states of nsIContent + * instances. These states are calculated by IntrinsicState() and + * ContentStatesChanged() has to be called when one of them changes thus + * informing the layout/style engine of the change. + * Event states are associated with pseudo-classes. + */ +#[repr(C)] +#[derive(Debug, Copy)] +pub struct EventStates { + pub mStates: ::std::os::raw::c_ulong, +} +impl ::std::clone::Clone for EventStates { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_EventStates() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} /** * Enum defining the mode in which a sheet is to be parsed. This is * usually, but not always, the same as the cascade level at which the @@ -2886,7 +2922,7 @@ fn bindgen_test_layout_nsMutationGuard() { extern "C" { #[link_name = "_ZN15nsMutationGuard11sGenerationE"] pub static mut nsMutationGuard_consts_sGeneration: - ::std::os::raw::c_ulonglong; + ::std::os::raw::c_ulong; } pub type Float = f32; #[repr(i8)] @@ -3262,6 +3298,259 @@ pub enum PlaybackDirection { EndGuard_ = 4, } pub type NativeType = AnimationEffectReadOnly; +#[repr(C)] +#[derive(Debug)] +pub struct nsAttrName { + pub mBits: usize, +} +#[test] +fn bindgen_test_layout_nsAttrName() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +pub type nscolor = u32; +#[repr(i8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsHexColorType { NoAlpha = 0, AllowAlpha = 1, } +pub enum nsStyledElementNotElementCSSInlineStyle { } +pub enum MiscContainer { } +pub enum ServoDeclarationBlock { } +pub enum Declaration { } +/** + * A class used to construct a nsString from a nsStringBuffer (we might + * want to move this to nsString at some point). + * + * WARNING: Note that nsCheapString doesn't take an explicit length -- it + * assumes the string is maximally large, given the nsStringBuffer's storage + * size. This means the given string buffer *must* be sized exactly correctly + * for the string it contains (including one byte for a null terminator). If + * it has any unused storage space, then that will result in bogus characters + * at the end of our nsCheapString. + */ +#[repr(C)] +#[derive(Debug)] +pub struct nsCheapString { + pub _base: nsString, +} +#[test] +fn bindgen_test_layout_nsCheapString() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +#[repr(C)] +#[derive(Debug)] +pub struct nsAttrValue { + pub mBits: usize, +} +pub const eSVGTypesBegin: nsAttrValue_ValueType = + nsAttrValue_ValueType::eSVGAngle; +pub const eSVGTypesEnd: nsAttrValue_ValueType = + nsAttrValue_ValueType::eSVGViewBox; +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsAttrValue_ValueType { + eString = 0, + eAtom = 2, + eInteger = 3, + eColor = 7, + eEnum = 11, + ePercent = 15, + eGeckoCSSDeclaration = 16, + eServoCSSDeclaration = 17, + eURL = 18, + eImage = 19, + eAtomArray = 20, + eDoubleValue = 21, + eIntMarginValue = 22, + eSVGAngle = 23, + eSVGIntegerPair = 24, + eSVGLength = 25, + eSVGLengthList = 26, + eSVGNumberList = 27, + eSVGNumberPair = 28, + eSVGPathData = 29, + eSVGPointList = 30, + eSVGPreserveAspectRatio = 31, + eSVGStringList = 32, + eSVGTransformList = 33, + eSVGViewBox = 34, +} +/** + * Structure for a mapping from int (enum) values to strings. When you use + * it you generally create an array of them. + * Instantiate like this: + * EnumTable myTable[] = { + * { "string1", 1 }, + * { "string2", 2 }, + * { 0 } + * } + */ +#[repr(C)] +#[derive(Debug, Copy)] +pub struct nsAttrValue_EnumTable { + /** The string the value maps to */ + pub tag: *const ::std::os::raw::c_char, + /** The enum value that maps to this string */ + pub value: i16, +} +impl ::std::clone::Clone for nsAttrValue_EnumTable { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_nsAttrValue_EnumTable() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsAttrValue_ValueBaseType { + eStringBase = 0, + eOtherBase = 1, + eAtomBase = 2, + eIntegerBase = 3, +} +#[test] +fn bindgen_test_layout_nsAttrValue() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +extern "C" { + #[link_name = "_ZN11nsAttrValue15sEnumTableArrayE"] + pub static mut nsAttrValue_consts_sEnumTableArray: + nsTArray<*const nsAttrValue_EnumTable>; +} +pub enum nsCSSSelector { } +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsChangeHint { + nsChangeHint_RepaintFrame = 1, + nsChangeHint_NeedReflow = 2, + nsChangeHint_ClearAncestorIntrinsics = 4, + nsChangeHint_ClearDescendantIntrinsics = 8, + nsChangeHint_NeedDirtyReflow = 16, + nsChangeHint_SyncFrameView = 32, + nsChangeHint_UpdateCursor = 64, + nsChangeHint_UpdateEffects = 128, + nsChangeHint_UpdateOpacityLayer = 256, + nsChangeHint_UpdateTransformLayer = 512, + nsChangeHint_ReconstructFrame = 1024, + nsChangeHint_UpdateOverflow = 2048, + nsChangeHint_UpdateSubtreeOverflow = 4096, + nsChangeHint_UpdatePostTransformOverflow = 8192, + nsChangeHint_UpdateParentOverflow = 16384, + nsChangeHint_ChildrenOnlyTransform = 32768, + nsChangeHint_RecomputePosition = 65536, + nsChangeHint_UpdateContainingBlock = 131072, + nsChangeHint_BorderStyleNoneChange = 262144, + nsChangeHint_UpdateTextPath = 524288, + nsChangeHint_SchedulePaint = 1048576, + nsChangeHint_NeutralChange = 2097152, + nsChangeHint_InvalidateRenderingObservers = 4194304, + nsChangeHint_ReflowChangesSizeOrPosition = 8388608, + nsChangeHint_UpdateComputedBSize = 16777216, + nsChangeHint_UpdateUsesOpacity = 33554432, + nsChangeHint_UpdateBackgroundPosition = 67108864, +} +pub type nsChangeHint_size_t = ::std::os::raw::c_int; +/** + * |nsRestyleHint| is a bitfield for the result of + * |HasStateDependentStyle| and |HasAttributeDependentStyle|. When no + * restyling is necessary, use |nsRestyleHint(0)|. + * + * Without eRestyle_Force or eRestyle_ForceDescendants, the restyling process + * can stop processing at a frame when it detects no style changes and it is + * known that the styles of the subtree beneath it will not change, leaving + * the old style context on the frame. eRestyle_Force can be used to skip this + * optimization on a frame, and to force its new style context to be used. + * + * Similarly, eRestyle_ForceDescendants will cause the frame and all of its + * descendants to be traversed and for the new style contexts that are created + * to be set on the frames. + * + * NOTE: When adding new restyle hints, please also add them to + * RestyleManager::RestyleHintToString. + */ +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsRestyleHint { + eRestyle_Self = 1, + eRestyle_SomeDescendants = 2, + eRestyle_Subtree = 4, + eRestyle_LaterSiblings = 8, + eRestyle_CSSTransitions = 16, + eRestyle_CSSAnimations = 32, + eRestyle_SVGAttrAnimations = 64, + eRestyle_StyleAttribute = 128, + eRestyle_StyleAttribute_Animations = 256, + eRestyle_Force = 512, + eRestyle_ForceDescendants = 1024, + eRestyle_AllHintsWithAnimations = 368, +} +pub type nsRestyleHint_size_t = ::std::os::raw::c_int; +/** + * Additional data used in conjunction with an nsRestyleHint to control the + * restyle process. + */ +#[repr(C)] +#[derive(Debug)] +pub struct RestyleHintData { + pub mSelectorsForDescendants: nsTArray<*mut nsCSSSelector>, +} +#[test] +fn bindgen_test_layout_RestyleHintData() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +/** + * A structure representing a single attribute name and value. + * + * This is pretty similar to the private nsAttrAndChildArray::InternalAttr. + */ +#[repr(C)] +#[derive(Debug)] +pub struct ServoAttrSnapshot { + pub mName: nsAttrName, + pub mValue: nsAttrValue, +} +#[test] +fn bindgen_test_layout_ServoAttrSnapshot() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +/** + * A bitflags enum class used to determine what data does a ServoElementSnapshot + * contains. + */ +#[repr(i8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ServoElementSnapshotFlags { + State = 1, + Attributes = 2, + HTMLElementInHTMLDocument = 4, + All = 7, +} +/** + * This class holds all non-tree-structural state of an element that might be + * used for selector matching eventually. + * + * This means the attributes, and the element state, such as :hover, :active, + * etc... + */ +#[repr(C)] +#[derive(Debug)] +pub struct ServoElementSnapshot { + pub mContains: ServoElementSnapshotFlags, + pub mAttrs: nsTArray, + pub mState: ::std::os::raw::c_uchar, + pub mExplicitRestyleHint: nsRestyleHint, + pub mExplicitChangeHint: nsChangeHint, + pub mIsHTMLElementInHTMLDocument: bool, +} +#[test] +fn bindgen_test_layout_ServoElementSnapshot() { + assert_eq!(::std::mem::size_of::() , 32usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} pub enum ErrorReporter { } #[repr(u32)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -4136,10 +4425,6 @@ pub enum nsCSSPropertyLogicalGroup { eCSSPropertyLogicalGroup_Size = 8, eCSSPropertyLogicalGroup_COUNT = 9, } -pub type nscolor = u32; -#[repr(i8)] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum nsHexColorType { NoAlpha = 0, AllowAlpha = 1, } /** * Class to safely handle main-thread-only pointers off the main thread. * diff --git a/ports/geckolib/gecko_bindings/structs_release.rs b/ports/geckolib/gecko_bindings/structs_release.rs index 16d904662fa..75b061eb9bb 100644 --- a/ports/geckolib/gecko_bindings/structs_release.rs +++ b/ports/geckolib/gecko_bindings/structs_release.rs @@ -188,10 +188,20 @@ pub const NS_ERROR_MODULE_BASE_OFFSET: ::std::os::raw::c_uint = 69; pub const MOZ_STRING_WITH_OBSOLETE_API: ::std::os::raw::c_uint = 1; pub const NSID_LENGTH: ::std::os::raw::c_uint = 39; pub const NS_NUMBER_OF_FLAGS_IN_REFCNT: ::std::os::raw::c_uint = 2; +pub const _STL_PAIR_H: ::std::os::raw::c_uint = 1; +pub const _GLIBCXX_UTILITY: ::std::os::raw::c_uint = 1; +pub const __cpp_lib_tuple_element_t: ::std::os::raw::c_uint = 201402; +pub const __cpp_lib_tuples_by_type: ::std::os::raw::c_uint = 201304; +pub const __cpp_lib_exchange_function: ::std::os::raw::c_uint = 201304; +pub const __cpp_lib_integer_sequence: ::std::os::raw::c_uint = 201304; +pub const NS_EVENT_STATE_HIGHEST_SERVO_BIT: ::std::os::raw::c_uint = 6; pub const DOM_USER_DATA: ::std::os::raw::c_uint = 1; pub const SMIL_MAPPED_ATTR_ANIMVAL: ::std::os::raw::c_uint = 2; pub const TWIPS_PER_POINT_INT: ::std::os::raw::c_uint = 20; pub const POINTS_PER_INCH_INT: ::std::os::raw::c_uint = 72; +pub const NS_ATTRNAME_NODEINFO_BIT: ::std::os::raw::c_uint = 1; +pub const NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM: ::std::os::raw::c_uint = 12; +pub const NS_ATTRVALUE_INTEGERTYPE_BITS: ::std::os::raw::c_uint = 4; pub const NS_FONT_VARIANT_NORMAL: ::std::os::raw::c_uint = 0; pub const NS_FONT_VARIANT_SMALL_CAPS: ::std::os::raw::c_uint = 1; pub const NS_CORNER_TOP_LEFT_X: ::std::os::raw::c_uint = 0; @@ -2704,6 +2714,12 @@ impl ::std::clone::Clone for nsIExpandedPrincipal { fn clone(&self) -> Self { *self } } #[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _Make_integer_sequence<_Tp, _ISeq> { + pub _phantom0: ::std::marker::PhantomData<_Tp>, + pub _phantom1: ::std::marker::PhantomData<_ISeq>, +} +#[repr(C)] #[derive(Debug, Copy)] pub struct nsIURI { pub _base: nsISupports, @@ -2745,6 +2761,26 @@ pub enum nsIRequest_nsIRequest_h_unnamed_7 { impl ::std::clone::Clone for nsIRequest { fn clone(&self) -> Self { *self } } +/** + * EventStates is the class used to represent the event states of nsIContent + * instances. These states are calculated by IntrinsicState() and + * ContentStatesChanged() has to be called when one of them changes thus + * informing the layout/style engine of the change. + * Event states are associated with pseudo-classes. + */ +#[repr(C)] +#[derive(Debug, Copy)] +pub struct EventStates { + pub mStates: ::std::os::raw::c_ulong, +} +impl ::std::clone::Clone for EventStates { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_EventStates() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} /** * Enum defining the mode in which a sheet is to be parsed. This is * usually, but not always, the same as the cascade level at which the @@ -2865,7 +2901,7 @@ fn bindgen_test_layout_nsMutationGuard() { extern "C" { #[link_name = "_ZN15nsMutationGuard11sGenerationE"] pub static mut nsMutationGuard_consts_sGeneration: - ::std::os::raw::c_ulonglong; + ::std::os::raw::c_ulong; } pub type Float = f32; #[repr(i8)] @@ -3241,6 +3277,259 @@ pub enum PlaybackDirection { EndGuard_ = 4, } pub type NativeType = AnimationEffectReadOnly; +#[repr(C)] +#[derive(Debug)] +pub struct nsAttrName { + pub mBits: usize, +} +#[test] +fn bindgen_test_layout_nsAttrName() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +pub type nscolor = u32; +#[repr(i8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsHexColorType { NoAlpha = 0, AllowAlpha = 1, } +pub enum nsStyledElementNotElementCSSInlineStyle { } +pub enum MiscContainer { } +pub enum ServoDeclarationBlock { } +pub enum Declaration { } +/** + * A class used to construct a nsString from a nsStringBuffer (we might + * want to move this to nsString at some point). + * + * WARNING: Note that nsCheapString doesn't take an explicit length -- it + * assumes the string is maximally large, given the nsStringBuffer's storage + * size. This means the given string buffer *must* be sized exactly correctly + * for the string it contains (including one byte for a null terminator). If + * it has any unused storage space, then that will result in bogus characters + * at the end of our nsCheapString. + */ +#[repr(C)] +#[derive(Debug)] +pub struct nsCheapString { + pub _base: nsString, +} +#[test] +fn bindgen_test_layout_nsCheapString() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +#[repr(C)] +#[derive(Debug)] +pub struct nsAttrValue { + pub mBits: usize, +} +pub const eSVGTypesBegin: nsAttrValue_ValueType = + nsAttrValue_ValueType::eSVGAngle; +pub const eSVGTypesEnd: nsAttrValue_ValueType = + nsAttrValue_ValueType::eSVGViewBox; +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsAttrValue_ValueType { + eString = 0, + eAtom = 2, + eInteger = 3, + eColor = 7, + eEnum = 11, + ePercent = 15, + eGeckoCSSDeclaration = 16, + eServoCSSDeclaration = 17, + eURL = 18, + eImage = 19, + eAtomArray = 20, + eDoubleValue = 21, + eIntMarginValue = 22, + eSVGAngle = 23, + eSVGIntegerPair = 24, + eSVGLength = 25, + eSVGLengthList = 26, + eSVGNumberList = 27, + eSVGNumberPair = 28, + eSVGPathData = 29, + eSVGPointList = 30, + eSVGPreserveAspectRatio = 31, + eSVGStringList = 32, + eSVGTransformList = 33, + eSVGViewBox = 34, +} +/** + * Structure for a mapping from int (enum) values to strings. When you use + * it you generally create an array of them. + * Instantiate like this: + * EnumTable myTable[] = { + * { "string1", 1 }, + * { "string2", 2 }, + * { 0 } + * } + */ +#[repr(C)] +#[derive(Debug, Copy)] +pub struct nsAttrValue_EnumTable { + /** The string the value maps to */ + pub tag: *const ::std::os::raw::c_char, + /** The enum value that maps to this string */ + pub value: i16, +} +impl ::std::clone::Clone for nsAttrValue_EnumTable { + fn clone(&self) -> Self { *self } +} +#[test] +fn bindgen_test_layout_nsAttrValue_EnumTable() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsAttrValue_ValueBaseType { + eStringBase = 0, + eOtherBase = 1, + eAtomBase = 2, + eIntegerBase = 3, +} +#[test] +fn bindgen_test_layout_nsAttrValue() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +extern "C" { + #[link_name = "_ZN11nsAttrValue15sEnumTableArrayE"] + pub static mut nsAttrValue_consts_sEnumTableArray: + nsTArray<*const nsAttrValue_EnumTable>; +} +pub enum nsCSSSelector { } +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsChangeHint { + nsChangeHint_RepaintFrame = 1, + nsChangeHint_NeedReflow = 2, + nsChangeHint_ClearAncestorIntrinsics = 4, + nsChangeHint_ClearDescendantIntrinsics = 8, + nsChangeHint_NeedDirtyReflow = 16, + nsChangeHint_SyncFrameView = 32, + nsChangeHint_UpdateCursor = 64, + nsChangeHint_UpdateEffects = 128, + nsChangeHint_UpdateOpacityLayer = 256, + nsChangeHint_UpdateTransformLayer = 512, + nsChangeHint_ReconstructFrame = 1024, + nsChangeHint_UpdateOverflow = 2048, + nsChangeHint_UpdateSubtreeOverflow = 4096, + nsChangeHint_UpdatePostTransformOverflow = 8192, + nsChangeHint_UpdateParentOverflow = 16384, + nsChangeHint_ChildrenOnlyTransform = 32768, + nsChangeHint_RecomputePosition = 65536, + nsChangeHint_UpdateContainingBlock = 131072, + nsChangeHint_BorderStyleNoneChange = 262144, + nsChangeHint_UpdateTextPath = 524288, + nsChangeHint_SchedulePaint = 1048576, + nsChangeHint_NeutralChange = 2097152, + nsChangeHint_InvalidateRenderingObservers = 4194304, + nsChangeHint_ReflowChangesSizeOrPosition = 8388608, + nsChangeHint_UpdateComputedBSize = 16777216, + nsChangeHint_UpdateUsesOpacity = 33554432, + nsChangeHint_UpdateBackgroundPosition = 67108864, +} +pub type nsChangeHint_size_t = ::std::os::raw::c_int; +/** + * |nsRestyleHint| is a bitfield for the result of + * |HasStateDependentStyle| and |HasAttributeDependentStyle|. When no + * restyling is necessary, use |nsRestyleHint(0)|. + * + * Without eRestyle_Force or eRestyle_ForceDescendants, the restyling process + * can stop processing at a frame when it detects no style changes and it is + * known that the styles of the subtree beneath it will not change, leaving + * the old style context on the frame. eRestyle_Force can be used to skip this + * optimization on a frame, and to force its new style context to be used. + * + * Similarly, eRestyle_ForceDescendants will cause the frame and all of its + * descendants to be traversed and for the new style contexts that are created + * to be set on the frames. + * + * NOTE: When adding new restyle hints, please also add them to + * RestyleManager::RestyleHintToString. + */ +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum nsRestyleHint { + eRestyle_Self = 1, + eRestyle_SomeDescendants = 2, + eRestyle_Subtree = 4, + eRestyle_LaterSiblings = 8, + eRestyle_CSSTransitions = 16, + eRestyle_CSSAnimations = 32, + eRestyle_SVGAttrAnimations = 64, + eRestyle_StyleAttribute = 128, + eRestyle_StyleAttribute_Animations = 256, + eRestyle_Force = 512, + eRestyle_ForceDescendants = 1024, + eRestyle_AllHintsWithAnimations = 368, +} +pub type nsRestyleHint_size_t = ::std::os::raw::c_int; +/** + * Additional data used in conjunction with an nsRestyleHint to control the + * restyle process. + */ +#[repr(C)] +#[derive(Debug)] +pub struct RestyleHintData { + pub mSelectorsForDescendants: nsTArray<*mut nsCSSSelector>, +} +#[test] +fn bindgen_test_layout_RestyleHintData() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +/** + * A structure representing a single attribute name and value. + * + * This is pretty similar to the private nsAttrAndChildArray::InternalAttr. + */ +#[repr(C)] +#[derive(Debug)] +pub struct ServoAttrSnapshot { + pub mName: nsAttrName, + pub mValue: nsAttrValue, +} +#[test] +fn bindgen_test_layout_ServoAttrSnapshot() { + assert_eq!(::std::mem::size_of::() , 16usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +/** + * A bitflags enum class used to determine what data does a ServoElementSnapshot + * contains. + */ +#[repr(i8)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ServoElementSnapshotFlags { + State = 1, + Attributes = 2, + HTMLElementInHTMLDocument = 4, + All = 7, +} +/** + * This class holds all non-tree-structural state of an element that might be + * used for selector matching eventually. + * + * This means the attributes, and the element state, such as :hover, :active, + * etc... + */ +#[repr(C)] +#[derive(Debug)] +pub struct ServoElementSnapshot { + pub mContains: ServoElementSnapshotFlags, + pub mAttrs: nsTArray, + pub mState: ::std::os::raw::c_uchar, + pub mExplicitRestyleHint: nsRestyleHint, + pub mExplicitChangeHint: nsChangeHint, + pub mIsHTMLElementInHTMLDocument: bool, +} +#[test] +fn bindgen_test_layout_ServoElementSnapshot() { + assert_eq!(::std::mem::size_of::() , 32usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} pub enum ErrorReporter { } #[repr(u32)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -4115,10 +4404,6 @@ pub enum nsCSSPropertyLogicalGroup { eCSSPropertyLogicalGroup_Size = 8, eCSSPropertyLogicalGroup_COUNT = 9, } -pub type nscolor = u32; -#[repr(i8)] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum nsHexColorType { NoAlpha = 0, AllowAlpha = 1, } /** * Class to safely handle main-thread-only pointers off the main thread. * diff --git a/ports/geckolib/gecko_bindings/tools/regen.py b/ports/geckolib/gecko_bindings/tools/regen.py index c7fd5819b88..74fd3beb8ff 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`. @@ -123,7 +126,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 f1da1fe14b5..95f316f5cc1 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; @@ -439,3 +442,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 { + 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 { + 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(&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 = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut *mut nsIAtom) -> u32; + +pub fn has_class(item: T, + name: &Atom, + getter: ClassOrClassList) -> 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(item: T, + mut callback: F, + getter: ClassOrClassList) + 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; @@ -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 { - 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(&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(&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) }