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_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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue