diff options
-rw-r--r-- | components/script/dom/document.rs | 1 | ||||
-rw-r--r-- | components/script/dom/element.rs | 12 | ||||
-rw-r--r-- | components/script/dom/node.rs | 11 | ||||
-rw-r--r-- | components/script/layout_wrapper.rs | 7 | ||||
-rw-r--r-- | components/selectors/matching.rs | 141 | ||||
-rw-r--r-- | components/selectors/parser.rs | 22 | ||||
-rw-r--r-- | components/selectors/size_of_tests.rs | 5 | ||||
-rw-r--r-- | components/selectors/tree.rs | 4 | ||||
-rw-r--r-- | components/style/context.rs | 16 | ||||
-rw-r--r-- | components/style/gecko/selector_parser.rs | 6 | ||||
-rw-r--r-- | components/style/gecko/wrapper.rs | 36 | ||||
-rw-r--r-- | components/style/matching.rs | 6 | ||||
-rw-r--r-- | components/style/restyle_hints.rs | 20 | ||||
-rw-r--r-- | components/style/servo/selector_parser.rs | 6 | ||||
-rw-r--r-- | components/style/stylist.rs | 9 |
15 files changed, 229 insertions, 73 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 1ce504308f7..8211830e9eb 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -209,6 +209,7 @@ pub struct Document { is_html_document: bool, activity: Cell<DocumentActivity>, url: DOMRefCell<ServoUrl>, + #[ignore_heap_size_of = "defined in selectors"] quirks_mode: Cell<QuirksMode>, /// Caches for the getElement methods id_map: DOMRefCell<HashMap<Atom, Vec<JS<Element>>>>, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index b4e7be3203a..3d7b0676fb8 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -86,7 +86,7 @@ use ref_filter_map::ref_filter_map; use script_layout_interface::message::ReflowQueryType; use script_thread::Runnable; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; +use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode}; use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::matching::{RelevantLinkStatus, matches_selector_list}; use servo_atoms::Atom; @@ -2063,7 +2063,9 @@ impl ElementMethods for Element { match SelectorParser::parse_author_origin_no_namespace(&selectors) { Err(_) => Err(Error::Syntax), Ok(selectors) => { - let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + let quirks_mode = document_from_node(self).quirks_mode(); + let mut ctx = MatchingContext::new(MatchingMode::Normal, None, + quirks_mode); Ok(matches_selector_list(&selectors, &Root::from_ref(self), &mut ctx)) } } @@ -2082,7 +2084,9 @@ impl ElementMethods for Element { let root = self.upcast::<Node>(); for element in root.inclusive_ancestors() { if let Some(element) = Root::downcast::<Element>(element) { - let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + let quirks_mode = document_from_node(self).quirks_mode(); + let mut ctx = MatchingContext::new(MatchingMode::Normal, None, + quirks_mode); if matches_selector_list(&selectors, &element, &mut ctx) { return Ok(Some(element)); } @@ -2432,7 +2436,7 @@ impl<'a> ::selectors::Element for Root<Element> { fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - _: &mut MatchingContext, + _: &mut LocalMatchingContext<Self::Impl>, _: &RelevantLinkStatus, _: &mut F) -> bool diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ec0a1117669..32cb8054ee7 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -347,11 +347,11 @@ impl<'a> Iterator for QuerySelectorIterator { fn next(&mut self) -> Option<Root<Node>> { let selectors = &self.selectors; - // TODO(cgaebel): Is it worth it to build a bloom filter here - // (instead of passing `None`)? Probably. - let mut ctx = MatchingContext::new(MatchingMode::Normal, None); - self.iterator.by_ref().filter_map(|node| { + // TODO(cgaebel): Is it worth it to build a bloom filter here + // (instead of passing `None`)? Probably. + let mut ctx = MatchingContext::new(MatchingMode::Normal, None, + node.owner_doc().quirks_mode()); if let Some(element) = Root::downcast(node) { if matches_selector_list(selectors, &element, &mut ctx) { return Some(Root::upcast(element)); @@ -720,7 +720,8 @@ impl Node { Err(_) => Err(Error::Syntax), // Step 3. Ok(selectors) => { - let mut ctx = MatchingContext::new(MatchingMode::Normal, None); + let mut ctx = MatchingContext::new(MatchingMode::Normal, None, + self.owner_doc().quirks_mode()); Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| { matches_selector_list(&selectors, element, &mut ctx) })) diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index bd4d74dcfec..15589619e17 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -50,7 +50,8 @@ use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData}; use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus, VisitedHandlingMode}; +use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus}; +use selectors::matching::VisitedHandlingMode; use servo_atoms::Atom; use servo_url::ServoUrl; use std::fmt; @@ -721,7 +722,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - _: &mut MatchingContext, + _: &mut LocalMatchingContext<Self::Impl>, _: &RelevantLinkStatus, _: &mut F) -> bool @@ -1232,7 +1233,7 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { fn match_non_ts_pseudo_class<F>(&self, _: &NonTSPseudoClass, - _: &mut MatchingContext, + _: &mut LocalMatchingContext<Self::Impl>, _: &RelevantLinkStatus, _: &mut F) -> bool diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 0ab6c7e29e0..9bb49cea13a 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -5,7 +5,7 @@ use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint}; use bloom::BloomFilter; use parser::{AncestorHashes, Combinator, Component, LocalName}; -use parser::{Selector, SelectorIter, SelectorList}; +use parser::{Selector, SelectorImpl, SelectorIter, SelectorList}; use std::borrow::Borrow; use tree::Element; @@ -100,6 +100,19 @@ pub enum VisitedHandlingMode { RelevantLinkVisited, } +/// Which quirks mode is this document in. +/// +/// See: https://quirks.spec.whatwg.org/ +#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] +pub enum QuirksMode { + /// Quirks mode. + Quirks, + /// Limited quirks mode. + LimitedQuirks, + /// No quirks mode. + NoQuirks, +} + /// Data associated with the matching process for a element. This context is /// used across many selectors for an element, so it's not appropriate for /// transient data that applies to only a single selector. @@ -119,12 +132,15 @@ pub struct MatchingContext<'a> { /// `RelevantLinkStatus` which tracks the status for the _current_ selector /// only.) pub relevant_link_found: bool, + /// The quirks mode of the document. + pub quirks_mode: QuirksMode, } impl<'a> MatchingContext<'a> { /// Constructs a new `MatchingContext`. pub fn new(matching_mode: MatchingMode, - bloom_filter: Option<&'a BloomFilter>) + bloom_filter: Option<&'a BloomFilter>, + quirks_mode: QuirksMode) -> Self { Self { @@ -133,13 +149,15 @@ impl<'a> MatchingContext<'a> { bloom_filter: bloom_filter, visited_handling: VisitedHandlingMode::AllLinksUnvisited, relevant_link_found: false, + quirks_mode: quirks_mode, } } /// Constructs a new `MatchingContext` for use in visited matching. pub fn new_for_visited(matching_mode: MatchingMode, bloom_filter: Option<&'a BloomFilter>, - visited_handling: VisitedHandlingMode) + visited_handling: VisitedHandlingMode, + quirks_mode: QuirksMode) -> Self { Self { @@ -148,8 +166,95 @@ impl<'a> MatchingContext<'a> { bloom_filter: bloom_filter, visited_handling: visited_handling, relevant_link_found: false, + quirks_mode: quirks_mode, + } + } +} + +/// Holds per-element data alongside a pointer to MatchingContext. +pub struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> { + /// Shared `MatchingContext`. + pub shared: &'a mut MatchingContext<'b>, + /// A reference to the base selector we're matching against. + pub selector: &'a Selector<Impl>, + /// The offset of the current compound selector being matched, kept up to date by + /// the callees when the iterator is advanced. This, in conjunction with the selector + /// reference above, allows callees to synthesize an iterator for the current compound + /// selector on-demand. This is necessary because the primary iterator may already have + /// been advanced partway through the current compound selector, and the callee may need + /// the whole thing. + offset: usize, + /// Holds a bool flag to see if LocalMatchingContext is within a functional + /// pseudo class argument. This is used for pseudo classes like + /// `:-moz-any` or `:not`. If this flag is true, :active and :hover + /// quirk shouldn't match. + pub within_functional_pseudo_class_argument: bool, +} + +impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl> + where Impl: SelectorImpl +{ + /// Constructs a new `LocalMatchingContext`. + pub fn new(shared: &'a mut MatchingContext<'b>, + selector: &'a Selector<Impl>) -> Self { + Self { + shared: shared, + selector: selector, + offset: 0, + within_functional_pseudo_class_argument: false, } } + + /// Updates offset of Selector to show new compound selector. + /// To be able to correctly re-synthesize main SelectorIter. + pub fn note_next_sequence(&mut self, selector_iter: &SelectorIter<Impl>) { + if let QuirksMode::Quirks = self.shared.quirks_mode { + self.offset = self.selector.len() - selector_iter.selector_length(); + } + } + + /// Returns true if current compound selector matches :active and :hover quirk. + /// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk + pub fn active_hover_quirk_matches(&mut self) -> bool { + if self.shared.quirks_mode != QuirksMode::Quirks || + self.within_functional_pseudo_class_argument { + return false; + } + + let mut iter = if self.offset == 0 { + self.selector.iter() + } else { + self.selector.iter_from(self.offset) + }; + + return iter.all(|simple| { + match *simple { + Component::LocalName(_) | + Component::AttributeInNoNamespaceExists { .. } | + Component::AttributeInNoNamespace { .. } | + Component::AttributeOther(_) | + Component::ID(_) | + Component::Class(_) | + Component::PseudoElement(_) | + Component::Negation(_) | + Component::FirstChild | + Component::LastChild | + Component::OnlyChild | + Component::Empty | + Component::NthChild(_, _) | + Component::NthLastChild(_, _) | + Component::NthOfType(_, _) | + Component::NthLastOfType(_, _) | + Component::FirstOfType | + Component::LastOfType | + Component::OnlyOfType => false, + Component::NonTSPseudoClass(ref pseudo_class) => { + Impl::is_active_or_hover(pseudo_class) + }, + _ => true, + } + }); + } } pub fn matches_selector_list<E>(selector_list: &SelectorList<E::Impl>, @@ -361,14 +466,15 @@ pub fn matches_selector<E, F>(selector: &Selector<E::Impl>, } } - matches_complex_selector(selector, offset, element, context, flags_setter) + let mut local_context = LocalMatchingContext::new(context, selector); + matches_complex_selector(&selector, offset, element, &mut local_context, flags_setter) } /// Matches a complex selector. pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>, offset: usize, element: &E, - context: &mut MatchingContext, + mut context: &mut LocalMatchingContext<E::Impl>, flags_setter: &mut F) -> bool where E: Element, @@ -381,14 +487,14 @@ pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>, }; if cfg!(debug_assertions) { - if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement { assert!(iter.clone().any(|c| { matches!(*c, Component::PseudoElement(..)) })); } } - if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement { match *iter.next().unwrap() { // Stateful pseudo, just don't match. Component::NonTSPseudoClass(..) => return false, @@ -401,6 +507,8 @@ pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>, if iter.next_sequence().is_none() { return true; } + // Inform the context that the we've advanced to the next compound selector. + context.note_next_sequence(&mut iter); } _ => panic!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"), } @@ -418,14 +526,14 @@ pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>, fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>, element: &E, - context: &mut MatchingContext, + context: &mut LocalMatchingContext<E::Impl>, relevant_link: &mut RelevantLinkStatus, flags_setter: &mut F) -> SelectorMatchingResult where E: Element, F: FnMut(&E, ElementSelectorFlags), { - *relevant_link = relevant_link.examine_potential_link(element, context); + *relevant_link = relevant_link.examine_potential_link(element, &mut context.shared); let matches_all_simple_selectors = selector_iter.all(|simple| { matches_simple_selector(simple, element, context, &relevant_link, flags_setter) @@ -435,6 +543,8 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im element, selector_iter, relevant_link); let combinator = selector_iter.next_sequence(); + // Inform the context that the we've advanced to the next compound selector. + context.note_next_sequence(&mut selector_iter); let siblings = combinator.map_or(false, |c| c.is_sibling()); if siblings { flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS); @@ -517,7 +627,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im fn matches_simple_selector<E, F>( selector: &Component<E::Impl>, element: &E, - context: &mut MatchingContext, + context: &mut LocalMatchingContext<E::Impl>, relevant_link: &RelevantLinkStatus, flags_setter: &mut F) -> bool @@ -527,7 +637,7 @@ fn matches_simple_selector<E, F>( match *selector { Component::Combinator(_) => unreachable!(), Component::PseudoElement(ref pseudo) => { - element.match_pseudo_element(pseudo, context) + element.match_pseudo_element(pseudo, context.shared) } Component::LocalName(LocalName { ref name, ref lower_name }) => { let is_html = element.is_html_element_in_html_document(); @@ -651,7 +761,14 @@ fn matches_simple_selector<E, F>( matches_generic_nth_child(element, 0, 1, true, true, flags_setter) } Component::Negation(ref negated) => { - !negated.iter().all(|ss| matches_simple_selector(ss, element, context, relevant_link, flags_setter)) + let old_value = context.within_functional_pseudo_class_argument; + context.within_functional_pseudo_class_argument = true; + let result = !negated.iter().all(|ss| { + matches_simple_selector(ss, element, context, + relevant_link, flags_setter) + }); + context.within_functional_pseudo_class_argument = old_value; + result } } } diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 39024502b4d..b485cae36de 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -103,6 +103,10 @@ macro_rules! with_all_bounds { /// pseudo-elements type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>; + + /// Returns whether the given pseudo class is :active or :hover. + #[inline] + fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool; } } } @@ -427,7 +431,7 @@ impl<Impl: SelectorImpl> Selector<Impl> { pub fn iter_from(&self, offset: usize) -> SelectorIter<Impl> { // Note: selectors are stored left-to-right but logical order is right-to-left. - let iter = self.0.slice[..(self.0.slice.len() - offset)].iter().rev(); + let iter = self.0.slice[..(self.len() - offset)].iter().rev(); SelectorIter { iter: iter, next_combinator: None, @@ -451,6 +455,11 @@ impl<Impl: SelectorImpl> Selector<Impl> { let header = HeaderWithLength::new(SpecificityAndFlags(specificity_and_flags), vec.len()); Selector(Arc::into_thin(Arc::from_header_and_iter(header, vec.into_iter()))) } + + /// Returns count of simple selectors and combinators in the Selector. + pub fn len(&self) -> usize { + self.0.slice.len() + } } #[derive(Clone)] @@ -465,6 +474,11 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> { pub fn next_sequence(&mut self) -> Option<Combinator> { self.next_combinator.take() } + + /// Returns remaining count of the simple selectors and combinators in the Selector. + pub fn selector_length(&self) -> usize { + self.iter.len() + } } impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> { @@ -1711,6 +1725,12 @@ pub mod tests { type BorrowedNamespaceUrl = DummyAtom; type NonTSPseudoClass = PseudoClass; type PseudoElement = PseudoElement; + + #[inline] + fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool { + matches!(*pseudo_class, PseudoClass::Active | + PseudoClass::Hover) + } } #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] diff --git a/components/selectors/size_of_tests.rs b/components/selectors/size_of_tests.rs index d9ed1651cf7..66ce80b8d64 100644 --- a/components/selectors/size_of_tests.rs +++ b/components/selectors/size_of_tests.rs @@ -34,6 +34,11 @@ impl SelectorImpl for Impl { type BorrowedNamespaceUrl = Atom; type NonTSPseudoClass = PseudoClass; type PseudoElement = gecko_like_types::PseudoElement; + + #[inline] + fn is_active_or_hover(_pseudo_class: &Self::NonTSPseudoClass) -> bool { + unimplemented!() + } } impl SelectorMethods for PseudoClass { diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index 93b59dbb4c5..7a47f2b4ab1 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -6,7 +6,7 @@ //! between layout and style. use attr::{AttrSelectorOperation, NamespaceConstraint}; -use matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus}; +use matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus}; use parser::SelectorImpl; use std::fmt::Debug; @@ -50,7 +50,7 @@ pub trait Element: Sized + Debug { fn match_non_ts_pseudo_class<F>(&self, pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass, - context: &mut MatchingContext, + context: &mut LocalMatchingContext<Self::Impl>, relevant_link: &RelevantLinkStatus, flags_setter: &mut F) -> bool where F: FnMut(&Self, ElementSelectorFlags); diff --git a/components/style/context.rs b/components/style/context.rs index 31736d3cf02..4526ba82aa8 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -33,6 +33,8 @@ use time; use timer::Timer; use traversal::{DomTraversal, TraversalFlags}; +pub use selectors::matching::QuirksMode; + /// This structure is used to create a local style context from a shared one. #[cfg(feature = "servo")] pub struct ThreadLocalStyleContextCreationInfo { @@ -49,20 +51,6 @@ impl ThreadLocalStyleContextCreationInfo { } } -/// Which quirks mode is this document in. -/// -/// See: https://quirks.spec.whatwg.org/ -#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum QuirksMode { - /// Quirks mode. - Quirks, - /// Limited quirks mode. - LimitedQuirks, - /// No quirks mode. - NoQuirks, -} - /// A global options structure for the style system. We use this instead of /// opts to abstract across Gecko and Servo. #[derive(Clone)] diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index c28fb36d632..9729672a1c7 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -237,6 +237,12 @@ impl ::selectors::SelectorImpl for SelectorImpl { type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; + + #[inline] + fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool { + matches!(*pseudo_class, NonTSPseudoClass::Active | + NonTSPseudoClass::Hover) + } } impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 2ac03135421..0ceb2c82a1b 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -72,7 +72,7 @@ use rule_tree::CascadeLevel as ServoCascadeLevel; use selector_parser::{AttrValue, ElementExt, PseudoClassStringArg}; use selectors::Element; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; +use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext}; use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode}; use shared_lock::Locked; use sink::Push; @@ -1424,7 +1424,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - context: &mut MatchingContext, + context: &mut LocalMatchingContext<Self::Impl>, relevant_link: &RelevantLinkStatus, flags_setter: &mut F) -> bool @@ -1432,10 +1432,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { { use selectors::matching::*; match *pseudo_class { - NonTSPseudoClass::AnyLink | - NonTSPseudoClass::Active | NonTSPseudoClass::Focus | - NonTSPseudoClass::Hover | NonTSPseudoClass::Enabled | NonTSPseudoClass::Disabled | NonTSPseudoClass::Checked | @@ -1477,12 +1474,19 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::MozMeterSubSubOptimum | NonTSPseudoClass::MozAutofill | NonTSPseudoClass::MozAutofillPreview => { - // NB: It's important to use `intersect` instead of `contains` - // here, to handle `:any-link` correctly. self.get_state().intersects(pseudo_class.state_flag()) }, - NonTSPseudoClass::Link => relevant_link.is_unvisited(self, context), - NonTSPseudoClass::Visited => relevant_link.is_visited(self, context), + NonTSPseudoClass::AnyLink => self.is_link(), + NonTSPseudoClass::Link => relevant_link.is_unvisited(self, context.shared), + NonTSPseudoClass::Visited => relevant_link.is_visited(self, context.shared), + NonTSPseudoClass::Active | + NonTSPseudoClass::Hover => { + if context.active_hover_quirk_matches() && !self.is_link() { + false + } else { + self.get_state().contains(pseudo_class.state_flag()) + } + }, NonTSPseudoClass::MozFirstNode => { flags_setter(self, HAS_EDGE_CHILD_SELECTOR); let mut elem = self.as_node(); @@ -1522,9 +1526,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } NonTSPseudoClass::MozPlaceholder => false, NonTSPseudoClass::MozAny(ref sels) => { - sels.iter().any(|s| { + let old_value = context.within_functional_pseudo_class_argument; + context.within_functional_pseudo_class_argument = true; + let result = sels.iter().any(|s| { matches_complex_selector(s, 0, self, context, flags_setter) - }) + }); + context.within_functional_pseudo_class_argument = old_value; + result } NonTSPseudoClass::Lang(ref lang_arg) => { self.match_element_lang(None, lang_arg) @@ -1563,11 +1571,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn is_link(&self) -> bool { - let mut context = MatchingContext::new(MatchingMode::Normal, None); - self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, - &mut context, - &RelevantLinkStatus::default(), - &mut |_, _| {}) + self.get_state().intersects(NonTSPseudoClass::AnyLink.state_flag()) } fn get_id(&self) -> Option<Atom> { diff --git a/components/style/matching.rs b/components/style/matching.rs index c801c295aa3..00eaba9b63e 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -1021,7 +1021,8 @@ pub trait MatchMethods : TElement { let mut matching_context = MatchingContext::new_for_visited(MatchingMode::Normal, Some(bloom_filter), - visited_handling); + visited_handling, + context.shared.quirks_mode); { let smil_override = data.get_smil_override(); @@ -1117,7 +1118,8 @@ pub trait MatchMethods : TElement { let mut matching_context = MatchingContext::new_for_visited(MatchingMode::ForStatelessPseudoElement, Some(bloom_filter), - visited_handling); + visited_handling, + context.shared.quirks_mode); // Compute rule nodes for eagerly-cascaded pseudo-elements. let mut matches_different_pseudos = false; diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index e89ef8dfef7..350608e118b 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -20,7 +20,7 @@ use selector_map::{SelectorMap, SelectorMapEntry}; use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue}; use selectors::Element; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; +use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode}; use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector}; use selectors::parser::{AncestorHashes, Combinator, Component}; use selectors::parser::{Selector, SelectorAndHashes, SelectorIter, SelectorMethods}; @@ -664,7 +664,7 @@ impl<'a, E> Element for ElementWrapper<'a, E> fn match_non_ts_pseudo_class<F>(&self, pseudo_class: &NonTSPseudoClass, - context: &mut MatchingContext, + context: &mut LocalMatchingContext<Self::Impl>, relevant_link: &RelevantLinkStatus, _setter: &mut F) -> bool @@ -707,10 +707,10 @@ impl<'a, E> Element for ElementWrapper<'a, E> // state directly. Instead, we use the `relevant_link` to determine if // they match. NonTSPseudoClass::Link => { - return relevant_link.is_unvisited(self, context); + return relevant_link.is_unvisited(self, context.shared); } NonTSPseudoClass::Visited => { - return relevant_link.is_visited(self, context); + return relevant_link.is_visited(self, context.shared); } #[cfg(feature = "gecko")] @@ -767,11 +767,7 @@ impl<'a, E> Element for ElementWrapper<'a, E> } fn is_link(&self) -> bool { - let mut context = MatchingContext::new(MatchingMode::Normal, None); - self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, - &mut context, - &RelevantLinkStatus::default(), - &mut |_, _| {}) + self.element.is_link() } fn parent_element(&self) -> Option<Self> { @@ -1202,7 +1198,8 @@ impl DependencySet { // not clear we _need_ it right now. let mut then_context = MatchingContext::new_for_visited(MatchingMode::Normal, None, - VisitedHandlingMode::AllLinksUnvisited); + VisitedHandlingMode::AllLinksUnvisited, + shared_context.quirks_mode); let matched_then = matches_selector(&dep.selector, dep.selector_offset, @@ -1212,7 +1209,8 @@ impl DependencySet { &mut |_, _| {}); let mut now_context = MatchingContext::new_for_visited(MatchingMode::Normal, bloom_filter, - VisitedHandlingMode::AllLinksUnvisited); + VisitedHandlingMode::AllLinksUnvisited, + shared_context.quirks_mode); let matches_now = matches_selector(&dep.selector, dep.selector_offset, diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 026c482f51f..289884c28f3 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -299,6 +299,12 @@ impl ::selectors::SelectorImpl for SelectorImpl { type NamespaceUrl = Namespace; type BorrowedLocalName = LocalName; type BorrowedNamespaceUrl = Namespace; + + #[inline] + fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool { + matches!(*pseudo_class, NonTSPseudoClass::Active | + NonTSPseudoClass::Hover) + } } impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 7f91855c4fc..497b51732d7 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -88,6 +88,7 @@ pub struct Stylist { effective_media_query_results: EffectiveMediaQueryResults, /// If true, the quirks-mode stylesheet is applied. + #[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")] quirks_mode: QuirksMode, /// If true, the device has changed, and the stylist needs to be updated. @@ -735,7 +736,9 @@ impl Stylist { // Bug 1364242: We need to add visited support for lazy pseudos let mut declarations = ApplicableDeclarationList::new(); let mut matching_context = - MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None); + MatchingContext::new(MatchingMode::ForStatelessPseudoElement, + None, + self.quirks_mode); self.push_applicable_declarations(element, Some(&pseudo), None, @@ -927,7 +930,7 @@ impl Stylist { V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>, { let mut matching_context = - MatchingContext::new(MatchingMode::Normal, None); + MatchingContext::new(MatchingMode::Normal, None, self.quirks_mode); let mut dummy_flag_setter = |_: &E, _: ElementSelectorFlags| {}; self.element_map.author.get_all_matching_rules(element, @@ -1156,7 +1159,7 @@ impl Stylist { // NB: `MatchingMode` doesn't really matter, given we don't share style // between pseudos. let mut matching_context = - MatchingContext::new(MatchingMode::Normal, bloom); + MatchingContext::new(MatchingMode::Normal, bloom, self.quirks_mode); // Note that, by the time we're revalidating, we're guaranteed that the // candidate and the entry have the same id, classes, and local name. |