aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/document.rs1
-rw-r--r--components/script/dom/element.rs12
-rw-r--r--components/script/dom/node.rs11
-rw-r--r--components/script/layout_wrapper.rs7
-rw-r--r--components/selectors/matching.rs141
-rw-r--r--components/selectors/parser.rs22
-rw-r--r--components/selectors/size_of_tests.rs5
-rw-r--r--components/selectors/tree.rs4
-rw-r--r--components/style/context.rs16
-rw-r--r--components/style/gecko/selector_parser.rs6
-rw-r--r--components/style/gecko/wrapper.rs36
-rw-r--r--components/style/matching.rs6
-rw-r--r--components/style/restyle_hints.rs20
-rw-r--r--components/style/servo/selector_parser.rs6
-rw-r--r--components/style/stylist.rs9
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.