mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
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:
parent
8ae546f7ea
commit
e3a256803d
8 changed files with 197 additions and 55 deletions
|
@ -87,8 +87,9 @@ 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, 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::{RelevantLinkStatus, matches_selector_list};
|
||||
use servo_atoms::Atom;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::borrow::Cow;
|
||||
|
@ -2429,6 +2430,7 @@ impl<'a> ::selectors::Element for Root<Element> {
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
_: &mut MatchingContext,
|
||||
_: &RelevantLinkStatus,
|
||||
_: &mut F)
|
||||
-> bool
|
||||
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> {
|
||||
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
|
||||
///
|
||||
/// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps
|
||||
|
|
|
@ -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::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
|
||||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
use std::fmt;
|
||||
|
@ -680,6 +680,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
_: &mut MatchingContext,
|
||||
_: &RelevantLinkStatus,
|
||||
_: &mut F)
|
||||
-> bool
|
||||
where F: FnMut(&Self, ElementSelectorFlags),
|
||||
|
@ -687,16 +688,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
match *pseudo_class {
|
||||
// https://github.com/servo/servo/issues/8718
|
||||
NonTSPseudoClass::Link |
|
||||
NonTSPseudoClass::AnyLink => 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,
|
||||
}
|
||||
},
|
||||
NonTSPseudoClass::AnyLink => self.is_link(),
|
||||
NonTSPseudoClass::Visited => false,
|
||||
|
||||
// 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]
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
unsafe {
|
||||
|
@ -1187,6 +1193,7 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
_: &NonTSPseudoClass,
|
||||
_: &mut MatchingContext,
|
||||
_: &RelevantLinkStatus,
|
||||
_: &mut F)
|
||||
-> bool
|
||||
where F: FnMut(&Self, ElementSelectorFlags),
|
||||
|
@ -1196,6 +1203,11 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn is_link(&self) -> bool {
|
||||
warn!("ServoThreadSafeLayoutElement::is_link called");
|
||||
false
|
||||
}
|
||||
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::get_id called");
|
||||
None
|
||||
|
|
|
@ -94,6 +94,16 @@ pub enum MatchingMode {
|
|||
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
|
||||
/// 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,
|
||||
/// The bloom filter used to fast-reject selectors.
|
||||
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> {
|
||||
|
@ -118,6 +135,8 @@ impl<'a> MatchingContext<'a> {
|
|||
relations: StyleRelations::empty(),
|
||||
matching_mode: matching_mode,
|
||||
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
|
||||
}
|
||||
|
||||
/// 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,
|
||||
///
|
||||
/// NotMatchedAndRestartFromClosestLaterSibling
|
||||
|
@ -267,6 +380,7 @@ pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl
|
|||
match matches_complex_selector_internal(iter,
|
||||
element,
|
||||
context,
|
||||
RelevantLinkStatus::Looking,
|
||||
flags_setter) {
|
||||
SelectorMatchingResult::Matched => true,
|
||||
_ => 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>,
|
||||
element: &E,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: RelevantLinkStatus,
|
||||
flags_setter: &mut F)
|
||||
-> SelectorMatchingResult
|
||||
where E: Element,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
let mut relevant_link = relevant_link.examine_potential_link(element, context);
|
||||
|
||||
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();
|
||||
|
@ -300,6 +417,9 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
|
|||
Some(c) => {
|
||||
let (mut next_element, candidate_not_found) = match c {
|
||||
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(),
|
||||
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(),
|
||||
&element,
|
||||
context,
|
||||
relevant_link,
|
||||
flags_setter);
|
||||
match (result, c) {
|
||||
// Return the status immediately.
|
||||
|
@ -365,6 +486,7 @@ fn matches_simple_selector<E, F>(
|
|||
selector: &Component<E::Impl>,
|
||||
element: &E,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
flags_setter: &mut F)
|
||||
-> bool
|
||||
where E: Element,
|
||||
|
@ -465,7 +587,7 @@ fn matches_simple_selector<E, F>(
|
|||
)
|
||||
}
|
||||
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 => {
|
||||
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)
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
* 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/. */
|
||||
|
||||
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
|
||||
//! style.
|
||||
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
|
||||
//! between layout and style.
|
||||
|
||||
use attr::{AttrSelectorOperation, NamespaceConstraint};
|
||||
use matching::{ElementSelectorFlags, MatchingContext};
|
||||
use matching::{ElementSelectorFlags, MatchingContext, RelevantLinkStatus};
|
||||
use parser::SelectorImpl;
|
||||
|
||||
pub trait Element: Sized {
|
||||
|
@ -50,6 +50,7 @@ pub trait Element: Sized {
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
flags_setter: &mut F) -> bool
|
||||
where F: FnMut(&Self, ElementSelectorFlags);
|
||||
|
||||
|
@ -58,6 +59,9 @@ pub trait Element: Sized {
|
|||
context: &mut MatchingContext)
|
||||
-> bool;
|
||||
|
||||
/// Whether this element is a `link`.
|
||||
fn is_link(&self) -> bool;
|
||||
|
||||
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
|
||||
|
||||
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
|
||||
|
|
|
@ -65,7 +65,7 @@ use rule_tree::CascadeLevel as ServoCascadeLevel;
|
|||
use selector_parser::ElementExt;
|
||||
use selectors::Element;
|
||||
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 sink::Push;
|
||||
use std::cell::RefCell;
|
||||
|
@ -1236,6 +1236,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
flags_setter: &mut F)
|
||||
-> bool
|
||||
where F: FnMut(&Self, ElementSelectorFlags),
|
||||
|
@ -1243,8 +1244,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
use selectors::matching::*;
|
||||
match *pseudo_class {
|
||||
NonTSPseudoClass::AnyLink |
|
||||
NonTSPseudoClass::Link |
|
||||
NonTSPseudoClass::Visited |
|
||||
NonTSPseudoClass::Active |
|
||||
NonTSPseudoClass::Focus |
|
||||
NonTSPseudoClass::Hover |
|
||||
|
@ -1293,6 +1292,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
// 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::MozFirstNode => {
|
||||
flags_setter(self, HAS_EDGE_CHILD_SELECTOR);
|
||||
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> {
|
||||
if !self.has_id() {
|
||||
return None;
|
||||
|
@ -1420,14 +1430,6 @@ impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
|
|||
}
|
||||
|
||||
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]
|
||||
fn matches_user_and_author_rules(&self) -> bool {
|
||||
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0
|
||||
|
|
|
@ -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, MatchingContext, MatchingMode, RelevantLinkStatus};
|
||||
use selectors::matching::matches_selector;
|
||||
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
|
@ -535,6 +535,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
context: &mut MatchingContext,
|
||||
relevant_link: &RelevantLinkStatus,
|
||||
_setter: &mut F)
|
||||
-> bool
|
||||
where F: FnMut(&Self, ElementSelectorFlags),
|
||||
|
@ -580,6 +581,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
if flag.is_empty() {
|
||||
return self.element.match_non_ts_pseudo_class(pseudo_class,
|
||||
context,
|
||||
relevant_link,
|
||||
&mut |_, _| {})
|
||||
}
|
||||
match self.snapshot().and_then(|s| s.state()) {
|
||||
|
@ -587,6 +589,7 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
None => {
|
||||
self.element.match_non_ts_pseudo_class(pseudo_class,
|
||||
context,
|
||||
relevant_link,
|
||||
&mut |_, _| {})
|
||||
}
|
||||
}
|
||||
|
@ -600,6 +603,14 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
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> {
|
||||
self.element.parent_element()
|
||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
||||
|
|
|
@ -103,9 +103,6 @@ pub enum PseudoElementCascadeType {
|
|||
|
||||
/// An extension to rust-selector's `Element` trait.
|
||||
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.
|
||||
///
|
||||
/// We use this for Native Anonymous Content in Gecko.
|
||||
|
|
|
@ -16,7 +16,6 @@ use restyle_hints::ElementSnapshot;
|
|||
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
|
||||
use selectors::Element;
|
||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
||||
use selectors::matching::{MatchingContext, MatchingMode};
|
||||
use selectors::parser::SelectorMethods;
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::borrow::Cow;
|
||||
|
@ -601,13 +600,6 @@ impl ServoElementSnapshot {
|
|||
}
|
||||
|
||||
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]
|
||||
fn matches_user_and_author_rules(&self) -> bool {
|
||||
true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue