mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #16915 - servo:attr-selectors, r=nox,emilio
Shrink selectors::Component, implement attr selector (in)case-sensitivity * https://bugzilla.mozilla.org/show_bug.cgi?id=1364148 * https://bugzilla.mozilla.org/show_bug.cgi?id=1364162 * https://bugzilla.mozilla.org/show_bug.cgi?id=1363531 * Fixes #3322 <!-- 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/16915) <!-- Reviewable:end -->
This commit is contained in:
commit
640b16634f
30 changed files with 963 additions and 841 deletions
|
@ -12,6 +12,7 @@ use cssparser::{self, Color, RGBA};
|
|||
use euclid::num::Zero;
|
||||
use num_traits::ToPrimitive;
|
||||
use properties::PropertyDeclarationBlock;
|
||||
use selectors::attr::AttrSelectorOperation;
|
||||
use servo_url::ServoUrl;
|
||||
use shared_lock::Locked;
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -349,6 +350,13 @@ impl AttrValue {
|
|||
panic!("Uint not found");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_selector(&self, selector: &AttrSelectorOperation<&String>) -> bool {
|
||||
// FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants
|
||||
// and doing Atom comparisons instead of string comparisons where possible,
|
||||
// with SelectorImpl::AttrValue changed to Atom.
|
||||
selector.eval_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for AttrValue {
|
||||
|
@ -371,6 +379,15 @@ impl ::std::ops::Deref for AttrValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Atom> for AttrValue {
|
||||
fn eq(&self, other: &Atom) -> bool {
|
||||
match *self {
|
||||
AttrValue::Atom(ref value) => value == other,
|
||||
_ => other == &**self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-zero-dimension-values
|
||||
pub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto {
|
||||
match parse_length(value) {
|
||||
|
|
|
@ -381,6 +381,9 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
|||
/// Whether an attribute value equals `value`.
|
||||
fn attr_equals(&self, namespace: &Namespace, attr: &LocalName, value: &Atom) -> bool;
|
||||
|
||||
/// Internal iterator for the classes of this element.
|
||||
fn each_class<F>(&self, callback: F) where F: FnMut(&Atom);
|
||||
|
||||
/// Get the pre-existing style to calculate restyle damage (change hints).
|
||||
///
|
||||
/// This needs to be generic since it varies between Servo and Gecko.
|
||||
|
|
|
@ -8,15 +8,14 @@
|
|||
use dom::TElement;
|
||||
use element_state::ElementState;
|
||||
use gecko::snapshot_helpers;
|
||||
use gecko::wrapper::{AttrSelectorHelpers, GeckoElement};
|
||||
use gecko::wrapper::{NamespaceConstraintHelpers, GeckoElement};
|
||||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
|
||||
use gecko_bindings::structs::ServoElementSnapshotTable;
|
||||
use restyle_hints::ElementSnapshot;
|
||||
use selector_parser::SelectorImpl;
|
||||
use selectors::parser::AttrSelector;
|
||||
use string_cache::Atom;
|
||||
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
|
||||
use string_cache::{Atom, Namespace};
|
||||
|
||||
/// A snapshot of a Gecko element.
|
||||
pub type GeckoElementSnapshot = ServoElementSnapshot;
|
||||
|
@ -47,11 +46,6 @@ impl SnapshotMap {
|
|||
}
|
||||
|
||||
impl GeckoElementSnapshot {
|
||||
#[inline]
|
||||
fn is_html_element_in_html_document(&self) -> bool {
|
||||
self.mIsHTMLElementInHTMLDocument
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_any(&self, flags: Flags) -> bool {
|
||||
(self.mContains as u8 & flags as u8) != 0
|
||||
|
@ -60,76 +54,67 @@ impl GeckoElementSnapshot {
|
|||
fn as_ptr(&self) -> *const Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool {
|
||||
/// selectors::Element::attr_matches
|
||||
pub fn attr_matches(&self,
|
||||
ns: &NamespaceConstraint<&Namespace>,
|
||||
local_name: &Atom,
|
||||
operation: &AttrSelectorOperation<&Atom>)
|
||||
-> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotHasAttr(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()))
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ true)
|
||||
}
|
||||
}
|
||||
fn match_attr_includes(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrIncludes(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_dash(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrDashEquals(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_prefix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasPrefix(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_substring(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSubstring(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_suffix(&self, attr: &AttrSelector<SelectorImpl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSuffix(self,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
match *operation {
|
||||
AttrSelectorOperation::Exists => {
|
||||
bindings:: Gecko_SnapshotHasAttr(self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr())
|
||||
}
|
||||
AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => {
|
||||
let ignore_case = match case_sensitivity {
|
||||
CaseSensitivity::CaseSensitive => false,
|
||||
CaseSensitivity::AsciiCaseInsensitive => true,
|
||||
};
|
||||
// FIXME: case sensitivity for operators other than Equal
|
||||
match operator {
|
||||
AttrSelectorOperator::Equal => bindings::Gecko_SnapshotAttrEquals(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
ignore_case
|
||||
),
|
||||
AttrSelectorOperator::Includes => bindings::Gecko_SnapshotAttrIncludes(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::DashMatch => bindings::Gecko_SnapshotAttrDashEquals(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Prefix => bindings::Gecko_SnapshotAttrHasPrefix(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Suffix => bindings::Gecko_SnapshotAttrHasSuffix(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Substring => bindings::Gecko_SnapshotAttrHasSubstring(
|
||||
self,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,8 @@ use properties::style_structs::Font;
|
|||
use rule_tree::CascadeLevel as ServoCascadeLevel;
|
||||
use selector_parser::ElementExt;
|
||||
use selectors::Element;
|
||||
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AttrSelector, NamespaceConstraint};
|
||||
use shared_lock::Locked;
|
||||
use sink::Push;
|
||||
use std::cell::RefCell;
|
||||
|
@ -655,6 +655,14 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
}
|
||||
}
|
||||
|
||||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
snapshot_helpers::each_class(self.0,
|
||||
callback,
|
||||
Gecko_ClassOrClassList)
|
||||
}
|
||||
|
||||
fn existing_style_for_restyle_damage<'a>(&'a self,
|
||||
_existing_values: &'a ComputedValues,
|
||||
pseudo: Option<&PseudoElement>)
|
||||
|
@ -1082,6 +1090,8 @@ impl<'le> PresentationalHintsSynthesizer for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn parent_element(&self) -> Option<Self> {
|
||||
let parent_node = self.as_node().parent_node();
|
||||
parent_node.and_then(|n| n.as_element())
|
||||
|
@ -1136,6 +1146,68 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
None
|
||||
}
|
||||
|
||||
fn attr_matches(&self,
|
||||
ns: &NamespaceConstraint<&Namespace>,
|
||||
local_name: &Atom,
|
||||
operation: &AttrSelectorOperation<&Atom>)
|
||||
-> bool {
|
||||
unsafe {
|
||||
match *operation {
|
||||
AttrSelectorOperation::Exists => {
|
||||
bindings::Gecko_HasAttr(self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr())
|
||||
}
|
||||
AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => {
|
||||
let ignore_case = match case_sensitivity {
|
||||
CaseSensitivity::CaseSensitive => false,
|
||||
CaseSensitivity::AsciiCaseInsensitive => true,
|
||||
};
|
||||
// FIXME: case sensitivity for operators other than Equal
|
||||
match operator {
|
||||
AttrSelectorOperator::Equal => bindings::Gecko_AttrEquals(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
ignore_case
|
||||
),
|
||||
AttrSelectorOperator::Includes => bindings::Gecko_AttrIncludes(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::DashMatch => bindings::Gecko_AttrDashEquals(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Prefix => bindings::Gecko_AttrHasPrefix(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Suffix => bindings::Gecko_AttrHasSuffix(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
AttrSelectorOperator::Substring => bindings::Gecko_AttrHasSubstring(
|
||||
self.0,
|
||||
ns.atom_or_null(),
|
||||
local_name.as_ptr(),
|
||||
expected_value.as_ptr(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_root(&self) -> bool {
|
||||
unsafe {
|
||||
Gecko_IsRootElement(self.0)
|
||||
|
@ -1322,18 +1394,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
Gecko_ClassOrClassList)
|
||||
}
|
||||
|
||||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
if !self.may_have_class() {
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot_helpers::each_class(self.0,
|
||||
callback,
|
||||
Gecko_ClassOrClassList)
|
||||
}
|
||||
|
||||
fn is_html_element_in_html_document(&self) -> bool {
|
||||
let node = self.as_node();
|
||||
let node_info = node.node_info();
|
||||
|
@ -1343,97 +1403,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
/// A few helpers to help with attribute selectors and snapshotting.
|
||||
pub trait AttrSelectorHelpers {
|
||||
pub trait NamespaceConstraintHelpers {
|
||||
/// Returns the namespace of the selector, or null otherwise.
|
||||
fn ns_or_null(&self) -> *mut nsIAtom;
|
||||
/// Returns the proper selector name depending on whether the requesting
|
||||
/// element is an HTML element in an HTML document or not.
|
||||
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom;
|
||||
fn atom_or_null(&self) -> *mut nsIAtom;
|
||||
}
|
||||
|
||||
impl AttrSelectorHelpers for AttrSelector<SelectorImpl> {
|
||||
fn ns_or_null(&self) -> *mut nsIAtom {
|
||||
match self.namespace {
|
||||
impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
|
||||
fn atom_or_null(&self) -> *mut nsIAtom {
|
||||
match *self {
|
||||
NamespaceConstraint::Any => ptr::null_mut(),
|
||||
NamespaceConstraint::Specific(ref ns) => ns.url.0.as_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom {
|
||||
if is_html_element_in_html_document {
|
||||
self.lower_name.as_ptr()
|
||||
} else {
|
||||
self.name.as_ptr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn match_attr_has(&self, attr: &AttrSelector<Self::Impl>) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_HasAttr(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()))
|
||||
}
|
||||
}
|
||||
fn match_attr_equals(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
}
|
||||
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
}
|
||||
fn match_attr_includes(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrIncludes(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_dash(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrDashEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_prefix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrHasPrefix(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_substring(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrHasSubstring(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_suffix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrHasSuffix(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
NamespaceConstraint::Specific(ref ns) => ns.0.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,18 +7,20 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
use Atom;
|
||||
use LocalName;
|
||||
use Namespace;
|
||||
use dom::TElement;
|
||||
use element_state::*;
|
||||
#[cfg(feature = "gecko")]
|
||||
use gecko_bindings::structs::nsRestyleHint;
|
||||
#[cfg(feature = "servo")]
|
||||
use heapsize::HeapSizeOf;
|
||||
use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
|
||||
use selectors::{Element, MatchAttr};
|
||||
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::matches_selector;
|
||||
use selectors::parser::{AttrSelector, Combinator, Component, Selector};
|
||||
use selectors::parser::{SelectorInner, SelectorMethods};
|
||||
use selectors::parser::{Combinator, Component, Selector, SelectorInner, SelectorMethods};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Borrow;
|
||||
|
@ -170,7 +172,7 @@ impl HeapSizeOf for RestyleHint {
|
|||
/// still need to take the ElementWrapper approach for attribute-dependent
|
||||
/// style. So we do it the same both ways for now to reduce complexity, but it's
|
||||
/// worth measuring the performance impact (if any) of the mStateMask approach.
|
||||
pub trait ElementSnapshot : Sized + MatchAttr<Impl=SelectorImpl> {
|
||||
pub trait ElementSnapshot : Sized {
|
||||
/// The state of the snapshot, if any.
|
||||
fn state(&self) -> Option<ElementState>;
|
||||
|
||||
|
@ -242,90 +244,6 @@ impl<'a, E> ElementWrapper<'a, E>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, E> MatchAttr for ElementWrapper<'a, E>
|
||||
where E: TElement,
|
||||
{
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn match_attr_has(&self, attr: &AttrSelector<SelectorImpl>) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_has(attr),
|
||||
_ => self.element.match_attr_has(attr)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_equals(attr, value),
|
||||
_ => self.element.match_attr_equals(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals_ignore_ascii_case(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_equals_ignore_ascii_case(attr, value),
|
||||
_ => self.element.match_attr_equals_ignore_ascii_case(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_includes(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_includes(attr, value),
|
||||
_ => self.element.match_attr_includes(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_dash(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_dash(attr, value),
|
||||
_ => self.element.match_attr_dash(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_prefix(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_prefix(attr, value),
|
||||
_ => self.element.match_attr_prefix(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_substring(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_substring(attr, value),
|
||||
_ => self.element.match_attr_substring(attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_suffix(&self,
|
||||
attr: &AttrSelector<SelectorImpl>,
|
||||
value: &AttrValue) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.match_attr_suffix(attr, value),
|
||||
_ => self.element.match_attr_suffix(attr, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn dir_selector_to_state(s: &[u16]) -> ElementState {
|
||||
// Jump through some hoops to deal with our Box<[u16]> thing.
|
||||
|
@ -345,6 +263,8 @@ fn dir_selector_to_state(s: &[u16]) -> ElementState {
|
|||
impl<'a, E> Element for ElementWrapper<'a, E>
|
||||
where E: TElement,
|
||||
{
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn match_non_ts_pseudo_class<F>(&self,
|
||||
pseudo_class: &NonTSPseudoClass,
|
||||
context: &mut MatchingContext,
|
||||
|
@ -450,6 +370,19 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
self.element.get_namespace()
|
||||
}
|
||||
|
||||
fn attr_matches(&self,
|
||||
ns: &NamespaceConstraint<&Namespace>,
|
||||
local_name: &LocalName,
|
||||
operation: &AttrSelectorOperation<&AttrValue>)
|
||||
-> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => {
|
||||
snapshot.attr_matches(ns, local_name, operation)
|
||||
}
|
||||
_ => self.element.attr_matches(ns, local_name, operation)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
|
@ -474,15 +407,6 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
self.element.is_root()
|
||||
}
|
||||
|
||||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom) {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs()
|
||||
=> snapshot.each_class(callback),
|
||||
_ => self.element.each_class(callback)
|
||||
}
|
||||
}
|
||||
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
self.element.closest_non_native_anonymous_ancestor()
|
||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
||||
|
@ -504,13 +428,9 @@ fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool {
|
|||
match *sel {
|
||||
Component::ID(_) |
|
||||
Component::Class(_) |
|
||||
Component::AttrExists(_) |
|
||||
Component::AttrEqual(_, _, _) |
|
||||
Component::AttrIncludes(_, _) |
|
||||
Component::AttrDashMatch(_, _) |
|
||||
Component::AttrPrefixMatch(_, _) |
|
||||
Component::AttrSubstringMatch(_, _) |
|
||||
Component::AttrSuffixMatch(_, _) => true,
|
||||
Component::AttributeInNoNamespaceExists { .. } |
|
||||
Component::AttributeInNoNamespace { .. } |
|
||||
Component::AttributeOther(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@ use element_state::ElementState;
|
|||
use fnv::FnvHashMap;
|
||||
use restyle_hints::ElementSnapshot;
|
||||
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
|
||||
use selectors::{Element, MatchAttrGeneric};
|
||||
use selectors::Element;
|
||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
||||
use selectors::matching::{MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AttrSelector, SelectorMethods};
|
||||
use selectors::parser::SelectorMethods;
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
@ -174,6 +175,7 @@ pub enum NonTSPseudoClass {
|
|||
ReadWrite,
|
||||
ReadOnly,
|
||||
ServoNonZeroBorder,
|
||||
ServoCaseSensitiveTypeAttr(Atom),
|
||||
Target,
|
||||
Visited,
|
||||
}
|
||||
|
@ -181,10 +183,18 @@ pub enum NonTSPseudoClass {
|
|||
impl ToCss for NonTSPseudoClass {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
use self::NonTSPseudoClass::*;
|
||||
if let Lang(ref lang) = *self {
|
||||
dest.write_str(":lang(")?;
|
||||
serialize_identifier(lang, dest)?;
|
||||
return dest.write_str(")");
|
||||
match *self {
|
||||
Lang(ref lang) => {
|
||||
dest.write_str(":lang(")?;
|
||||
serialize_identifier(lang, dest)?;
|
||||
return dest.write_str(")")
|
||||
}
|
||||
ServoCaseSensitiveTypeAttr(ref value) => {
|
||||
dest.write_str(":-servo-case-sensitive-type-attr(")?;
|
||||
serialize_identifier(value, dest)?;
|
||||
return dest.write_str(")")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
dest.write_str(match *self {
|
||||
|
@ -197,7 +207,6 @@ impl ToCss for NonTSPseudoClass {
|
|||
Fullscreen => ":fullscreen",
|
||||
Hover => ":hover",
|
||||
Indeterminate => ":indeterminate",
|
||||
Lang(_) => unreachable!(),
|
||||
Link => ":link",
|
||||
PlaceholderShown => ":placeholder-shown",
|
||||
ReadWrite => ":read-write",
|
||||
|
@ -205,6 +214,8 @@ impl ToCss for NonTSPseudoClass {
|
|||
ServoNonZeroBorder => ":-servo-nonzero-border",
|
||||
Target => ":target",
|
||||
Visited => ":visited",
|
||||
Lang(_) |
|
||||
ServoCaseSensitiveTypeAttr(_) => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +254,8 @@ impl NonTSPseudoClass {
|
|||
Lang(_) |
|
||||
Link |
|
||||
Visited |
|
||||
ServoNonZeroBorder => ElementState::empty(),
|
||||
ServoNonZeroBorder |
|
||||
ServoCaseSensitiveTypeAttr(_) => ElementState::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +324,15 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
|||
-> Result<NonTSPseudoClass, ()> {
|
||||
use self::NonTSPseudoClass::*;
|
||||
let pseudo_class = match_ignore_ascii_case!{ &name,
|
||||
"lang" => Lang(String::from(try!(parser.expect_ident_or_string())).into_boxed_str()),
|
||||
"lang" => {
|
||||
Lang(parser.expect_ident_or_string()?.into_owned().into_boxed_str())
|
||||
}
|
||||
"-servo-case-sensitive-type-attr" => {
|
||||
if !self.in_user_agent_stylesheet() {
|
||||
return Err(());
|
||||
}
|
||||
ServoCaseSensitiveTypeAttr(Atom::from(parser.expect_ident()?))
|
||||
}
|
||||
_ => return Err(())
|
||||
};
|
||||
|
||||
|
@ -519,10 +539,10 @@ impl ServoElementSnapshot {
|
|||
.map(|&(_, ref v)| v)
|
||||
}
|
||||
|
||||
fn get_attr_ignore_ns(&self, name: &LocalName) -> Option<&AttrValue> {
|
||||
fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
|
||||
where F: FnMut(&AttrValue) -> bool {
|
||||
self.attrs.as_ref().unwrap().iter()
|
||||
.find(|&&(ref ident, _)| ident.local_name == *name)
|
||||
.map(|&(_, ref v)| v)
|
||||
.any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,19 +575,22 @@ impl ElementSnapshot for ServoElementSnapshot {
|
|||
}
|
||||
}
|
||||
|
||||
impl MatchAttrGeneric for ServoElementSnapshot {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn match_attr<F>(&self, attr: &AttrSelector<SelectorImpl>, test: F) -> bool
|
||||
where F: Fn(&str) -> bool
|
||||
{
|
||||
use selectors::parser::NamespaceConstraint;
|
||||
let html = self.is_html_element_in_html_document;
|
||||
let local_name = if html { &attr.lower_name } else { &attr.name };
|
||||
match attr.namespace {
|
||||
NamespaceConstraint::Specific(ref ns) => self.get_attr(&ns.url, local_name),
|
||||
NamespaceConstraint::Any => self.get_attr_ignore_ns(local_name),
|
||||
}.map_or(false, |v| test(v))
|
||||
impl ServoElementSnapshot {
|
||||
/// selectors::Element::attr_matches
|
||||
pub fn attr_matches(&self,
|
||||
ns: &NamespaceConstraint<&Namespace>,
|
||||
local_name: &LocalName,
|
||||
operation: &AttrSelectorOperation<&String>)
|
||||
-> bool {
|
||||
match *ns {
|
||||
NamespaceConstraint::Specific(ref ns) => {
|
||||
self.get_attr(ns, local_name)
|
||||
.map_or(false, |value| value.eval_selector(operation))
|
||||
}
|
||||
NamespaceConstraint::Any => {
|
||||
self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use {Atom, LocalName};
|
||||
use {Atom, LocalName, Namespace};
|
||||
use bit_vec::BitVec;
|
||||
use context::QuirksMode;
|
||||
use data::ComputedStyle;
|
||||
|
@ -26,10 +26,11 @@ use properties::PropertyDeclarationBlock;
|
|||
use restyle_hints::{RestyleHint, DependencySet};
|
||||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
||||
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
|
||||
use selectors::attr::NamespaceConstraint;
|
||||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
||||
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::visitor::SelectorVisitor;
|
||||
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
||||
|
@ -502,7 +503,7 @@ impl Stylist {
|
|||
/// Returns whether the given attribute might appear in an attribute
|
||||
/// selector of some rule in the stylist.
|
||||
pub fn might_have_attribute_dependency(&self,
|
||||
local_name: &<SelectorImpl as ::selectors::SelectorImpl>::LocalName)
|
||||
local_name: &LocalName)
|
||||
-> bool {
|
||||
#[cfg(feature = "servo")]
|
||||
let style_lower_name = local_name!("style");
|
||||
|
@ -1088,17 +1089,19 @@ struct AttributeAndStateDependencyVisitor<'a>(&'a mut Stylist);
|
|||
impl<'a> SelectorVisitor for AttributeAndStateDependencyVisitor<'a> {
|
||||
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")]
|
||||
let style_lower_name = local_name!("style");
|
||||
#[cfg(feature = "gecko")]
|
||||
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;
|
||||
} else {
|
||||
self.0.attribute_dependencies.insert(&selector.name);
|
||||
self.0.attribute_dependencies.insert(&selector.lower_name);
|
||||
self.0.attribute_dependencies.insert(&name);
|
||||
self.0.attribute_dependencies.insert(&lower_name);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -1157,13 +1160,9 @@ impl SelectorVisitor for RevalidationVisitor {
|
|||
/// concerned.
|
||||
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
|
||||
match *s {
|
||||
Component::AttrExists(_) |
|
||||
Component::AttrEqual(_, _, _) |
|
||||
Component::AttrIncludes(_, _) |
|
||||
Component::AttrDashMatch(_, _) |
|
||||
Component::AttrPrefixMatch(_, _) |
|
||||
Component::AttrSubstringMatch(_, _) |
|
||||
Component::AttrSuffixMatch(_, _) |
|
||||
Component::AttributeInNoNamespaceExists { .. } |
|
||||
Component::AttributeInNoNamespace { .. } |
|
||||
Component::AttributeOther(_) |
|
||||
Component::Empty |
|
||||
Component::FirstChild |
|
||||
Component::LastChild |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue