Auto merge of #17439 - bholley:compound_left_to_right, r=SimonSapin

Match compound selectors left-to-right (second try)

https://bugzilla.mozilla.org/show_bug.cgi?id=1373800

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17439)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-06-21 09:28:43 -07:00 committed by GitHub
commit b211664e87
15 changed files with 482 additions and 303 deletions

View file

@ -21,8 +21,8 @@ use rule_tree::CascadeLevel;
use selector_parser::{AttrValue, ElementExt, PreExistingComputedValues};
use selector_parser::{PseudoClassStringArg, PseudoElement};
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
use selectors::sink::Push;
use shared_lock::Locked;
use sink::Push;
use smallvec::VecLike;
use std::fmt;
#[cfg(feature = "gecko")] use std::collections::HashMap;

View file

@ -310,7 +310,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
Selector::parse(self, input)
})?;
// Selectors inside `:-moz-any` may not include combinators.
if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
if selectors.iter().flat_map(|x| x.iter_raw_match_order()).any(|s| s.is_combinator()) {
return Err(SelectorParseError::UnexpectedIdent("-moz-any".into()).into())
}
NonTSPseudoClass::MozAny(selectors.into_boxed_slice())

View file

@ -76,8 +76,8 @@ use selectors::Element;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext};
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode};
use selectors::sink::Push;
use shared_lock::Locked;
use sink::Push;
use smallvec::VecLike;
use std::cell::RefCell;
use std::collections::HashMap;

View file

@ -53,7 +53,7 @@ impl fmt::Debug for Invalidation {
use cssparser::ToCss;
f.write_str("Invalidation(")?;
for component in self.selector.iter_raw_rev_from(self.offset - 1) {
for component in self.selector.iter_raw_parse_order_from(self.offset - 1) {
if matches!(*component, Component::Combinator(..)) {
break;
}
@ -568,7 +568,7 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
if matches!(next_combinator, Combinator::PseudoElement) {
let pseudo_selector =
invalidation.selector
.iter_raw_rev_from(next_combinator_offset - 1)
.iter_raw_parse_order_from(next_combinator_offset - 1)
.next()
.unwrap();
let pseudo = match *pseudo_selector {

View file

@ -126,7 +126,6 @@ pub mod sharing;
pub mod stylist;
#[cfg(feature = "servo")] #[allow(unsafe_code)] pub mod servo;
pub mod sequential;
pub mod sink;
pub mod str;
pub mod style_adjuster;
pub mod stylesheet_set;

View file

@ -369,15 +369,12 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
}
}
/// Searches the selector from right to left, beginning to the left of the
/// ::pseudo-element (if any), and ending at the first combinator.
/// Searches a compound selector from left to right. If the compound selector
/// is a pseudo-element, it's ignored.
///
/// The first non-None value returned from |f| is returned.
///
/// Effectively, pseudo-elements are ignored, given only state pseudo-classes
/// may appear before them.
#[inline(always)]
fn find_from_right<F, R>(mut iter: SelectorIter<SelectorImpl>,
fn find_from_left<F, R>(mut iter: SelectorIter<SelectorImpl>,
mut f: F)
-> Option<R>
where F: FnMut(&Component<SelectorImpl>) -> Option<R>,
@ -388,6 +385,8 @@ fn find_from_right<F, R>(mut iter: SelectorIter<SelectorImpl>,
}
}
// Effectively, pseudo-elements are ignored, given only state pseudo-classes
// may appear before them.
if iter.next_sequence() == Some(Combinator::PseudoElement) {
for ss in &mut iter {
if let Some(r) = f(ss) {
@ -403,7 +402,7 @@ fn find_from_right<F, R>(mut iter: SelectorIter<SelectorImpl>,
#[inline(always)]
pub fn get_id_name(iter: SelectorIter<SelectorImpl>)
-> Option<Atom> {
find_from_right(iter, |ss| {
find_from_left(iter, |ss| {
// TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode.
if let Component::ID(ref id) = *ss {
@ -417,7 +416,7 @@ pub fn get_id_name(iter: SelectorIter<SelectorImpl>)
#[inline(always)]
pub fn get_class_name(iter: SelectorIter<SelectorImpl>)
-> Option<Atom> {
find_from_right(iter, |ss| {
find_from_left(iter, |ss| {
// TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode.
if let Component::Class(ref class) = *ss {
@ -431,7 +430,7 @@ pub fn get_class_name(iter: SelectorIter<SelectorImpl>)
#[inline(always)]
pub fn get_local_name(iter: SelectorIter<SelectorImpl>)
-> Option<LocalNameSelector<SelectorImpl>> {
find_from_right(iter, |ss| {
find_from_left(iter, |ss| {
if let Component::LocalName(ref n) = *ss {
return Some(LocalNameSelector {
name: n.name.clone(),

View file

@ -1,53 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! Small helpers to abstract over different containers.
#![deny(missing_docs)]
use smallvec::{Array, SmallVec};
use std::marker::PhantomData;
/// A trait to abstract over a `push` method that may be implemented for
/// different kind of types.
///
/// Used to abstract over `Array`, `SmallVec` and `Vec`, and also to implement a
/// type which `push` method does only tweak a byte when we only need to check
/// for the presence of something.
pub trait Push<T> {
/// Push a value into self.
fn push(&mut self, value: T);
}
impl<T> Push<T> for Vec<T> {
fn push(&mut self, value: T) {
Vec::push(self, value);
}
}
impl<A: Array> Push<A::Item> for SmallVec<A> {
fn push(&mut self, value: A::Item) {
SmallVec::push(self, value);
}
}
/// A struct that implements `Push`, but only stores whether it's empty.
pub struct ForgetfulSink<T>(bool, PhantomData<T>);
impl<T> ForgetfulSink<T> {
/// Trivially construct a new `ForgetfulSink`.
pub fn new() -> Self {
ForgetfulSink(true, PhantomData)
}
/// Whether this sink is empty or not.
pub fn is_empty(&self) -> bool {
self.0
}
}
impl<T> Push<T> for ForgetfulSink<T> {
fn push(&mut self, _value: T) {
self.0 = false;
}
}

View file

@ -31,9 +31,9 @@ use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContex
use selectors::matching::AFFECTED_BY_PRESENTATIONAL_HINTS;
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorAndHashes};
use selectors::parser::{SelectorIter, SelectorMethods};
use selectors::sink::Push;
use selectors::visitor::SelectorVisitor;
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use sink::Push;
use smallvec::VecLike;
use std::fmt::Debug;
#[cfg(feature = "servo")]