stylo: Support :hover and :active quirk

This commit is contained in:
Nazım Can Altınova 2017-06-10 20:43:05 +03:00
parent ff17af064b
commit 15fe48f3f6
No known key found for this signature in database
GPG key ID: AF9BCD7CE6449954
15 changed files with 229 additions and 73 deletions

View file

@ -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)]

View file

@ -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> {

View file

@ -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> {

View file

@ -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;

View file

@ -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,

View file

@ -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> {

View file

@ -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.