Auto merge of #26218 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

See individual commits for details.

This fixes some rebase mistakes from #26201 and such.
This commit is contained in:
bors-servo 2020-04-18 01:31:29 -04:00 committed by GitHub
commit 7799dccb25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 336 additions and 442 deletions

View file

@ -748,6 +748,7 @@ where
Component::Slotted(ref selector) | Component::Host(Some(ref selector)) => {
selector.size_of(ops)
},
Component::Is(ref list) | Component::Where(ref list) => list.size_of(ops),
Component::PseudoElement(ref pseudo) => (*pseudo).size_of(ops),
Component::Combinator(..) |
Component::ExplicitAnyNamespace |

View file

@ -330,6 +330,19 @@ where
specificity.class_like_selectors += 1;
}
},
Component::Is(ref list) => {
// https://drafts.csswg.org/selectors/#specificity-rules:
//
// The specificity of an :is() pseudo-class is replaced by the
// specificity of the most specific complex selector in its
// selector list argument.
let mut max = 0;
for selector in &**list {
max = std::cmp::max(selector.specificity(), max);
}
*specificity += Specificity::from(max);
},
Component::Where(..) |
Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |

View file

@ -851,6 +851,14 @@ where
matches_generic_nth_child(element, context, 0, 1, true, false, flags_setter) &&
matches_generic_nth_child(element, context, 0, 1, true, true, flags_setter)
},
Component::Is(ref list) | Component::Where(ref list) => context.shared.nest(|context| {
for selector in &**list {
if matches_complex_selector(selector.iter(), element, context, flags_setter) {
return true;
}
}
false
}),
Component::Negation(ref negated) => context.shared.nest_for_negation(|context| {
let mut local_context = LocalMatchingContext {
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk::No,

View file

@ -9,7 +9,7 @@ use crate::bloom::BLOOM_HASH_MASK;
use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
use crate::context::QuirksMode;
use crate::sink::Push;
pub use crate::visitor::{SelectorVisitor, Visit};
pub use crate::visitor::SelectorVisitor;
use cssparser::{parse_nth, serialize_identifier};
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
use cssparser::{CowRcStr, Delimiter, SourceLocation};
@ -55,6 +55,13 @@ pub trait NonTSPseudoClass: Sized + ToCss {
/// Whether this pseudo-class has zero specificity.
fn has_zero_specificity(&self) -> bool;
fn visit<V>(&self, _visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
true
}
}
/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a
@ -147,6 +154,7 @@ pub enum SelectorParseErrorKind<'i> {
NonCompoundSelector,
NonPseudoElementAfterSlotted,
InvalidPseudoElementAfterSlotted,
InvalidPseudoElementInsideWhere,
InvalidState,
UnexpectedTokenInAttributeSelector(Token<'i>),
PseudoElementExpectedColon(Token<'i>),
@ -226,6 +234,11 @@ pub trait Parser<'i> {
false
}
/// Whether to parse the `:where` pseudo-class.
fn parse_is_and_where(&self) -> bool {
false
}
/// Whether to parse the `:host` pseudo-class.
fn parse_host(&self) -> bool {
false
@ -441,128 +454,6 @@ impl AncestorHashes {
}
}
impl<Impl: SelectorImpl> Visit for Selector<Impl>
where
Impl::NonTSPseudoClass: Visit<Impl = Impl>,
{
type Impl = Impl;
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Impl>,
{
let mut current = self.iter();
let mut combinator = None;
loop {
if !visitor.visit_complex_selector(combinator) {
return false;
}
for selector in &mut current {
if !selector.visit(visitor) {
return false;
}
}
combinator = current.next_sequence();
if combinator.is_none() {
break;
}
}
true
}
}
impl<Impl: SelectorImpl> Visit for Component<Impl>
where
Impl::NonTSPseudoClass: Visit<Impl = Impl>,
{
type Impl = Impl;
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Impl>,
{
use self::Component::*;
if !visitor.visit_simple_selector(self) {
return false;
}
match *self {
Slotted(ref selector) => {
if !selector.visit(visitor) {
return false;
}
},
Host(Some(ref selector)) => {
if !selector.visit(visitor) {
return false;
}
},
Negation(ref negated) => {
for component in negated.iter() {
if !component.visit(visitor) {
return false;
}
}
},
AttributeInNoNamespaceExists {
ref local_name,
ref local_name_lower,
} => {
if !visitor.visit_attribute_selector(
&NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
local_name,
local_name_lower,
) {
return false;
}
},
AttributeInNoNamespace {
ref local_name,
never_matches,
..
} if !never_matches => {
if !visitor.visit_attribute_selector(
&NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
local_name,
local_name,
) {
return false;
}
},
AttributeOther(ref attr_selector) if !attr_selector.never_matches => {
let empty_string;
let namespace = match attr_selector.namespace() {
Some(ns) => ns,
None => {
empty_string = crate::parser::namespace_empty_string::<Impl>();
NamespaceConstraint::Specific(&empty_string)
},
};
if !visitor.visit_attribute_selector(
&namespace,
&attr_selector.local_name,
&attr_selector.local_name_lower,
) {
return false;
}
},
NonTSPseudoClass(ref pseudo_class) => {
if !pseudo_class.visit(visitor) {
return false;
}
},
_ => {},
}
true
}
}
pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
// Rust types default, not default namespace
Impl::NamespaceUrl::default()
@ -781,6 +672,50 @@ impl<Impl: SelectorImpl> Selector<Impl> {
pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void {
self.0.heap_ptr()
}
/// Traverse selector components inside `self`.
///
/// Implementations of this method should call `SelectorVisitor` methods
/// or other impls of `Visit` as appropriate based on the fields of `Self`.
///
/// A return value of `false` indicates terminating the traversal.
/// It should be propagated with an early return.
/// On the contrary, `true` indicates that all fields of `self` have been traversed:
///
/// ```rust,ignore
/// if !visitor.visit_simple_selector(&self.some_simple_selector) {
/// return false;
/// }
/// if !self.some_component.visit(visitor) {
/// return false;
/// }
/// true
/// ```
pub fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Impl>,
{
let mut current = self.iter();
let mut combinator = None;
loop {
if !visitor.visit_complex_selector(combinator) {
return false;
}
for selector in &mut current {
if !selector.visit(visitor) {
return false;
}
}
combinator = current.next_sequence();
if combinator.is_none() {
break;
}
}
true
}
}
#[derive(Clone)]
@ -1027,6 +962,20 @@ pub enum Component<Impl: SelectorImpl> {
///
/// See https://github.com/w3c/csswg-drafts/issues/2158
Host(Option<Selector<Impl>>),
/// The `:where` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#zero-matches
///
/// The inner argument is conceptually a SelectorList, but we move the
/// selectors to the heap to keep Component small.
Where(Box<[Selector<Impl>]>),
/// The `:is` pseudo-class.
///
/// https://drafts.csswg.org/selectors/#matches-pseudo
///
/// Same comment as above re. the argument.
Is(Box<[Selector<Impl>]>),
/// An implementation-dependent pseudo-element selector.
PseudoElement(#[shmem(field_bound)] Impl::PseudoElement),
}
@ -1080,6 +1029,94 @@ impl<Impl: SelectorImpl> Component<Impl> {
_ => None,
}
}
pub fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Impl>,
{
use self::Component::*;
if !visitor.visit_simple_selector(self) {
return false;
}
match *self {
Slotted(ref selector) => {
if !selector.visit(visitor) {
return false;
}
},
Host(Some(ref selector)) => {
if !selector.visit(visitor) {
return false;
}
},
Negation(ref negated) => {
for component in negated.iter() {
if !component.visit(visitor) {
return false;
}
}
},
AttributeInNoNamespaceExists {
ref local_name,
ref local_name_lower,
} => {
if !visitor.visit_attribute_selector(
&NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
local_name,
local_name_lower,
) {
return false;
}
},
AttributeInNoNamespace {
ref local_name,
never_matches,
..
} if !never_matches => {
if !visitor.visit_attribute_selector(
&NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
local_name,
local_name,
) {
return false;
}
},
AttributeOther(ref attr_selector) if !attr_selector.never_matches => {
let empty_string;
let namespace = match attr_selector.namespace() {
Some(ns) => ns,
None => {
empty_string = crate::parser::namespace_empty_string::<Impl>();
NamespaceConstraint::Specific(&empty_string)
},
};
if !visitor.visit_attribute_selector(
&namespace,
&attr_selector.local_name,
&attr_selector.local_name_lower,
) {
return false;
}
},
NonTSPseudoClass(ref pseudo_class) => {
if !pseudo_class.visit(visitor) {
return false;
}
},
Is(ref list) | Where(ref list) => {
if !visitor.visit_selector_list(&list) {
return false;
}
},
_ => {},
}
true
}
}
#[derive(Clone, Eq, PartialEq, ToShmem)]
@ -1114,21 +1151,29 @@ impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
}
}
fn serialize_selector_list<'a, Impl, I, W>(mut iter: I, dest: &mut W) -> fmt::Result
where
Impl: SelectorImpl,
I: Iterator<Item = &'a Selector<Impl>>,
W: fmt::Write,
{
let first = iter
.next()
.expect("Empty SelectorList, should contain at least one selector");
first.to_css(dest)?;
for selector in iter {
dest.write_str(", ")?;
selector.to_css(dest)?;
}
Ok(())
}
impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
let mut iter = self.0.iter();
let first = iter
.next()
.expect("Empty SelectorList, should contain at least one selector");
first.to_css(dest)?;
for selector in iter {
dest.write_str(", ")?;
selector.to_css(dest)?;
}
Ok(())
serialize_selector_list(self.0.iter(), dest)
}
}
@ -1401,6 +1446,15 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
write_affine(dest, a, b)?;
dest.write_char(')')
},
Is(ref list) | Where(ref list) => {
match *self {
Where(..) => dest.write_str(":where(")?,
Is(..) => dest.write_str(":is(")?,
_ => unreachable!(),
}
serialize_selector_list(list.iter(), dest)?;
dest.write_str(")")
},
NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),
}
}
@ -2086,6 +2140,28 @@ where
}
}
fn parse_is_or_where<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
component: impl FnOnce(Box<[Selector<Impl>]>) -> Component<Impl>,
) -> Result<Component<Impl>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
debug_assert!(parser.parse_is_and_where());
let inner = SelectorList::parse(parser, input)?;
// https://drafts.csswg.org/selectors/#matches-pseudo:
//
// Pseudo-elements cannot be represented by the matches-any
// pseudo-class; they are not valid within :is().
//
if inner.0.iter().any(|i| i.has_pseudo_element()) {
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidPseudoElementInsideWhere));
}
Ok(component(inner.0.into_vec().into_boxed_slice()))
}
fn parse_functional_pseudo_class<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
@ -2105,6 +2181,8 @@ where
"nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?),
"nth-last-child" => return Ok(parse_nth_pseudo_class(input, Component::NthLastChild)?),
"nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?),
"is" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, Component::Is),
"where" if parser.parse_is_and_where() => return parse_is_or_where(parser, input, Component::Where),
"host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input)?))),
"not" => {
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
@ -2396,17 +2474,6 @@ pub mod tests {
}
}
impl Visit for PseudoClass {
type Impl = DummySelectorImpl;
fn visit<V>(&self, _visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
true
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DummySelectorImpl;
@ -2469,6 +2536,10 @@ pub mod tests {
true
}
fn parse_is_and_where(&self) -> bool {
true
}
fn parse_part(&self) -> bool {
true
}
@ -3100,6 +3171,10 @@ pub mod tests {
assert!(parse("slot::slotted(div,foo)::first-line").is_err());
assert!(parse("::slotted(div)::before").is_ok());
assert!(parse("slot::slotted(div,foo)").is_err());
assert!(parse("foo:where()").is_err());
assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
assert!(parse("foo:where(::before)").is_err());
}
#[test]

View file

@ -7,13 +7,13 @@
#![deny(missing_docs)]
use crate::attr::NamespaceConstraint;
use crate::parser::{Combinator, Component, SelectorImpl};
use crate::parser::{Combinator, Component, Selector, SelectorImpl};
/// A trait to visit selector properties.
///
/// All the `visit_foo` methods return a boolean indicating whether the
/// traversal should continue or not.
pub trait SelectorVisitor {
pub trait SelectorVisitor: Sized {
/// The selector implementation this visitor wants to visit.
type Impl: SelectorImpl;
@ -34,6 +34,19 @@ pub trait SelectorVisitor {
true
}
/// Visit a nested selector list. The caller is responsible to call visit
/// into the internal selectors if / as needed.
///
/// The default implementation does this.
fn visit_selector_list(&mut self, list: &[Selector<Self::Impl>]) -> bool {
for nested in list {
if !nested.visit(self) {
return false;
}
}
true
}
/// Visits a complex selector.
///
/// Gets the combinator to the right of the selector, or `None` if the
@ -42,31 +55,3 @@ pub trait SelectorVisitor {
true
}
}
/// Enables traversing selector components stored in various types
pub trait Visit {
/// The type parameter of selector component types.
type Impl: SelectorImpl;
/// Traverse selector components inside `self`.
///
/// Implementations of this method should call `SelectorVisitor` methods
/// or other impls of `Visit` as appropriate based on the fields of `Self`.
///
/// A return value of `false` indicates terminating the traversal.
/// It should be propagated with an early return.
/// On the contrary, `true` indicates that all fields of `self` have been traversed:
///
/// ```rust,ignore
/// if !visitor.visit_simple_selector(&self.some_simple_selector) {
/// return false;
/// }
/// if !self.some_component.visit(visitor) {
/// return false;
/// }
/// true
/// ```
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>;
}

View file

@ -14,8 +14,8 @@ use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use crate::values::serialize_atom_identifier;
use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
use selectors::parser::SelectorParseErrorKind;
use selectors::parser::{self as selector_parser, Selector};
use selectors::parser::{SelectorParseErrorKind, Visit};
use selectors::visitor::SelectorVisitor;
use selectors::SelectorList;
use std::fmt;
@ -109,25 +109,6 @@ impl ToCss for NonTSPseudoClass {
}
}
impl Visit for NonTSPseudoClass {
type Impl = SelectorImpl;
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
if let NonTSPseudoClass::MozAny(ref selectors) = *self {
for selector in selectors.iter() {
if !selector.visit(visitor) {
return false;
}
}
}
true
}
}
impl NonTSPseudoClass {
/// Parses the name and returns a non-ts-pseudo-class if succeeds.
/// None otherwise. It doesn't check whether the pseudo-class is enabled
@ -280,6 +261,21 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
fn has_zero_specificity(&self) -> bool {
matches!(*self, NonTSPseudoClass::MozNativeAnonymousNoSpecificity)
}
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
if let NonTSPseudoClass::MozAny(ref selectors) = *self {
for selector in selectors.iter() {
if !selector.visit(visitor) {
return false;
}
}
}
true
}
}
/// The dummy struct we use to implement our selector parsing.
@ -354,6 +350,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
true
}
#[inline]
fn parse_is_and_where(&self) -> bool {
static_prefs::pref!("layout.css.is-where-selectors.enabled")
}
#[inline]
fn parse_part(&self) -> bool {
self.chrome_rules_enabled() || static_prefs::pref!("layout.css.shadow-parts.enabled")

View file

@ -13,7 +13,7 @@ use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
use selectors::attr::NamespaceConstraint;
use selectors::parser::{Combinator, Component};
use selectors::parser::{Selector, SelectorIter, Visit};
use selectors::parser::{Selector, SelectorIter};
use selectors::visitor::SelectorVisitor;
use smallvec::SmallVec;
@ -322,6 +322,17 @@ impl InvalidationMap {
}
/// A struct that collects invalidations for a given compound selector.
///
/// FIXME(emilio, bug 1509418): :where is mishandled, figure out the right way
/// to do invalidation for :where when combinators are inside.
///
/// Simplest feasible idea seems to be: For each :where branch, if there are
/// combinators in the branch, treat as its own selector (basically, call
/// `.note_selector` with the nested selector). That may over-invalidate, but
/// not too much. If there are no combinators, then behave like we do today and
/// use the outer selector as a whole. If we care a lot about that potential
/// over-invalidation where combinators are present then we need more complex
/// data-structures in `Dependency`.
struct CompoundSelectorDependencyCollector<'a> {
/// The state this compound selector is affected by.
state: ElementState,

View file

@ -353,198 +353,6 @@ impl StrongRuleNode {
}
}
/// Returns true if any properties specified by `rule_type_mask` was set by
/// an author rule.
#[cfg(feature = "gecko")]
pub fn has_author_specified_rules<E>(
&self,
mut element: E,
mut pseudo: Option<PseudoElement>,
guards: &StylesheetGuards,
rule_type_mask: u32,
author_colors_allowed: bool,
) -> bool
where
E: crate::dom::TElement,
{
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BACKGROUND;
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_BORDER;
use crate::gecko_bindings::structs::NS_AUTHOR_SPECIFIED_PADDING;
use crate::properties::{CSSWideKeyword, LonghandId};
use crate::properties::{PropertyDeclaration, PropertyDeclarationId};
use std::borrow::Cow;
// Reset properties:
const BACKGROUND_PROPS: &'static [LonghandId] =
&[LonghandId::BackgroundColor, LonghandId::BackgroundImage];
const BORDER_PROPS: &'static [LonghandId] = &[
LonghandId::BorderTopColor,
LonghandId::BorderTopStyle,
LonghandId::BorderTopWidth,
LonghandId::BorderRightColor,
LonghandId::BorderRightStyle,
LonghandId::BorderRightWidth,
LonghandId::BorderBottomColor,
LonghandId::BorderBottomStyle,
LonghandId::BorderBottomWidth,
LonghandId::BorderLeftColor,
LonghandId::BorderLeftStyle,
LonghandId::BorderLeftWidth,
LonghandId::BorderTopLeftRadius,
LonghandId::BorderTopRightRadius,
LonghandId::BorderBottomRightRadius,
LonghandId::BorderBottomLeftRadius,
LonghandId::BorderInlineStartColor,
LonghandId::BorderInlineStartStyle,
LonghandId::BorderInlineStartWidth,
LonghandId::BorderInlineEndColor,
LonghandId::BorderInlineEndStyle,
LonghandId::BorderInlineEndWidth,
LonghandId::BorderBlockStartColor,
LonghandId::BorderBlockStartStyle,
LonghandId::BorderBlockStartWidth,
LonghandId::BorderBlockEndColor,
LonghandId::BorderBlockEndStyle,
LonghandId::BorderBlockEndWidth,
];
const PADDING_PROPS: &'static [LonghandId] = &[
LonghandId::PaddingTop,
LonghandId::PaddingRight,
LonghandId::PaddingBottom,
LonghandId::PaddingLeft,
LonghandId::PaddingInlineStart,
LonghandId::PaddingInlineEnd,
LonghandId::PaddingBlockStart,
LonghandId::PaddingBlockEnd,
];
// Set of properties that we are currently interested in.
let mut properties = LonghandIdSet::new();
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
for id in BACKGROUND_PROPS {
properties.insert(*id);
}
}
if rule_type_mask & NS_AUTHOR_SPECIFIED_BORDER != 0 {
for id in BORDER_PROPS {
properties.insert(*id);
}
}
if rule_type_mask & NS_AUTHOR_SPECIFIED_PADDING != 0 {
for id in PADDING_PROPS {
properties.insert(*id);
}
}
// If author colors are not allowed, don't look at those properties
// (except for background-color which is special and we handle below).
if !author_colors_allowed {
properties.remove_all(LonghandIdSet::ignored_when_colors_disabled());
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
properties.insert(LonghandId::BackgroundColor);
}
}
let mut element_rule_node = Cow::Borrowed(self);
loop {
// We need to be careful not to count styles covered up by
// user-important or UA-important declarations. But we do want to
// catch explicit inherit styling in those and check our parent
// element to see whether we have user styling for those properties.
// Note that we don't care here about inheritance due to lack of a
// specified value, since all the properties we care about are reset
// properties.
let mut inherited_properties = LonghandIdSet::new();
let mut have_explicit_ua_inherit = false;
for node in element_rule_node.self_and_ancestors() {
let source = node.style_source();
let declarations = if source.is_some() {
source
.as_ref()
.unwrap()
.read(node.cascade_level().guard(guards))
.declaration_importance_iter()
} else {
continue;
};
// Iterate over declarations of the longhands we care about.
let node_importance = node.importance();
let longhands = declarations.rev().filter_map(|(declaration, importance)| {
if importance != node_importance {
return None;
}
match declaration.id() {
PropertyDeclarationId::Longhand(id) => Some((id, declaration)),
_ => None,
}
});
let is_author = node.cascade_level().origin() == Origin::Author;
for (id, declaration) in longhands {
if !properties.contains(id) {
continue;
}
if is_author {
if !author_colors_allowed {
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
if color.is_transparent() {
return true;
}
continue;
}
}
return true;
}
// This property was set by a non-author rule.
// Stop looking for it in this element's rule
// nodes.
properties.remove(id);
// However, if it is inherited, then it might be
// inherited from an author rule from an
// ancestor element's rule nodes.
if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) {
have_explicit_ua_inherit = true;
inherited_properties.insert(id);
}
}
}
if !have_explicit_ua_inherit {
break;
}
// Continue to the parent element and search for the inherited properties.
if let Some(pseudo) = pseudo.take() {
if pseudo.inherits_from_default_values() {
break;
}
} else {
element = match element.inheritance_parent() {
Some(parent) => parent,
None => break,
};
let parent_data = element.mutate_data().unwrap();
let parent_rule_node = parent_data.styles.primary().rules().clone();
element_rule_node = Cow::Owned(parent_rule_node);
}
properties = inherited_properties;
}
false
}
/// Returns true if there is either animation or transition level rule.
pub fn has_animation_or_transition_rules(&self) -> bool {
self.self_and_ancestors()

View file

@ -2,8 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// FIXME: Make that forbid once the two unsafe gecko-specific methods are removed.
#![deny(unsafe_code)]
#![forbid(unsafe_code)]
use crate::properties::PropertyDeclarationBlock;
use crate::shared_lock::{Locked, SharedRwLockReadGuard};
@ -53,31 +52,6 @@ impl StyleSource {
let _ = write!(writer, " -> {:?}", self.read(guard).declarations());
}
// This is totally unsafe, should be removed when we figure out the cause of
// bug 1607553.
#[allow(unsafe_code)]
#[cfg(feature = "gecko")]
pub(super) unsafe fn dump_unchecked<W: Write>(&self, writer: &mut W) {
if let Some(ref rule) = self.0.as_first() {
let rule = rule.read_unchecked();
let _ = write!(writer, "{:?}", rule.selectors);
}
let _ = write!(writer, " -> {:?}", self.read_unchecked().declarations());
}
// This is totally unsafe, should be removed when we figure out the cause of
// bug 1607553.
#[inline]
#[allow(unsafe_code)]
#[cfg(feature = "gecko")]
pub(super) unsafe fn read_unchecked(&self) -> &PropertyDeclarationBlock {
let block: &Locked<PropertyDeclarationBlock> = match self.0.borrow() {
ArcUnionBorrow::First(ref rule) => &rule.get().read_unchecked().block,
ArcUnionBorrow::Second(ref block) => block.get(),
};
block.read_unchecked()
}
/// Read the style source guard, and obtain thus read access to the
/// underlying property declaration block.
#[inline]

View file

@ -500,6 +500,15 @@ fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a>
// match the slotted <span>.
Component::Slotted(ref selector) => find_bucket(selector.iter()),
Component::Host(Some(ref selector)) => find_bucket(selector.iter()),
Component::Is(ref list) | Component::Where(ref list) => {
if list.len() == 1 {
find_bucket(list[0].iter())
} else {
// TODO(emilio): We should handle the multiple element case by
// inserting multiple entries in the map.
Bucket::Universal
}
},
_ => Bucket::Universal,
}
}

