Shrink selectors::Component, add case-insensitive for other attr selectors

* https://bugzilla.mozilla.org/show_bug.cgi?id=1364148
* https://bugzilla.mozilla.org/show_bug.cgi?id=1364162
This commit is contained in:
Simon Sapin 2017-05-17 22:21:00 +02:00
parent 83c7824fda
commit 9376abdd2c
15 changed files with 377 additions and 357 deletions

View file

@ -86,10 +86,9 @@ use net_traits::request::CorsSettings;
use ref_filter_map::ref_filter_map; 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; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, matches_selector_list}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, matches_selector_list};
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::parser::NamespaceConstraint;
use servo_atoms::Atom; use servo_atoms::Atom;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::borrow::Cow; use std::borrow::Cow;
@ -2387,13 +2386,13 @@ impl<'a> ::selectors::Element for Root<Element> {
} }
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName, local_name: &LocalName,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&String>)
-> bool { -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => { NamespaceConstraint::Specific(ref ns) => {
self.get_attribute(&ns.url, local_name) self.get_attribute(ns, local_name)
.map_or(false, |attr| attr.value().eval_selector(operation)) .map_or(false, |attr| attr.value().eval_selector(operation))
} }
NamespaceConstraint::Any => { NamespaceConstraint::Any => {

View file

@ -50,9 +50,8 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
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; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::parser::NamespaceConstraint;
use servo_atoms::Atom; use servo_atoms::Atom;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::fmt; use std::fmt;
@ -606,13 +605,13 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName, local_name: &LocalName,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&String>)
-> bool { -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => { NamespaceConstraint::Specific(ref ns) => {
self.get_attr_enum(&ns.url, local_name) self.get_attr_enum(ns, local_name)
.map_or(false, |value| value.eval_selector(operation)) .map_or(false, |value| value.eval_selector(operation))
} }
NamespaceConstraint::Any => { NamespaceConstraint::Any => {
@ -1159,13 +1158,13 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
} }
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName, local_name: &LocalName,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&String>)
-> bool { -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => { NamespaceConstraint::Specific(ref ns) => {
self.get_attr_enum(&ns.url, local_name) self.get_attr_enum(ns, local_name)
.map_or(false, |value| value.eval_selector(operation)) .map_or(false, |value| value.eval_selector(operation))
} }
NamespaceConstraint::Any => { NamespaceConstraint::Any => {

View file

@ -2,24 +2,54 @@
* 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/. */
use cssparser::ToCss;
use parser::SelectorImpl; use parser::SelectorImpl;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::fmt;
pub enum AttrSelectorOperation<'a, Impl: SelectorImpl> where Impl::AttrValue: 'a { #[derive(Eq, PartialEq, Clone)]
pub struct AttrSelectorWithNamespace<Impl: SelectorImpl> {
pub namespace: NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>,
pub local_name: Impl::LocalName,
pub local_name_lower: Impl::LocalName,
pub operation: AttrSelectorOperation<Impl::AttrValue>,
pub never_matches: bool,
}
impl<Impl: SelectorImpl> AttrSelectorWithNamespace<Impl> {
pub fn namespace(&self) -> NamespaceConstraint<&Impl::NamespaceUrl> {
match self.namespace {
NamespaceConstraint::Any => NamespaceConstraint::Any,
NamespaceConstraint::Specific((_, ref url)) => {
NamespaceConstraint::Specific(url)
}
}
}
}
#[derive(Eq, PartialEq, Clone)]
pub enum NamespaceConstraint<NamespaceUrl> {
Any,
/// Empty string for no namespace
Specific(NamespaceUrl),
}
#[derive(Eq, PartialEq, Clone)]
pub enum AttrSelectorOperation<AttrValue> {
Exists, Exists,
WithValue { WithValue {
operator: AttrSelectorOperator, operator: AttrSelectorOperator,
case_sensitivity: CaseSensitivity, case_sensitivity: CaseSensitivity,
expected_value: &'a Impl::AttrValue, expected_value: AttrValue,
} }
} }
impl<'a, Impl: SelectorImpl> AttrSelectorOperation<'a, Impl> { impl<AttrValue> AttrSelectorOperation<AttrValue> {
pub fn eval_str(&self, element_attr_value: &str) -> bool pub fn eval_str(&self, element_attr_value: &str) -> bool where AttrValue: AsRef<str> {
where Impl::AttrValue: AsRef<str> {
match *self { match *self {
AttrSelectorOperation::Exists => true, AttrSelectorOperation::Exists => true,
AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => { AttrSelectorOperation::WithValue { operator, case_sensitivity, ref expected_value } => {
operator.eval_str(element_attr_value, expected_value.as_ref(), case_sensitivity) operator.eval_str(element_attr_value, expected_value.as_ref(), case_sensitivity)
} }
} }
@ -28,12 +58,25 @@ impl<'a, Impl: SelectorImpl> AttrSelectorOperation<'a, Impl> {
#[derive(Eq, PartialEq, Clone, Copy)] #[derive(Eq, PartialEq, Clone, Copy)]
pub enum AttrSelectorOperator { pub enum AttrSelectorOperator {
Equal, // = Equal,
Includes, // ~= Includes,
DashMatch, // |= DashMatch,
Prefix, // ^= Prefix,
Substring, // *= Substring,
Suffix, // $= Suffix,
}
impl ToCss for AttrSelectorOperator {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
dest.write_str(match *self {
AttrSelectorOperator::Equal => " = ",
AttrSelectorOperator::Includes => " ~= ",
AttrSelectorOperator::DashMatch => " |= ",
AttrSelectorOperator::Prefix => " ^= ",
AttrSelectorOperator::Substring => " *= ",
AttrSelectorOperator::Suffix => " $= ",
})
}
} }
impl AttrSelectorOperator { impl AttrSelectorOperator {

View file

@ -2,10 +2,10 @@
* 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/. */
use attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity}; use attr::{AttrSelectorOperation, NamespaceConstraint};
use bloom::BloomFilter; use bloom::BloomFilter;
use parser::{Combinator, ComplexSelector, Component, LocalName, NamespaceConstraint}; use parser::{Combinator, ComplexSelector, Component, LocalName};
use parser::{Selector, SelectorInner, SelectorIter, SelectorImpl, AttrSelector}; use parser::{Selector, SelectorInner, SelectorIter, SelectorImpl};
use std::borrow::Borrow; use std::borrow::Borrow;
use tree::Element; use tree::Element;
@ -387,8 +387,7 @@ fn matches_simple_selector<E, F>(
element.match_pseudo_element(pseudo, context) element.match_pseudo_element(pseudo, context)
} }
Component::LocalName(LocalName { ref name, ref lower_name }) => { Component::LocalName(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name }; element.get_local_name() == select_name(element, name, lower_name).borrow()
element.get_local_name() == name.borrow()
} }
Component::ExplicitUniversalType | Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace => { Component::ExplicitAnyNamespace => {
@ -399,9 +398,8 @@ fn matches_simple_selector<E, F>(
element.get_namespace() == url.borrow() element.get_namespace() == url.borrow()
} }
Component::ExplicitNoNamespace => { Component::ExplicitNoNamespace => {
// Rust types default, not default namespace let ns = ::parser::namespace_empty_string::<E::Impl>();
let empty_string = <E::Impl as SelectorImpl>::NamespaceUrl::default(); element.get_namespace() == ns.borrow()
element.get_namespace() == empty_string.borrow()
} }
// TODO: case-sensitivity depends on the document type and quirks mode // TODO: case-sensitivity depends on the document type and quirks mode
Component::ID(ref id) => { Component::ID(ref id) => {
@ -411,63 +409,58 @@ fn matches_simple_selector<E, F>(
Component::Class(ref class) => { Component::Class(ref class) => {
element.has_class(class) element.has_class(class)
} }
Component::AttrExists(ref attr) => { Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => {
let (ns, n) = unpack_attr_selector(element, attr); element.attr_matches(
element.attr_matches(ns, n, &AttrSelectorOperation::Exists) &NamespaceConstraint::Specific(&::parser::namespace_empty_string::<E::Impl>()),
select_name(element, local_name, local_name_lower),
&AttrSelectorOperation::Exists
)
} }
Component::AttrEqual(ref attr, ref value, case_sensitivity) => { Component::AttributeInNoNamespace {
let (ns, n) = unpack_attr_selector(element, attr); ref local_name,
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue { ref local_name_lower,
operator: AttrSelectorOperator::Equal, ref value,
case_sensitivity: case_sensitivity, operator,
expected_value: value case_sensitivity,
}) never_matches,
} } => {
Component::AttrIncludes(ref attr, ref value) => { if never_matches {
let (ns, n) = unpack_attr_selector(element, attr);
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue {
operator: AttrSelectorOperator::Includes,
case_sensitivity: CaseSensitivity::CaseSensitive,
expected_value: value
})
}
Component::AttrDashMatch(ref attr, ref value) => {
let (ns, n) = unpack_attr_selector(element, attr);
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue {
operator: AttrSelectorOperator::DashMatch,
case_sensitivity: CaseSensitivity::CaseSensitive,
expected_value: value
})
}
Component::AttrPrefixMatch(ref attr, ref value) => {
let (ns, n) = unpack_attr_selector(element, attr);
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue {
operator: AttrSelectorOperator::Prefix,
case_sensitivity: CaseSensitivity::CaseSensitive,
expected_value: value
})
}
Component::AttrSubstringMatch(ref attr, ref value) => {
let (ns, n) = unpack_attr_selector(element, attr);
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue {
operator: AttrSelectorOperator::Substring,
case_sensitivity: CaseSensitivity::CaseSensitive,
expected_value: value
})
}
Component::AttrSuffixMatch(ref attr, ref value) => {
let (ns, n) = unpack_attr_selector(element, attr);
element.attr_matches(ns, n, &AttrSelectorOperation::WithValue {
operator: AttrSelectorOperator::Suffix,
case_sensitivity: CaseSensitivity::CaseSensitive,
expected_value: value
})
}
Component::AttrIncludesNeverMatch(..) |
Component::AttrPrefixNeverMatch(..) |
Component::AttrSubstringNeverMatch(..) |
Component::AttrSuffixNeverMatch(..) => {
false false
} else {
element.attr_matches(
&NamespaceConstraint::Specific(&::parser::namespace_empty_string::<E::Impl>()),
select_name(element, local_name, local_name_lower),
&AttrSelectorOperation::WithValue {
operator: operator,
case_sensitivity: case_sensitivity,
expected_value: value,
}
)
}
}
Component::AttributeOther(ref attr_sel) => {
if attr_sel.never_matches {
return false
} else {
element.attr_matches(
&attr_sel.namespace(),
select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),
&match attr_sel.operation {
AttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
AttrSelectorOperation::WithValue {
operator,
case_sensitivity,
ref expected_value,
} => {
AttrSelectorOperation::WithValue {
operator: operator,
case_sensitivity: case_sensitivity,
expected_value: expected_value,
}
}
}
)
}
} }
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, flags_setter)
@ -519,16 +512,15 @@ fn matches_simple_selector<E, F>(
} }
} }
fn unpack_attr_selector<'a, E>(element: &E, attr: &'a AttrSelector<E::Impl>) fn select_name<'a, E>(element: &E, local_name: &'a <E::Impl as SelectorImpl>::LocalName,
-> (&'a NamespaceConstraint<E::Impl>, local_name_lower: &'a <E::Impl as SelectorImpl>::LocalName)
&'a <E::Impl as SelectorImpl>::LocalName) -> &'a <E::Impl as SelectorImpl>::LocalName
where E: Element { where E: Element {
let name = if element.is_html_element_in_html_document() { if element.is_html_element_in_html_document() {
&attr.lower_name local_name_lower
} else { } else {
&attr.name local_name
}; }
(&attr.namespace, name)
} }
#[inline] #[inline]

View file

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use arcslice::ArcSlice; use arcslice::ArcSlice;
use attr::{CaseSensitivity, SELECTOR_WHITESPACE}; use attr::{AttrSelectorWithNamespace, AttrSelectorOperation, AttrSelectorOperator};
use attr::{CaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter}; use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
use precomputed_hash::PrecomputedHash; use precomputed_hash::PrecomputedHash;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -313,18 +314,28 @@ impl<Impl: SelectorImpl> SelectorMethods for Component<Impl> {
return false; return false;
} }
} }
}, }
AttrExists(ref selector) |
AttrEqual(ref selector, _, _) | AttributeInNoNamespace { ref local_name, ref local_name_lower, .. } |
AttrIncludes(ref selector, _) | AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => {
AttrDashMatch(ref selector, _) | if !visitor.visit_attribute_selector(
AttrPrefixMatch(ref selector, _) | &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
AttrSubstringMatch(ref selector, _) | local_name,
AttrSuffixMatch(ref selector, _) => { local_name_lower,
if !visitor.visit_attribute_selector(selector) { ) {
return false; return false;
} }
} }
AttributeOther(ref attr_selector) => {
if !visitor.visit_attribute_selector(
&attr_selector.namespace(),
&attr_selector.local_name,
&attr_selector.local_name_lower,
) {
return false;
}
}
NonTSPseudoClass(ref pseudo_class) => { NonTSPseudoClass(ref pseudo_class) => {
if !pseudo_class.visit(visitor) { if !pseudo_class.visit(visitor) {
return false; return false;
@ -337,6 +348,11 @@ impl<Impl: SelectorImpl> SelectorMethods for Component<Impl> {
} }
} }
pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
// Rust types default, not default namespace
Impl::NamespaceUrl::default()
}
/// A ComplexSelectors stores a sequence of simple selectors and combinators. The /// A ComplexSelectors stores a sequence of simple selectors and combinators. The
/// iterator classes allow callers to iterate at either the raw sequence level or /// iterator classes allow callers to iterate at either the raw sequence level or
/// at the level of sequences of simple selectors separated by combinators. Most /// at the level of sequences of simple selectors separated by combinators. Most
@ -523,19 +539,20 @@ pub enum Component<Impl: SelectorImpl> {
ID(Impl::Identifier), ID(Impl::Identifier),
Class(Impl::ClassName), Class(Impl::ClassName),
// Attribute selectors AttributeInNoNamespaceExists {
AttrExists(AttrSelector<Impl>), // [foo] local_name: Impl::LocalName,
AttrEqual(AttrSelector<Impl>, Impl::AttrValue, CaseSensitivity), // [foo=bar] local_name_lower: Impl::LocalName,
AttrIncludes(AttrSelector<Impl>, Impl::AttrValue), // [foo~=bar] },
AttrDashMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo|=bar] AttributeInNoNamespace {
AttrPrefixMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo^=bar] local_name: Impl::LocalName,
AttrSubstringMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo*=bar] local_name_lower: Impl::LocalName,
AttrSuffixMatch(AttrSelector<Impl>, Impl::AttrValue), // [foo$=bar] operator: AttrSelectorOperator,
value: Impl::AttrValue,
AttrIncludesNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value or with whitespace case_sensitivity: CaseSensitivity,
AttrPrefixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value never_matches: bool,
AttrSubstringNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value },
AttrSuffixNeverMatch(AttrSelector<Impl>, Impl::AttrValue), // empty value // Use a Box in the less common cases with more data to keep size_of::<Component>() small.
AttributeOther(Box<AttrSelectorWithNamespace<Impl>>),
// Pseudo-classes // Pseudo-classes
// //
@ -610,35 +627,6 @@ pub struct LocalName<Impl: SelectorImpl> {
pub lower_name: Impl::LocalName, pub lower_name: Impl::LocalName,
} }
#[derive(Eq, PartialEq, Clone)]
pub struct AttrSelector<Impl: SelectorImpl> {
pub name: Impl::LocalName,
pub lower_name: Impl::LocalName,
pub namespace: NamespaceConstraint<Impl>,
}
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum NamespaceConstraint<Impl: SelectorImpl> {
Any,
Specific(Namespace<Impl>),
}
#[derive(Eq, PartialEq, Clone)]
pub struct Namespace<Impl: SelectorImpl> {
pub prefix: Option<Impl::NamespacePrefix>,
pub url: Impl::NamespaceUrl,
}
impl<Impl: SelectorImpl> Default for Namespace<Impl> {
fn default() -> Self {
Namespace {
prefix: None,
url: Impl::NamespaceUrl::default(), // empty string
}
}
}
impl<Impl: SelectorImpl> Debug for Selector<Impl> { impl<Impl: SelectorImpl> Debug for Selector<Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Selector(")?; f.write_str("Selector(")?;
@ -656,10 +644,7 @@ impl<Impl: SelectorImpl> Debug for ComplexSelector<Impl> {
impl<Impl: SelectorImpl> Debug for Component<Impl> { impl<Impl: SelectorImpl> Debug for Component<Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
} }
impl<Impl: SelectorImpl> Debug for AttrSelector<Impl> { impl<Impl: SelectorImpl> Debug for AttrSelectorWithNamespace<Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
}
impl<Impl: SelectorImpl> Debug for Namespace<Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) }
} }
impl<Impl: SelectorImpl> Debug for LocalName<Impl> { impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
@ -737,27 +722,25 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
dest.write_char('|') dest.write_char('|')
} }
// Attribute selectors AttributeInNoNamespaceExists { ref local_name, .. } => {
AttrExists(ref a) => {
dest.write_char('[')?; dest.write_char('[')?;
a.to_css(dest)?; display_to_css_identifier(local_name, dest)?;
dest.write_char(']') dest.write_char(']')
} }
AttrEqual(ref a, ref v, case) => { AttributeInNoNamespace { ref local_name, operator, ref value, case_sensitivity, .. } => {
attr_selector_to_css(a, " = ", v, match case { dest.write_char('[')?;
CaseSensitivity::CaseSensitive => None, display_to_css_identifier(local_name, dest)?;
CaseSensitivity::AsciiCaseInsensitive => Some(" i"), operator.to_css(dest)?;
}, dest) dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", value)?;
dest.write_char('"')?;
match case_sensitivity {
CaseSensitivity::CaseSensitive => {},
CaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
} }
AttrDashMatch(ref a, ref v) => attr_selector_to_css(a, " |= ", v, None, dest), dest.write_char(']')
AttrIncludesNeverMatch(ref a, ref v) | }
AttrIncludes(ref a, ref v) => attr_selector_to_css(a, " ~= ", v, None, dest), AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
AttrPrefixNeverMatch(ref a, ref v) |
AttrPrefixMatch(ref a, ref v) => attr_selector_to_css(a, " ^= ", v, None, dest),
AttrSubstringNeverMatch(ref a, ref v) |
AttrSubstringMatch(ref a, ref v) => attr_selector_to_css(a, " *= ", v, None, dest),
AttrSuffixNeverMatch(ref a, ref v) |
AttrSuffixMatch(ref a, ref v) => attr_selector_to_css(a, " $= ", v, None, dest),
// Pseudo-classes // Pseudo-classes
Negation(ref arg) => { Negation(ref arg) => {
@ -786,22 +769,33 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
} }
} }
impl<Impl: SelectorImpl> ToCss for AttrSelector<Impl> { impl<Impl: SelectorImpl> ToCss for AttrSelectorWithNamespace<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if let NamespaceConstraint::Specific(ref ns) = self.namespace { dest.write_char('[')?;
ns.to_css(dest)?; match self.namespace {
} NamespaceConstraint::Specific((ref prefix, _)) => {
display_to_css_identifier(&self.name, dest)
}
}
impl<Impl: SelectorImpl> ToCss for Namespace<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if let Some(ref prefix) = self.prefix {
display_to_css_identifier(prefix, dest)?; display_to_css_identifier(prefix, dest)?;
dest.write_char('|')?; dest.write_char('|')?
} }
Ok(()) NamespaceConstraint::Any => {
dest.write_str("*|")?
}
}
display_to_css_identifier(&self.local_name, dest)?;
match self.operation {
AttrSelectorOperation::Exists => {},
AttrSelectorOperation::WithValue { operator, case_sensitivity, ref expected_value } => {
operator.to_css(dest)?;
dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", expected_value)?;
dest.write_char('"')?;
match case_sensitivity {
CaseSensitivity::CaseSensitive => {},
CaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
}
},
}
dest.write_char(']')
} }
} }
@ -811,26 +805,6 @@ impl<Impl: SelectorImpl> ToCss for LocalName<Impl> {
} }
} }
fn attr_selector_to_css<Impl, W>(attr: &AttrSelector<Impl>,
operator: &str,
value: &Impl::AttrValue,
modifier: Option<&str>,
dest: &mut W)
-> fmt::Result
where Impl: SelectorImpl, W: fmt::Write
{
dest.write_char('[')?;
attr.to_css(dest)?;
dest.write_str(operator)?;
dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", value)?;
dest.write_char('"')?;
if let Some(m) = modifier {
dest.write_str(m)?;
}
dest.write_char(']')
}
/// Serialize the output of Display as a CSS identifier /// Serialize the output of Display as a CSS identifier
fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) -> fmt::Result { fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) -> fmt::Result {
// FIXME(SimonSapin): it is possible to avoid this heap allocation // FIXME(SimonSapin): it is possible to avoid this heap allocation
@ -926,18 +900,9 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
specificity.id_selectors += 1 specificity.id_selectors += 1
} }
Component::Class(..) | Component::Class(..) |
Component::AttrExists(..) | Component::AttributeInNoNamespace { .. } |
Component::AttrEqual(..) | Component::AttributeInNoNamespaceExists { .. } |
Component::AttrIncludes(..) | Component::AttributeOther(..) |
Component::AttrDashMatch(..) |
Component::AttrPrefixMatch(..) |
Component::AttrSubstringMatch(..) |
Component::AttrSuffixMatch(..) |
Component::AttrIncludesNeverMatch(..) |
Component::AttrPrefixNeverMatch(..) |
Component::AttrSubstringNeverMatch(..) |
Component::AttrSuffixNeverMatch(..) |
Component::FirstChild | Component::LastChild | Component::FirstChild | Component::LastChild |
Component::OnlyChild | Component::Root | Component::OnlyChild | Component::Root |
@ -1223,89 +1188,118 @@ fn parse_attribute_selector<P, Impl>(parser: &P, input: &mut CssParser)
-> Result<Component<Impl>, ()> -> Result<Component<Impl>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
let attr = match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? { let namespace;
let local_name;
let local_name_lower;
match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
None => return Err(()), None => return Err(()),
Some((_, None)) => unreachable!(), Some((_, None)) => unreachable!(),
Some((namespace, Some(local_name))) => AttrSelector { Some((ns, Some(ln))) => {
namespace: match namespace { local_name_lower = from_ascii_lowercase(&ln);
local_name = from_cow_str(ln);
namespace = match ns {
QNamePrefix::ImplicitNoNamespace | QNamePrefix::ImplicitNoNamespace |
QNamePrefix::ExplicitNoNamespace => { QNamePrefix::ExplicitNoNamespace => {
NamespaceConstraint::Specific(Namespace { None
prefix: None,
url: Impl::NamespaceUrl::default(),
})
} }
QNamePrefix::ExplicitNamespace(prefix, url) => { QNamePrefix::ExplicitNamespace(prefix, url) => {
NamespaceConstraint::Specific(Namespace { Some(NamespaceConstraint::Specific((prefix, url)))
prefix: Some(prefix),
url: url,
})
} }
QNamePrefix::ExplicitAnyNamespace => { QNamePrefix::ExplicitAnyNamespace => {
NamespaceConstraint::Any Some(NamespaceConstraint::Any)
} }
QNamePrefix::ImplicitAnyNamespace | QNamePrefix::ImplicitAnyNamespace |
QNamePrefix::ImplicitDefaultNamespace(_) => { QNamePrefix::ImplicitDefaultNamespace(_) => {
unreachable!() // Not returned with in_attr_selector = true unreachable!() // Not returned with in_attr_selector = true
} }
}, }
lower_name: from_ascii_lowercase(&local_name), }
name: from_cow_str(local_name), }
},
};
let operator;
let value;
let never_matches;
match input.next() { match input.next() {
// [foo] // [foo]
Err(()) => Ok(Component::AttrExists(attr)), Err(()) => {
if let Some(namespace) = namespace {
return Ok(Component::AttributeOther(Box::new(AttrSelectorWithNamespace {
namespace: namespace,
local_name: local_name,
local_name_lower: local_name_lower,
operation: AttrSelectorOperation::Exists,
never_matches: false,
})))
} else {
return Ok(Component::AttributeInNoNamespaceExists {
local_name: local_name,
local_name_lower: local_name_lower,
})
}
}
// [foo=bar] // [foo=bar]
Ok(Token::Delim('=')) => { Ok(Token::Delim('=')) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
let flags = parse_attribute_flags(input)?; never_matches = false;
Ok(Component::AttrEqual(attr, from_cow_str(value), flags)) operator = AttrSelectorOperator::Equal;
} }
// [foo~=bar] // [foo~=bar]
Ok(Token::IncludeMatch) => { Ok(Token::IncludeMatch) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
if value.is_empty() || value.contains(SELECTOR_WHITESPACE) { never_matches = value.is_empty() || value.contains(SELECTOR_WHITESPACE);
Ok(Component::AttrIncludesNeverMatch(attr, from_cow_str(value))) operator = AttrSelectorOperator::Includes;
} else {
Ok(Component::AttrIncludes(attr, from_cow_str(value)))
}
} }
// [foo|=bar] // [foo|=bar]
Ok(Token::DashMatch) => { Ok(Token::DashMatch) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
Ok(Component::AttrDashMatch(attr, from_cow_str(value))) never_matches = false;
operator = AttrSelectorOperator::DashMatch;
} }
// [foo^=bar] // [foo^=bar]
Ok(Token::PrefixMatch) => { Ok(Token::PrefixMatch) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
if value.is_empty() { never_matches = value.is_empty();
Ok(Component::AttrPrefixNeverMatch(attr, from_cow_str(value))) operator = AttrSelectorOperator::Prefix;
} else {
Ok(Component::AttrPrefixMatch(attr, from_cow_str(value)))
}
} }
// [foo*=bar] // [foo*=bar]
Ok(Token::SubstringMatch) => { Ok(Token::SubstringMatch) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
if value.is_empty() { never_matches = value.is_empty();
Ok(Component::AttrSubstringNeverMatch(attr, from_cow_str(value))) operator = AttrSelectorOperator::Substring;
} else {
Ok(Component::AttrSubstringMatch(attr, from_cow_str(value)))
}
} }
// [foo$=bar] // [foo$=bar]
Ok(Token::SuffixMatch) => { Ok(Token::SuffixMatch) => {
let value = input.expect_ident_or_string()?; value = input.expect_ident_or_string()?;
if value.is_empty() { never_matches = value.is_empty();
Ok(Component::AttrSuffixNeverMatch(attr, from_cow_str(value))) operator = AttrSelectorOperator::Suffix;
}
_ => return Err(())
}
let case_sensitivity = parse_attribute_flags(input)?;
let value = from_cow_str(value);
if let Some(namespace) = namespace {
Ok(Component::AttributeOther(Box::new(AttrSelectorWithNamespace {
namespace: namespace,
local_name: local_name,
local_name_lower: local_name_lower,
never_matches: never_matches,
operation: AttrSelectorOperation::WithValue {
operator: operator,
case_sensitivity: case_sensitivity,
expected_value: value,
}
})))
} else { } else {
Ok(Component::AttrSuffixMatch(attr, from_cow_str(value))) Ok(Component::AttributeInNoNamespace {
} local_name: local_name,
} local_name_lower: local_name_lower,
_ => Err(()) operator: operator,
value: value,
case_sensitivity: case_sensitivity,
never_matches: never_matches,
})
} }
} }
@ -1845,14 +1839,12 @@ pub mod tests {
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
let mut parser = DummyParser::default(); let mut parser = DummyParser::default();
assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("[Foo]", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec![
Component::AttrExists(AttrSelector { Component::AttributeInNoNamespaceExists {
name: DummyAtom::from("Foo"), local_name: DummyAtom::from("Foo"),
lower_name: DummyAtom::from("foo"), local_name_lower: DummyAtom::from("foo"),
namespace: NamespaceConstraint::Specific(Namespace { }
prefix: None, ]),
url: "".into(),
}) }))),
specificity_and_flags: specificity(0, 1, 0), specificity_and_flags: specificity(0, 1, 0),
})))); }))));
assert_eq!(parse_ns("svg|circle", &parser), Err(())); assert_eq!(parse_ns("svg|circle", &parser), Err(()));
@ -1885,14 +1877,10 @@ pub mod tests {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
vec![ vec![
Component::DefaultNamespace(MATHML.into()), Component::DefaultNamespace(MATHML.into()),
Component::AttrExists(AttrSelector { Component::AttributeInNoNamespaceExists {
name: DummyAtom::from("Foo"), local_name: DummyAtom::from("Foo"),
lower_name: DummyAtom::from("foo"), local_name_lower: DummyAtom::from("foo"),
namespace: NamespaceConstraint::Specific(Namespace { },
prefix: None,
url: "".into(),
}),
}),
]), ]),
specificity_and_flags: specificity(0, 1, 0), specificity_and_flags: specificity(0, 1, 0),
})))); }))));
@ -1960,14 +1948,14 @@ pub mod tests {
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector { assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
vec![ vec![
Component::AttrDashMatch(AttrSelector { Component::AttributeInNoNamespace {
name: DummyAtom::from("attr"), local_name: DummyAtom::from("attr"),
lower_name: DummyAtom::from("attr"), local_name_lower: DummyAtom::from("attr"),
namespace: NamespaceConstraint::Specific(Namespace { operator: AttrSelectorOperator::DashMatch,
prefix: None, value: DummyAtom::from("foo"),
url: "".into(), never_matches: false,
}), case_sensitivity: CaseSensitivity::CaseSensitive,
}, DummyAtom::from("foo")) }
]), ]),
specificity_and_flags: specificity(0, 1, 0), specificity_and_flags: specificity(0, 1, 0),
}]))); }])));

