Look for relevant links while matching

Adjust the matching process to look for a "relevant link" while matching.  A
"relevant link" is the element being matched if it is a link or the nearest
ancestor link.

Matching for links now depends on the `VisitedHandlingMode`, which determines
whether all links match as if they are unvisited (the default) or if the
relevant link matches as visited (and all others remain unvisited).

If a relevant link is ever found for any selector, track this as part of the
`MatchingContext` object.  This is used in the next patch to determine if an
additional match and cascade should be performed to compute the styles when
visited.

MozReview-Commit-ID: 3xUbRo7vpuD
This commit is contained in:
J. Ryan Stinnett 2017-05-15 10:14:49 -05:00
parent 8ae546f7ea
commit e3a256803d
8 changed files with 197 additions and 55 deletions

View file

@ -87,8 +87,9 @@ use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType; use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable; use script_thread::Runnable;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, matches_selector_list}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; 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; use servo_atoms::Atom;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::borrow::Cow; use std::borrow::Cow;
@ -2429,6 +2430,7 @@ impl<'a> ::selectors::Element for Root<Element> {
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
_: &mut MatchingContext, _: &mut MatchingContext,
_: &RelevantLinkStatus,
_: &mut F) _: &mut F)
-> bool -> bool
where F: FnMut(&Self, ElementSelectorFlags), where F: FnMut(&Self, ElementSelectorFlags),
@ -2478,6 +2480,20 @@ impl<'a> ::selectors::Element for Root<Element> {
} }
} }
fn is_link(&self) -> bool {
// FIXME: This is HTML only.
let node = self.upcast::<Node>();
match node.type_id() {
// https://html.spec.whatwg.org/multipage/#selector-link
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
self.has_attribute(&local_name!("href"))
},
_ => false,
}
}
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
self.id_attribute.borrow().clone() self.id_attribute.borrow().clone()
} }
@ -2592,20 +2608,6 @@ impl Element {
} }
} }
fn is_link(&self) -> bool {
// FIXME: This is HTML only.
let node = self.upcast::<Node>();
match node.type_id() {
// https://html.spec.whatwg.org/multipage/#selector-link
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
self.has_attribute(&local_name!("href"))
},
_ => false,
}
}
/// Please call this method *only* for real click events /// Please call this method *only* for real click events
/// ///
/// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps /// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps

View file

@ -51,7 +51,7 @@ use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutD
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
use servo_atoms::Atom; use servo_atoms::Atom;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::fmt; use std::fmt;
@ -680,6 +680,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
_: &mut MatchingContext, _: &mut MatchingContext,
_: &RelevantLinkStatus,
_: &mut F) _: &mut F)
-> bool -> bool
where F: FnMut(&Self, ElementSelectorFlags), where F: FnMut(&Self, ElementSelectorFlags),
@ -687,16 +688,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
match *pseudo_class { match *pseudo_class {
// https://github.com/servo/servo/issues/8718 // https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link | NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => unsafe { NonTSPseudoClass::AnyLink => self.is_link(),
match self.as_node().script_type_id() {
// https://html.spec.whatwg.org/multipage/#selector-link
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
(*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("href")).is_some(),
_ => false,
}
},
NonTSPseudoClass::Visited => false, NonTSPseudoClass::Visited => false,
// FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647 // FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647
@ -731,6 +723,20 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
} }
#[inline]
fn is_link(&self) -> bool {
unsafe {
match self.as_node().script_type_id() {
// https://html.spec.whatwg.org/multipage/#selector-link
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
(*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("href")).is_some(),
_ => false,
}
}
}
#[inline] #[inline]
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
unsafe { unsafe {
@ -1187,6 +1193,7 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
_: &NonTSPseudoClass, _: &NonTSPseudoClass,
_: &mut MatchingContext, _: &mut MatchingContext,
_: &RelevantLinkStatus,
_: &mut F) _: &mut F)
-> bool -> bool
where F: FnMut(&Self, ElementSelectorFlags), where F: FnMut(&Self, ElementSelectorFlags),
@ -1196,6 +1203,11 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn is_link(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_link called");
false
}
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::get_id called"); debug!("ServoThreadSafeLayoutElement::get_id called");
None None

View file

@ -94,6 +94,16 @@ pub enum MatchingMode {
ForStatelessPseudoElement, ForStatelessPseudoElement,
} }
/// The mode to use when matching unvisited and visited links.
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum VisitedHandlingMode {
/// All links are matched as if they are unvisted.
AllLinksUnvisited,
/// A element's "relevant link" is the element being matched if it is a link
/// or the nearest ancestor link. The relevant link is matched as though it
/// is visited, and all other links are matched as if they are unvisited.
RelevantLinkVisited,
}
/// Data associated with the matching process for a element. This context is /// 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 /// used across many selectors for an element, so it's not appropriate for
@ -106,6 +116,13 @@ pub struct MatchingContext<'a> {
pub matching_mode: MatchingMode, pub matching_mode: MatchingMode,
/// The bloom filter used to fast-reject selectors. /// The bloom filter used to fast-reject selectors.
pub bloom_filter: Option<&'a BloomFilter>, pub bloom_filter: Option<&'a BloomFilter>,
/// Input that controls how matching for links is handled.
pub visited_handling: VisitedHandlingMode,
/// Output that records whether we encountered a "relevant link" while
/// matching _any_ selector for this element. (This differs from
/// `RelevantLinkStatus` which tracks the status for the _current_ selector
/// only.)
relevant_link_found: bool,
} }
impl<'a> MatchingContext<'a> { impl<'a> MatchingContext<'a> {
@ -118,6 +135,8 @@ impl<'a> MatchingContext<'a> {
relations: StyleRelations::empty(), relations: StyleRelations::empty(),
matching_mode: matching_mode, matching_mode: matching_mode,
bloom_filter: bloom_filter, bloom_filter: bloom_filter,
visited_handling: VisitedHandlingMode::AllLinksUnvisited,
relevant_link_found: false,
} }
} }
} }
@ -156,6 +175,100 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>,
true true
} }
/// Tracks whether we are currently looking for relevant links for a given
/// complex selector. A "relevant link" is the element being matched if it is a
/// link or the nearest ancestor link.
///
/// `matches_complex_selector` creates a new instance of this for each complex
/// selector we try to match for an element. This is done because `is_visited`
/// and `is_unvisited` are based on relevant link state of only the current
/// complex selector being matched (not the global relevant link status for all
/// selectors in `MatchingContext`).
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum RelevantLinkStatus {
/// Looking for a possible relevant link. This is the initial mode when
/// matching a selector.
Looking,
/// Not looking for a relevant link. We transition to this mode if we
/// encounter a sibiling combinator (since only ancestor combinators are
/// allowed for this purpose).
NotLooking,
/// Found a relevant link for the element being matched.
Found,
}
impl Default for RelevantLinkStatus {
fn default() -> Self {
RelevantLinkStatus::NotLooking
}
}
impl RelevantLinkStatus {
/// If we found the relevant link for this element, record that in the
/// overall matching context for the element as a whole and stop looking for
/// addtional links.
fn examine_potential_link<E>(&self, element: &E, context: &mut MatchingContext)
-> RelevantLinkStatus
where E: Element,
{
if *self != RelevantLinkStatus::Looking {
return *self
}
if !element.is_link() {
return *self
}
// We found a relevant link. Record this in the `MatchingContext`,
// where we track whether one was found for _any_ selector (meaning
// this field might already be true from a previous selector).
context.relevant_link_found = true;
// Also return `Found` to update the relevant link status for _this_
// specific selector's matching process.
RelevantLinkStatus::Found
}
/// Returns whether an element is considered visited for the purposes of
/// matching. This is true only if the element is a link, an relevant link
/// exists for the element, and the visited handling mode is set to accept
/// relevant links as visited.
pub fn is_visited<E>(&self, element: &E, context: &MatchingContext) -> bool
where E: Element,
{
if !element.is_link() {
return false
}
// Non-relevant links are always unvisited.
if *self != RelevantLinkStatus::Found {
return false
}
context.visited_handling == VisitedHandlingMode::RelevantLinkVisited
}
/// Returns whether an element is considered unvisited for the purposes of
/// matching. Assuming the element is a link, this is always true for
/// non-relevant links, since only relevant links can potentially be treated
/// as visited. If this is a relevant link, then is it unvisited if the
/// visited handling mode is set to treat all links as unvisted (including
/// relevant links).
pub fn is_unvisited<E>(&self, element: &E, context: &MatchingContext) -> bool
where E: Element,
{
if !element.is_link() {
return false
}
// Non-relevant links are always unvisited.
if *self != RelevantLinkStatus::Found {
return true
}
context.visited_handling == VisitedHandlingMode::AllLinksUnvisited
}
}
/// A result of selector matching, includes 3 failure types, /// A result of selector matching, includes 3 failure types,
/// ///
/// NotMatchedAndRestartFromClosestLaterSibling /// NotMatchedAndRestartFromClosestLaterSibling
@ -267,6 +380,7 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
match matches_complex_selector_internal(iter, match matches_complex_selector_internal(iter,
element, element,
context, context,
RelevantLinkStatus::Looking,
flags_setter) { flags_setter) {
SelectorMatchingResult::Matched => true, SelectorMatchingResult::Matched => true,
_ => false _ => false
@ -276,13 +390,16 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>, fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
element: &E, element: &E,
context: &mut MatchingContext, context: &mut MatchingContext,
relevant_link: RelevantLinkStatus,
flags_setter: &mut F) flags_setter: &mut F)
-> SelectorMatchingResult -> SelectorMatchingResult
where E: Element, where E: Element,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
let mut relevant_link = relevant_link.examine_potential_link(element, context);
let matches_all_simple_selectors = selector_iter.all(|simple| { let matches_all_simple_selectors = selector_iter.all(|simple| {
matches_simple_selector(simple, element, context, flags_setter) matches_simple_selector(simple, element, context, &relevant_link, flags_setter)
}); });
let combinator = selector_iter.next_sequence(); let combinator = selector_iter.next_sequence();
@ -300,6 +417,9 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
Some(c) => { Some(c) => {
let (mut next_element, candidate_not_found) = match c { let (mut next_element, candidate_not_found) = match c {
Combinator::NextSibling | Combinator::LaterSibling => { Combinator::NextSibling | Combinator::LaterSibling => {
// Only ancestor combinators are allowed while looking for
// relevant links, so switch to not looking.
relevant_link = RelevantLinkStatus::NotLooking;
(element.prev_sibling_element(), (element.prev_sibling_element(),
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
} }
@ -321,6 +441,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
let result = matches_complex_selector_internal(selector_iter.clone(), let result = matches_complex_selector_internal(selector_iter.clone(),
&element, &element,
context, context,
relevant_link,
flags_setter); flags_setter);
match (result, c) { match (result, c) {
// Return the status immediately. // Return the status immediately.
@ -365,6 +486,7 @@ fn matches_simple_selector<E, F>(
selector: &Component<E::Impl>, selector: &Component<E::Impl>,
element: &E, element: &E,
context: &mut MatchingContext, context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F) flags_setter: &mut F)
-> bool -> bool
where E: Element, where E: Element,
@ -465,7 +587,7 @@ fn matches_simple_selector<E, F>(
) )
} }
Component::NonTSPseudoClass(ref pc) => { Component::NonTSPseudoClass(ref pc) => {
element.match_non_ts_pseudo_class(pc, context, flags_setter) element.match_non_ts_pseudo_class(pc, context, relevant_link, flags_setter)
} }
Component::FirstChild => { Component::FirstChild => {
matches_first_child(element, flags_setter) matches_first_child(element, flags_setter)
@ -509,7 +631,7 @@ fn matches_simple_selector<E, F>(
matches_generic_nth_child(element, 0, 1, true, true, flags_setter) matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
} }
Component::Negation(ref negated) => { Component::Negation(ref negated) => {
!negated.iter().all(|ss| matches_simple_selector(ss, element, context, flags_setter)) !negated.iter().all(|ss| matches_simple_selector(ss, element, context, relevant_link, flags_setter))
} }
} }
} }

View file

@ -2,11 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
//! style. //! between layout and style.
use attr::{AttrSelectorOperation, NamespaceConstraint}; use attr::{AttrSelectorOperation, NamespaceConstraint};
use matching::{ElementSelectorFlags, MatchingContext}; use matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
use parser::SelectorImpl; use parser::SelectorImpl;
pub trait Element: Sized { pub trait Element: Sized {
@ -50,6 +50,7 @@ pub trait Element: Sized {
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass, pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
context: &mut MatchingContext, context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F) -> bool flags_setter: &mut F) -> bool
where F: FnMut(&Self, ElementSelectorFlags); where F: FnMut(&Self, ElementSelectorFlags);
@ -58,6 +59,9 @@ pub trait Element: Sized {
context: &mut MatchingContext) context: &mut MatchingContext)
-> bool; -> bool;
/// Whether this element is a `link`.
fn is_link(&self) -> bool;
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>; fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool; fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;

View file

@ -65,7 +65,7 @@ use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::ElementExt; use selector_parser::ElementExt;
use selectors::Element; use selectors::Element;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, RelevantLinkStatus};
use shared_lock::Locked; use shared_lock::Locked;
use sink::Push; use sink::Push;
use std::cell::RefCell; use std::cell::RefCell;
@ -1236,6 +1236,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
context: &mut MatchingContext, context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
flags_setter: &mut F) flags_setter: &mut F)
-> bool -> bool
where F: FnMut(&Self, ElementSelectorFlags), where F: FnMut(&Self, ElementSelectorFlags),
@ -1243,8 +1244,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
use selectors::matching::*; use selectors::matching::*;
match *pseudo_class { match *pseudo_class {
NonTSPseudoClass::AnyLink | NonTSPseudoClass::AnyLink |
NonTSPseudoClass::Link |
NonTSPseudoClass::Visited |
NonTSPseudoClass::Active | NonTSPseudoClass::Active |
NonTSPseudoClass::Focus | NonTSPseudoClass::Focus |
NonTSPseudoClass::Hover | NonTSPseudoClass::Hover |
@ -1293,6 +1292,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
// here, to handle `:any-link` correctly. // here, to handle `:any-link` correctly.
self.get_state().intersects(pseudo_class.state_flag()) 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::MozFirstNode => { NonTSPseudoClass::MozFirstNode => {
flags_setter(self, HAS_EDGE_CHILD_SELECTOR); flags_setter(self, HAS_EDGE_CHILD_SELECTOR);
let mut elem = self.as_node(); let mut elem = self.as_node();
@ -1369,6 +1370,15 @@ 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 |_, _| {})
}
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
if !self.has_id() { if !self.has_id() {
return None; return None;
@ -1420,14 +1430,6 @@ impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
} }
impl<'le> ElementExt for GeckoElement<'le> { impl<'le> ElementExt 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,
&mut |_, _| {})
}
#[inline] #[inline]
fn matches_user_and_author_rules(&self) -> bool { fn matches_user_and_author_rules(&self) -> bool {
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0 self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0

View file

@ -20,7 +20,7 @@ use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue}; use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
use selectors::Element; use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, RelevantLinkStatus};
use selectors::matching::matches_selector; use selectors::matching::matches_selector;
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods}; use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
@ -535,6 +535,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
context: &mut MatchingContext, context: &mut MatchingContext,
relevant_link: &RelevantLinkStatus,
_setter: &mut F) _setter: &mut F)
-> bool -> bool
where F: FnMut(&Self, ElementSelectorFlags), where F: FnMut(&Self, ElementSelectorFlags),
@ -580,6 +581,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
if flag.is_empty() { if flag.is_empty() {
return self.element.match_non_ts_pseudo_class(pseudo_class, return self.element.match_non_ts_pseudo_class(pseudo_class,
context, context,
relevant_link,
&mut |_, _| {}) &mut |_, _| {})
} }
match self.snapshot().and_then(|s| s.state()) { match self.snapshot().and_then(|s| s.state()) {
@ -587,6 +589,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
None => { None => {
self.element.match_non_ts_pseudo_class(pseudo_class, self.element.match_non_ts_pseudo_class(pseudo_class,
context, context,
relevant_link,
&mut |_, _| {}) &mut |_, _| {})
} }
} }
@ -600,6 +603,14 @@ impl<'a, E> Element for ElementWrapper<'a, E>
self.element.match_pseudo_element(pseudo_element, context) self.element.match_pseudo_element(pseudo_element, context)
} }
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 |_, _| {})
}
fn parent_element(&self) -> Option<Self> { fn parent_element(&self) -> Option<Self> {
self.element.parent_element() self.element.parent_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map)) .map(|e| ElementWrapper::new(e, self.snapshot_map))

View file

@ -103,9 +103,6 @@ pub enum PseudoElementCascadeType {
/// An extension to rust-selector's `Element` trait. /// An extension to rust-selector's `Element` trait.
pub trait ElementExt: Element<Impl=SelectorImpl> + Debug { pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
/// Whether this element is a `link`.
fn is_link(&self) -> bool;
/// Whether this element should match user and author rules. /// Whether this element should match user and author rules.
/// ///
/// We use this for Native Anonymous Content in Gecko. /// We use this for Native Anonymous Content in Gecko.

View file

@ -16,7 +16,6 @@ use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::Element; use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{MatchingContext, MatchingMode};
use selectors::parser::SelectorMethods; use selectors::parser::SelectorMethods;
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use std::borrow::Cow; use std::borrow::Cow;
@ -601,13 +600,6 @@ impl ServoElementSnapshot {
} }
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E { impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut context,
&mut |_, _| {})
}
#[inline] #[inline]
fn matches_user_and_author_rules(&self) -> bool { fn matches_user_and_author_rules(&self) -> bool {
true true