View file

@ -19,7 +19,7 @@ use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
use cssparser::{serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation, ToCss};
use fxhash::FxHashMap;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::parser::{SelectorParseErrorKind, Visit};
use selectors::parser::SelectorParseErrorKind;
use selectors::visitor::SelectorVisitor;
use std::fmt;
use std::mem;
@ -315,6 +315,13 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
fn has_zero_specificity(&self) -> bool {
false
}
fn visit<V>(&self, _: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
true
}
}
impl ToCss for NonTSPseudoClass {
@ -352,17 +359,6 @@ impl ToCss for NonTSPseudoClass {
}
}
impl Visit for NonTSPseudoClass {
type Impl = SelectorImpl;
fn visit<V>(&self, _: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
true
}
}
impl NonTSPseudoClass {
/// Gets a given state flag for this pseudo-class. This is used to do
/// selector matching, and it's set from the DOM.

View file

@ -42,8 +42,7 @@ use selectors::attr::{CaseSensitivity, NamespaceConstraint};
use selectors::bloom::BloomFilter;
use selectors::matching::VisitedHandlingMode;
use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
use selectors::parser::{SelectorIter, Visit};
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorIter};
use selectors::visitor::SelectorVisitor;
use selectors::NthIndexCache;
use servo_arc::{Arc, ArcBorrow};
@ -1530,11 +1529,11 @@ impl SelectorMapEntry for RevalidationSelectorAndHashes {
/// A selector visitor implementation that collects all the state the Stylist
/// cares about a selector.
struct StylistSelectorVisitor<'a> {
/// Whether the selector needs revalidation for the style sharing cache.
needs_revalidation: bool,
/// Whether we've past the rightmost compound selector, not counting
/// pseudo-elements.
passed_rightmost_selector: bool,
/// Whether the selector needs revalidation for the style sharing cache.
needs_revalidation: &'a mut bool,
/// The filter with all the id's getting referenced from rightmost
/// selectors.
mapped_ids: &'a mut PrecomputedHashSet<Atom>,
@ -1582,14 +1581,10 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
type Impl = SelectorImpl;
fn visit_complex_selector(&mut self, combinator: Option<Combinator>) -> bool {
self.needs_revalidation =
self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
*self.needs_revalidation =
*self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
// NOTE(emilio): This works properly right now because we can't store
// complex selectors in nested selectors, otherwise we may need to
// rethink this.
//
// Also, note that this call happens before we visit any of the simple
// NOTE(emilio): this call happens before we visit any of the simple
// selectors in the next ComplexSelector, so we can use this to skip
// looking at them.
self.passed_rightmost_selector = self.passed_rightmost_selector ||
@ -1598,6 +1593,22 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
true
}
fn visit_selector_list(&mut self, list: &[Selector<Self::Impl>]) -> bool {
for selector in list {
let mut nested = StylistSelectorVisitor {
passed_rightmost_selector: false,
needs_revalidation: &mut *self.needs_revalidation,
attribute_dependencies: &mut *self.attribute_dependencies,
state_dependencies: &mut *self.state_dependencies,
document_state_dependencies: &mut *self.document_state_dependencies,
mapped_ids: &mut *self.mapped_ids,
};
let _ret = selector.visit(&mut nested);
debug_assert!(_ret, "We never return false");
}
true
}
fn visit_attribute_selector(
&mut self,
_ns: &NamespaceConstraint<&Namespace>,
@ -1610,7 +1621,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
}
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
self.needs_revalidation = self.needs_revalidation ||
*self.needs_revalidation = *self.needs_revalidation ||
component_needs_revalidation(s, self.passed_rightmost_selector);
match *s {
@ -2022,8 +2033,9 @@ impl CascadeData {
if rebuild_kind.should_rebuild_invalidation() {
self.invalidation_map.note_selector(selector, quirks_mode)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: false,
needs_revalidation: &mut needs_revalidation,
passed_rightmost_selector: false,
attribute_dependencies: &mut self.attribute_dependencies,
state_dependencies: &mut self.state_dependencies,
@ -2033,7 +2045,7 @@ impl CascadeData {
rule.selector.visit(&mut visitor);
if visitor.needs_revalidation {
if needs_revalidation {
self.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(
rule.selector.clone(),
@ -2365,14 +2377,15 @@ pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
let mut mapped_ids = Default::default();
let mut state_dependencies = ElementState::empty();
let mut document_state_dependencies = DocumentState::empty();
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: false,
passed_rightmost_selector: false,
needs_revalidation: &mut needs_revalidation,
attribute_dependencies: &mut attribute_dependencies,
state_dependencies: &mut state_dependencies,
document_state_dependencies: &mut document_state_dependencies,
mapped_ids: &mut mapped_ids,
};
s.visit(&mut visitor);
visitor.needs_revalidation
needs_revalidation
}