View file

@ -16,8 +16,7 @@ size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1);
size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40); size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40);
size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24); size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24);
size_of_test!(size_of_component, Component<Impl>, 64); size_of_test!(size_of_component, Component<Impl>, 32);
size_of_test!(size_of_attr_selector, AttrSelector<Impl>, 48);
size_of_test!(size_of_pseudo_class, PseudoClass, 24); size_of_test!(size_of_pseudo_class, PseudoClass, 24);
impl parser::PseudoElement for gecko_like_types::PseudoElement { impl parser::PseudoElement for gecko_like_types::PseudoElement {

View file

@ -5,9 +5,9 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style. //! style.
use attr::AttrSelectorOperation; use attr::{AttrSelectorOperation, NamespaceConstraint};
use matching::{ElementSelectorFlags, MatchingContext}; use matching::{ElementSelectorFlags, MatchingContext};
use parser::{NamespaceConstraint, SelectorImpl}; use parser::SelectorImpl;
pub trait Element: Sized { pub trait Element: Sized {
type Impl: SelectorImpl; type Impl: SelectorImpl;
@ -22,26 +22,29 @@ pub trait Element: Sized {
self.parent_element() self.parent_element()
} }
// Skips non-element nodes /// Skips non-element nodes
fn first_child_element(&self) -> Option<Self>; fn first_child_element(&self) -> Option<Self>;
// Skips non-element nodes /// Skips non-element nodes
fn last_child_element(&self) -> Option<Self>; fn last_child_element(&self) -> Option<Self>;
// Skips non-element nodes /// Skips non-element nodes
fn prev_sibling_element(&self) -> Option<Self>; fn prev_sibling_element(&self) -> Option<Self>;
// Skips non-element nodes /// Skips non-element nodes
fn next_sibling_element(&self) -> Option<Self>; fn next_sibling_element(&self) -> Option<Self>;
fn is_html_element_in_html_document(&self) -> bool; fn is_html_element_in_html_document(&self) -> bool;
fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName; fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName;
/// Empty string for no namespace
fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl; fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl;
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<Self::Impl>, ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
local_name: &<Self::Impl as SelectorImpl>::LocalName, local_name: &<Self::Impl as SelectorImpl>::LocalName,
operation: &AttrSelectorOperation<Self::Impl>) operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>)
-> bool; -> bool;
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
@ -56,6 +59,7 @@ pub trait Element: Sized {
-> bool; -> 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;
/// Returns whether this element matches `:empty`. /// Returns whether this element matches `:empty`.

View file

@ -6,8 +6,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use parser::{AttrSelector, Combinator, Component}; use attr::NamespaceConstraint;
use parser::{SelectorImpl, SelectorIter}; use parser::{Combinator, Component, SelectorImpl, SelectorIter};
/// A trait to visit selector properties. /// A trait to visit selector properties.
/// ///
@ -20,7 +20,12 @@ pub trait SelectorVisitor {
/// Visit an attribute selector that may match (there are other selectors /// Visit an attribute selector that may match (there are other selectors
/// that may never match, like those containing whitespace or the empty /// that may never match, like those containing whitespace or the empty
/// string). /// string).
fn visit_attribute_selector(&mut self, _: &AttrSelector<Self::Impl>) -> bool { fn visit_attribute_selector(
&mut self,
_namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
_local_name: &<Self::Impl as SelectorImpl>::LocalName,
_local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
) -> bool {
true true
} }

View file

@ -12,7 +12,6 @@ use cssparser::{self, Color, RGBA};
use euclid::num::Zero; use euclid::num::Zero;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use properties::PropertyDeclarationBlock; use properties::PropertyDeclarationBlock;
use selector_parser::SelectorImpl;
use selectors::attr::AttrSelectorOperation; use selectors::attr::AttrSelectorOperation;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use shared_lock::Locked; use shared_lock::Locked;
@ -352,9 +351,10 @@ impl AttrValue {
} }
} }
pub fn eval_selector(&self, selector: &AttrSelectorOperation<SelectorImpl>) -> bool { pub fn eval_selector(&self, selector: &AttrSelectorOperation<&String>) -> bool {
// FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants
// and doing Atom comparisons instead of string comparisons where possible. // and doing Atom comparisons instead of string comparisons where possible,
// with SelectorImpl::AttrValue changed to Atom.
selector.eval_str(self) selector.eval_str(self)
} }
} }

