diff --git a/components/style/element_state.rs b/components/style/element_state.rs index d6ecc3bf5a9..adc05293e9e 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -144,6 +144,7 @@ bitflags! { /// /// NB: Is important for this to remain in sync with Gecko's /// dom/base/nsIDocument.h. + #[derive(MallocSizeOf)] pub flags DocumentState: u64 { /// RTL locale: specific to the XUL localedir attribute const NS_DOCUMENT_STATE_RTL_LOCALE = 1 << 0, diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index cbc2806ef83..0fe124beafa 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -5,7 +5,7 @@ //! Gecko-specific bits for selector-parsing. use cssparser::{BasicParseError, BasicParseErrorKind, Parser, ToCss, Token, CowRcStr, SourceLocation}; -use element_state::ElementState; +use element_state::{self, DocumentState, ElementState}; use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::RawServoSelectorList; use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; @@ -195,6 +195,15 @@ impl NonTSPseudoClass { apply_non_ts_list!(pseudo_class_state) } + /// Get the document state flag associated with a pseudo-class, if any. + pub fn document_state_flag(&self) -> DocumentState { + match *self { + NonTSPseudoClass::MozLocaleDir(..) => element_state::NS_DOCUMENT_STATE_RTL_LOCALE, + NonTSPseudoClass::MozWindowInactive => element_state::NS_DOCUMENT_STATE_WINDOW_INACTIVE, + _ => DocumentState::empty(), + } + } + /// Returns true if the given pseudoclass should trigger style sharing cache /// revalidation. pub fn needs_cache_revalidation(&self) -> bool { @@ -433,13 +442,6 @@ impl SelectorImpl { fun(pseudo.clone()) } } - - - /// Returns the relevant state flag for a given non-tree-structural - /// pseudo-class. - pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { - pc.state_flag() - } } fn utf16_to_ascii_lowercase(unit: u16) -> u16 { diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index bffb436cf1c..300bf861e6e 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -10,7 +10,7 @@ use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt}; use attr::{AttrIdentifier, AttrValue}; use cssparser::{Parser as CssParser, ToCss, serialize_identifier, CowRcStr, SourceLocation}; use dom::{OpaqueNode, TElement, TNode}; -use element_state::ElementState; +use element_state::{DocumentState, ElementState}; use fnv::FnvHashMap; use invalidation::element::element_wrapper::ElementSnapshot; use properties::ComputedValues; @@ -353,6 +353,11 @@ impl NonTSPseudoClass { } } + /// Get the document state flag associated with a pseudo-class, if any. + pub fn document_state_flag(&self) -> DocumentState { + DocumentState::empty() + } + /// Returns true if the given pseudoclass should trigger style sharing cache revalidation. pub fn needs_cache_revalidation(&self) -> bool { self.state_flag().is_empty() @@ -561,12 +566,6 @@ impl SelectorImpl { fun(PseudoElement::from_eager_index(i)); } } - - /// Returns the pseudo-class state flag for selector matching. - #[inline] - pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { - pc.state_flag() - } } /// A map from elements to snapshots for the Servo style back-end. diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 2657e1dffdb..20ab38b81b7 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -8,7 +8,7 @@ use {Atom, LocalName, Namespace}; use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList}; use context::{CascadeInputs, QuirksMode}; use dom::TElement; -use element_state::ElementState; +use element_state::{DocumentState, ElementState}; use font_metrics::FontMetricsProvider; #[cfg(feature = "gecko")] use gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; @@ -625,12 +625,6 @@ impl Stylist { } } - /// Returns whether the given ElementState bit might be relied upon by a - /// selector of some rule in the stylist. - pub fn might_have_state_dependency(&self, state: ElementState) -> bool { - self.has_state_dependency(state) - } - /// Returns whether the given ElementState bit is relied upon by a selector /// of some rule in the stylist. pub fn has_state_dependency(&self, state: ElementState) -> bool { @@ -639,6 +633,14 @@ impl Stylist { .any(|(d, _)| d.state_dependencies.intersects(state)) } + /// Returns whether the given DocumentState bit is relied upon by a selector + /// of some rule in the stylist. + pub fn has_document_state_dependency(&self, state: DocumentState) -> bool { + self.cascade_data + .iter_origins() + .any(|(d, _)| d.document_state_dependencies.intersects(state)) + } + /// Computes the style for a given "precomputed" pseudo-element, taking the /// universal rules and applying them. /// @@ -1677,6 +1679,8 @@ struct StylistSelectorVisitor<'a> { style_attribute_dependency: &'a mut bool, /// All the states selectors in the page reference. state_dependencies: &'a mut ElementState, + /// All the document states selectors in the page reference. + document_state_dependencies: &'a mut DocumentState, } fn component_needs_revalidation( @@ -1764,6 +1768,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { match *s { Component::NonTSPseudoClass(ref p) => { self.state_dependencies.insert(p.state_flag()); + self.document_state_dependencies.insert(p.document_state_flag()); } Component::ID(ref id) if !self.passed_rightmost_selector => { // We want to stop storing mapped ids as soon as we've moved off @@ -1832,6 +1837,11 @@ struct CascadeData { /// when an irrelevant element state bit changes. state_dependencies: ElementState, + /// The document state bits that are relied on by selectors. This is used + /// to tell whether we need to restyle the entire document when a document + /// state bit changes. + document_state_dependencies: DocumentState, + /// The ids that appear in the rightmost complex selector of selectors (and /// hence in our selector maps). Used to determine when sharing styles is /// safe: we disallow style sharing for elements whose id matches this @@ -1873,6 +1883,7 @@ impl CascadeData { attribute_dependencies: NonCountingBloomFilter::new(), style_attribute_dependency: false, state_dependencies: ElementState::empty(), + document_state_dependencies: DocumentState::empty(), mapped_ids: NonCountingBloomFilter::new(), selectors_for_cache_revalidation: SelectorMap::new(), effective_media_query_results: EffectiveMediaQueryResults::new(), @@ -2037,6 +2048,7 @@ impl CascadeData { attribute_dependencies: &mut self.attribute_dependencies, style_attribute_dependency: &mut self.style_attribute_dependency, state_dependencies: &mut self.state_dependencies, + document_state_dependencies: &mut self.document_state_dependencies, mapped_ids: &mut self.mapped_ids, }; @@ -2225,6 +2237,7 @@ impl CascadeData { self.attribute_dependencies.clear(); self.style_attribute_dependency = false; self.state_dependencies = ElementState::empty(); + self.document_state_dependencies = DocumentState::empty(); self.mapped_ids.clear(); self.selectors_for_cache_revalidation.clear(); } @@ -2333,12 +2346,14 @@ pub fn needs_revalidation_for_testing(s: &Selector) -> bool { let mut mapped_ids = NonCountingBloomFilter::new(); let mut style_attribute_dependency = false; let mut state_dependencies = ElementState::empty(); + let mut document_state_dependencies = DocumentState::empty(); let mut visitor = StylistSelectorVisitor { needs_revalidation: false, passed_rightmost_selector: false, attribute_dependencies: &mut attribute_dependencies, style_attribute_dependency: &mut style_attribute_dependency, state_dependencies: &mut state_dependencies, + document_state_dependencies: &mut document_state_dependencies, mapped_ids: &mut mapped_ids, }; s.visit(&mut visitor); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 98b7cde6866..c8a5140fcfd 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -21,7 +21,7 @@ use style::context::ThreadLocalStyleContext; use style::data::{ElementStyles, self}; use style::dom::{ShowSubtreeData, TElement, TNode}; use style::driver; -use style::element_state::ElementState; +use style::element_state::{DocumentState, ElementState}; use style::error_reporting::{NullReporter, ParseErrorReporter}; use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product}; use style::gecko::data::{GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl}; @@ -4090,19 +4090,30 @@ pub extern "C" fn Servo_StyleSet_HasStateDependency( let state = ElementState::from_bits_truncate(state); let data = PerDocumentStyleData::from_ffi(raw_data).borrow(); - let mut has_dep = data.stylist.might_have_state_dependency(state); + let mut has_dep = data.stylist.has_state_dependency(state); if !has_dep { // TODO(emilio): Consider optimizing this storing attribute // dependencies from UA sheets separately, so we could optimize // the above lookup if cut_off_inheritance is true. element.each_xbl_stylist(|stylist| { - has_dep = has_dep || stylist.might_have_state_dependency(state); + has_dep = has_dep || stylist.has_state_dependency(state); }); } has_dep } +#[no_mangle] +pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency( + raw_data: RawServoStyleSetBorrowed, + state: u64, +) -> bool { + let state = DocumentState::from_bits_truncate(state); + let data = PerDocumentStyleData::from_ffi(raw_data).borrow(); + + data.stylist.has_document_state_dependency(state) +} + #[no_mangle] pub extern "C" fn Servo_GetCustomPropertyValue( computed_values: ServoStyleContextBorrowed,