View file

@ -14,10 +14,8 @@ use gecko_bindings::structs::ServoElementSnapshot;
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags; use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
use gecko_bindings::structs::ServoElementSnapshotTable; use gecko_bindings::structs::ServoElementSnapshotTable;
use restyle_hints::ElementSnapshot; use restyle_hints::ElementSnapshot;
use selector_parser::SelectorImpl; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity}; use string_cache::{Atom, Namespace};
use selectors::parser::NamespaceConstraint;
use string_cache::Atom;
/// A snapshot of a Gecko element. /// A snapshot of a Gecko element.
pub type GeckoElementSnapshot = ServoElementSnapshot; pub type GeckoElementSnapshot = ServoElementSnapshot;
@ -59,9 +57,9 @@ impl GeckoElementSnapshot {
/// selectors::Element::attr_matches /// selectors::Element::attr_matches
pub fn attr_matches(&self, pub fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &Atom, local_name: &Atom,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&Atom>)
-> bool { -> bool {
unsafe { unsafe {
match *operation { match *operation {

View file

@ -64,9 +64,8 @@ use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel; 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}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::parser::NamespaceConstraint;
use shared_lock::Locked; use shared_lock::Locked;
use sink::Push; use sink::Push;
use std::cell::RefCell; use std::cell::RefCell;
@ -1140,9 +1139,9 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &Atom, local_name: &Atom,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&Atom>)
-> bool { -> bool {
unsafe { unsafe {
match *operation { match *operation {
@ -1413,11 +1412,11 @@ pub trait NamespaceConstraintHelpers {
fn atom_or_null(&self) -> *mut nsIAtom; fn atom_or_null(&self) -> *mut nsIAtom;
} }
impl NamespaceConstraintHelpers for NamespaceConstraint<SelectorImpl> { impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
fn atom_or_null(&self) -> *mut nsIAtom { fn atom_or_null(&self) -> *mut nsIAtom {
match *self { match *self {
NamespaceConstraint::Any => ptr::null_mut(), NamespaceConstraint::Any => ptr::null_mut(),
NamespaceConstraint::Specific(ref ns) => ns.url.0.as_ptr(), NamespaceConstraint::Specific(ref ns) => ns.0.as_ptr(),
} }
} }
} }

View file

@ -8,19 +8,19 @@
use Atom; use Atom;
use LocalName; use LocalName;
use Namespace;
use dom::TElement; use dom::TElement;
use element_state::*; use element_state::*;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use gecko_bindings::structs::nsRestyleHint; use gecko_bindings::structs::nsRestyleHint;
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap}; use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
use selectors::Element; use selectors::Element;
use selectors::attr::AttrSelectorOperation; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::matches_selector; use selectors::matching::matches_selector;
use selectors::parser::{Combinator, Component, Selector}; use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
use selectors::parser::{SelectorInner, SelectorMethods, NamespaceConstraint};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -371,9 +371,9 @@ impl<'a, E> Element for ElementWrapper<'a, E>
} }
fn attr_matches(&self, fn attr_matches(&self,
ns: &NamespaceConstraint<Self::Impl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName, local_name: &LocalName,
operation: &AttrSelectorOperation<Self::Impl>) operation: &AttrSelectorOperation<&AttrValue>)
-> bool { -> bool {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => { Some(snapshot) if snapshot.has_attrs() => {
@ -437,13 +437,9 @@ fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool {
match *sel { match *sel {
Component::ID(_) | Component::ID(_) |
Component::Class(_) | Component::Class(_) |
Component::AttrExists(_) | Component::AttributeInNoNamespaceExists { .. } |
Component::AttrEqual(_, _, _) | Component::AttributeInNoNamespace { .. } |
Component::AttrIncludes(_, _) | Component::AttributeOther(_) => true,
Component::AttrDashMatch(_, _) |
Component::AttrPrefixMatch(_, _) |
Component::AttrSubstringMatch(_, _) |
Component::AttrSuffixMatch(_, _) => true,
_ => false, _ => false,
} }
} }

View file

@ -14,10 +14,10 @@ use element_state::ElementState;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot; 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; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::matching::{MatchingContext, MatchingMode}; use selectors::matching::{MatchingContext, MatchingMode};
use selectors::parser::{SelectorMethods, NamespaceConstraint}; use selectors::parser::SelectorMethods;
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
@ -559,14 +559,13 @@ impl ElementSnapshot for ServoElementSnapshot {
impl ServoElementSnapshot { impl ServoElementSnapshot {
/// selectors::Element::attr_matches /// selectors::Element::attr_matches
pub fn attr_matches(&self, pub fn attr_matches(&self,
ns: &NamespaceConstraint<SelectorImpl>, ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName, local_name: &LocalName,
operation: &AttrSelectorOperation<SelectorImpl>) operation: &AttrSelectorOperation<&String>)
-> bool { -> bool {
use selectors::parser::NamespaceConstraint;
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => { NamespaceConstraint::Specific(ref ns) => {
self.get_attr(&ns.url, local_name) self.get_attr(ns, local_name)
.map_or(false, |value| value.eval_selector(operation)) .map_or(false, |value| value.eval_selector(operation))
} }
NamespaceConstraint::Any => { NamespaceConstraint::Any => {

View file

@ -6,7 +6,7 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use {Atom, LocalName}; use {Atom, LocalName, Namespace};
use bit_vec::BitVec; use bit_vec::BitVec;
use context::QuirksMode; use context::QuirksMode;
use data::ComputedStyle; use data::ComputedStyle;
@ -26,10 +26,11 @@ use properties::PropertyDeclarationBlock;
use restyle_hints::{RestyleHint, DependencySet}; use restyle_hints::{RestyleHint, DependencySet};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap}; use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
use selectors::attr::NamespaceConstraint;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode}; use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter}; use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorIter};
use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector}; use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
@ -502,7 +503,7 @@ impl Stylist {
/// Returns whether the given attribute might appear in an attribute /// Returns whether the given attribute might appear in an attribute
/// selector of some rule in the stylist. /// selector of some rule in the stylist.
pub fn might_have_attribute_dependency(&self, pub fn might_have_attribute_dependency(&self,
local_name: &<SelectorImpl as ::selectors::SelectorImpl>::LocalName) local_name: &LocalName)
-> bool { -> bool {
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
let style_lower_name = local_name!("style"); let style_lower_name = local_name!("style");
@ -1088,17 +1089,19 @@ struct AttributeAndStateDependencyVisitor<'a>(&'a mut Stylist);
impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> { impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
type Impl = SelectorImpl; type Impl = SelectorImpl;
fn visit_attribute_selector(&mut self, selector: &AttrSelector<Self::Impl>) -> bool { fn visit_attribute_selector(&mut self, _ns: &NamespaceConstraint<&Namespace>,
name: &LocalName, lower_name: &LocalName)
-> bool {
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
let style_lower_name = local_name!("style"); let style_lower_name = local_name!("style");
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
let style_lower_name = atom!("style"); let style_lower_name = atom!("style");
if selector.lower_name == style_lower_name { if *lower_name == style_lower_name {
self.0.style_attribute_dependency = true; self.0.style_attribute_dependency = true;
} else { } else {
self.0.attribute_dependencies.insert(&selector.name); self.0.attribute_dependencies.insert(&name);
self.0.attribute_dependencies.insert(&selector.lower_name); self.0.attribute_dependencies.insert(&lower_name);
} }
true true
} }
@ -1157,13 +1160,9 @@ impl SelectorVisitor for RevalidationVisitor {
/// concerned. /// concerned.
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool { fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
match *s { match *s {
Component::AttrExists(_) | Component::AttributeInNoNamespaceExists { .. } |
Component::AttrEqual(_, _, _) | Component::AttributeInNoNamespace { .. } |
Component::AttrIncludes(_, _) | Component::AttributeOther(_) |
Component::AttrDashMatch(_, _) |
Component::AttrPrefixMatch(_, _) |
Component::AttrSubstringMatch(_, _) |
Component::AttrSuffixMatch(_, _) |
Component::Empty | Component::Empty |
Component::FirstChild | Component::FirstChild |
Component::LastChild | Component::LastChild |

View file

@ -6,7 +6,7 @@ use cssparser::{self, Parser as CssParser, SourcePosition, SourceLocation};
use html5ever::{Namespace as NsAtom}; use html5ever::{Namespace as NsAtom};
use media_queries::CSSErrorReporterTest; use media_queries::CSSErrorReporterTest;
use parking_lot::RwLock; use parking_lot::RwLock;
use selectors::attr::CaseSensitivity; use selectors::attr::*;
use selectors::parser::*; use selectors::parser::*;
use servo_atoms::Atom; use servo_atoms::Atom;
use servo_url::ServoUrl; use servo_url::ServoUrl;
@ -98,14 +98,14 @@ fn test_parse_stylesheet() {
name: local_name!("input"), name: local_name!("input"),
lower_name: local_name!("input"), lower_name: local_name!("input"),
}), }),
Component::AttrEqual(AttrSelector { Component::AttributeInNoNamespace {
name: local_name!("type"), local_name: local_name!("type"),
lower_name: local_name!("type"), local_name_lower: local_name!("type"),
namespace: NamespaceConstraint::Specific(Namespace { operator: AttrSelectorOperator::Equal,
prefix: None, value: "hidden".to_owned(),
url: ns!() case_sensitivity: CaseSensitivity::AsciiCaseInsensitive,
}), never_matches: false,
}, "hidden".to_owned(), CaseSensitivity::AsciiCaseInsensitive) }
]), ]),
(0 << 20) + (1 << 10) + (1 << 0) (0 << 20) + (1 << 10) + (1 << 0)
), ),