mirror of
https://github.com/servo/servo.git
synced 2025-07-25 08:10:21 +01:00
ID and class selectors are ASCII case-insensitive in quirks mode.
https://bugzilla.mozilla.org/show_bug.cgi?id=1363778
This commit is contained in:
parent
524fcac191
commit
5bccf98aa4
22 changed files with 313 additions and 191 deletions
|
@ -68,3 +68,5 @@ fullscreenchange
|
||||||
fullscreenerror
|
fullscreenerror
|
||||||
gattserverdisconnected
|
gattserverdisconnected
|
||||||
onchange
|
onchange
|
||||||
|
|
||||||
|
reftest-wait
|
||||||
|
|
|
@ -85,7 +85,7 @@ 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, NamespaceConstraint};
|
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
|
||||||
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::matching::{RelevantLinkStatus, matches_selector_list};
|
use selectors::matching::{RelevantLinkStatus, matches_selector_list};
|
||||||
|
@ -97,6 +97,7 @@ use std::convert::TryFrom;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use style::CaseSensitivityExt;
|
||||||
use style::applicable_declarations::ApplicableDeclarationBlock;
|
use style::applicable_declarations::ApplicableDeclarationBlock;
|
||||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
||||||
use style::context::{QuirksMode, ReflowGoal};
|
use style::context::{QuirksMode, ReflowGoal};
|
||||||
|
@ -345,7 +346,9 @@ impl RawLayoutElementHelpers for Element {
|
||||||
|
|
||||||
pub trait LayoutElementHelpers {
|
pub trait LayoutElementHelpers {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn has_class_for_layout(&self, name: &Atom) -> bool;
|
unsafe fn in_quirks_mode_document_for_layout(&self) -> bool;
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
|
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
|
||||||
|
|
||||||
|
@ -373,9 +376,15 @@ pub trait LayoutElementHelpers {
|
||||||
impl LayoutElementHelpers for LayoutJS<Element> {
|
impl LayoutElementHelpers for LayoutJS<Element> {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn has_class_for_layout(&self, name: &Atom) -> bool {
|
unsafe fn in_quirks_mode_document_for_layout(&self) -> bool {
|
||||||
|
self.upcast::<Node>().owner_doc_for_layout().quirks_mode() == QuirksMode::Quirks
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")).map_or(false, |attr| {
|
get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")).map_or(false, |attr| {
|
||||||
attr.value_tokens_forever().unwrap().iter().any(|atom| atom == name)
|
attr.value_tokens_forever().unwrap().iter().any(|atom| case_sensitivity.eq_atom(atom, name))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,16 +1167,10 @@ impl Element {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_class(&self, name: &Atom) -> bool {
|
pub fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
let quirks_mode = document_from_node(self).quirks_mode();
|
self.get_attribute(&ns!(), &local_name!("class")).map_or(false, |attr| {
|
||||||
let is_equal = |lhs: &Atom, rhs: &Atom| {
|
attr.value().as_tokens().iter().any(|atom| case_sensitivity.eq_atom(name, atom))
|
||||||
match quirks_mode {
|
})
|
||||||
QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => lhs == rhs,
|
|
||||||
QuirksMode::Quirks => lhs.eq_ignore_ascii_case(&rhs),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.get_attribute(&ns!(), &local_name!("class"))
|
|
||||||
.map_or(false, |attr| attr.value().as_tokens().iter().any(|atom| is_equal(name, atom)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_atomic_attribute(&self, local_name: &LocalName, value: DOMString) {
|
pub fn set_atomic_attribute(&self, local_name: &LocalName, value: DOMString) {
|
||||||
|
@ -2503,12 +2506,16 @@ impl<'a> ::selectors::Element for Root<Element> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
self.id_attribute.borrow().clone()
|
self.id_attribute.borrow().as_ref().map_or(false, |atom| case_sensitivity.eq_atom(id, atom))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
Element::has_class(&**self, name)
|
Element::has_class(&**self, name, case_sensitivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_quirks_mode_document(&self) -> bool {
|
||||||
|
document_from_node(&**self).quirks_mode() == QuirksMode::Quirks
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_html_element_in_html_document(&self) -> bool {
|
fn is_html_element_in_html_document(&self) -> bool {
|
||||||
|
|
|
@ -11,12 +11,14 @@ use dom::bindings::str::DOMString;
|
||||||
use dom::bindings::trace::JSTraceable;
|
use dom::bindings::trace::JSTraceable;
|
||||||
use dom::bindings::xmlname::namespace_from_domstring;
|
use dom::bindings::xmlname::namespace_from_domstring;
|
||||||
use dom::element::Element;
|
use dom::element::Element;
|
||||||
use dom::node::Node;
|
use dom::node::{Node, document_from_node};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use html5ever::{LocalName, QualName};
|
use html5ever::{LocalName, QualName};
|
||||||
|
use selectors::attr::CaseSensitivity;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use style::context::QuirksMode;
|
||||||
use style::str::split_html_space_chars;
|
use style::str::split_html_space_chars;
|
||||||
|
|
||||||
pub trait CollectionFilter : JSTraceable {
|
pub trait CollectionFilter : JSTraceable {
|
||||||
|
@ -199,7 +201,12 @@ impl HTMLCollection {
|
||||||
}
|
}
|
||||||
impl CollectionFilter for ClassNameFilter {
|
impl CollectionFilter for ClassNameFilter {
|
||||||
fn filter(&self, elem: &Element, _root: &Node) -> bool {
|
fn filter(&self, elem: &Element, _root: &Node) -> bool {
|
||||||
self.classes.iter().all(|class| elem.has_class(class))
|
let case_sensitivity = match document_from_node(elem).quirks_mode() {
|
||||||
|
QuirksMode::NoQuirks |
|
||||||
|
QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive,
|
||||||
|
QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
|
||||||
|
};
|
||||||
|
self.classes.iter().all(|class| elem.has_class(class, case_sensitivity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let filter = ClassNameFilter {
|
let filter = ClassNameFilter {
|
||||||
|
|
|
@ -84,7 +84,7 @@ use script_traits::{ConstellationControlMsg, DocumentState, LoadData, MozBrowser
|
||||||
use script_traits::{ScriptMsg as ConstellationMsg, ScrollState, TimerEvent, TimerEventId};
|
use script_traits::{ScriptMsg as ConstellationMsg, ScrollState, TimerEvent, TimerEventId};
|
||||||
use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||||
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
|
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
|
||||||
use servo_atoms::Atom;
|
use selectors::attr::CaseSensitivity;
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
use servo_config::prefs::PREFS;
|
use servo_config::prefs::PREFS;
|
||||||
use servo_geometry::{f32_rect_to_au_rect, max_rect};
|
use servo_geometry::{f32_rect_to_au_rect, max_rect};
|
||||||
|
@ -1365,7 +1365,7 @@ impl Window {
|
||||||
// See http://testthewebforward.org/docs/reftests.html
|
// See http://testthewebforward.org/docs/reftests.html
|
||||||
let html_element = document.GetDocumentElement();
|
let html_element = document.GetDocumentElement();
|
||||||
let reftest_wait = html_element.map_or(false, |elem| {
|
let reftest_wait = html_element.map_or(false, |elem| {
|
||||||
elem.has_class(&Atom::from("reftest-wait"))
|
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive)
|
||||||
});
|
});
|
||||||
|
|
||||||
let ready_state = document.ReadyState();
|
let ready_state = document.ReadyState();
|
||||||
|
|
|
@ -49,7 +49,7 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
|
||||||
use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData};
|
use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData};
|
||||||
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, NamespaceConstraint};
|
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
|
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
|
||||||
use selectors::matching::VisitedHandlingMode;
|
use selectors::matching::VisitedHandlingMode;
|
||||||
use servo_atoms::Atom;
|
use servo_atoms::Atom;
|
||||||
|
@ -61,6 +61,7 @@ use std::marker::PhantomData;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use style;
|
use style;
|
||||||
|
use style::CaseSensitivityExt;
|
||||||
use style::applicable_declarations::ApplicableDeclarationBlock;
|
use style::applicable_declarations::ApplicableDeclarationBlock;
|
||||||
use style::attr::AttrValue;
|
use style::attr::AttrValue;
|
||||||
use style::computed_values::display;
|
use style::computed_values::display;
|
||||||
|
@ -414,6 +415,13 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
self.get_attr(namespace, attr).is_some()
|
self.get_attr(namespace, attr).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_id(&self) -> Option<Atom> {
|
||||||
|
unsafe {
|
||||||
|
(*self.element.id_attribute()).clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
|
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -779,16 +787,24 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
(*self.element.id_attribute()).clone()
|
(*self.element.id_attribute())
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |atom| case_sensitivity.eq_atom(atom, id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.element.has_class_for_layout(name)
|
self.element.has_class_for_layout(name, case_sensitivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_quirks_mode_document(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
self.element.in_quirks_mode_document_for_layout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,6 +1210,11 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_quirks_mode_document(&self) -> bool {
|
||||||
|
debug!("ServoThreadSafeLayoutElement::in_quirks_mode_document called");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_local_name(&self) -> &LocalName {
|
fn get_local_name(&self) -> &LocalName {
|
||||||
self.element.get_local_name()
|
self.element.get_local_name()
|
||||||
|
@ -1249,12 +1270,12 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
|
||||||
debug!("ServoThreadSafeLayoutElement::get_id called");
|
debug!("ServoThreadSafeLayoutElement::has_id called");
|
||||||
None
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_class(&self, _name: &Atom) -> bool {
|
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
|
||||||
debug!("ServoThreadSafeLayoutElement::has_class called");
|
debug!("ServoThreadSafeLayoutElement::has_class called");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||||
pub enum ParsedCaseSensitivity {
|
pub enum ParsedCaseSensitivity {
|
||||||
CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
|
CaseSensitive,
|
||||||
AsciiCaseInsensitive,
|
AsciiCaseInsensitive,
|
||||||
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
|
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ impl ParsedCaseSensitivity {
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||||
pub enum CaseSensitivity {
|
pub enum CaseSensitivity {
|
||||||
CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
|
CaseSensitive,
|
||||||
AsciiCaseInsensitive,
|
AsciiCaseInsensitive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* 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::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint};
|
use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use bloom::{BLOOM_HASH_MASK, BloomFilter};
|
use bloom::{BLOOM_HASH_MASK, BloomFilter};
|
||||||
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
||||||
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
|
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
|
||||||
|
@ -666,12 +666,21 @@ fn matches_simple_selector<E, F>(
|
||||||
let ns = ::parser::namespace_empty_string::<E::Impl>();
|
let ns = ::parser::namespace_empty_string::<E::Impl>();
|
||||||
element.get_namespace() == ns.borrow()
|
element.get_namespace() == ns.borrow()
|
||||||
}
|
}
|
||||||
// TODO: case-sensitivity depends on the document type and quirks mode
|
|
||||||
Component::ID(ref id) => {
|
Component::ID(ref id) => {
|
||||||
element.get_id().map_or(false, |attr| attr == *id)
|
let case_sensitivity = if element.in_quirks_mode_document() {
|
||||||
|
CaseSensitivity::AsciiCaseInsensitive
|
||||||
|
} else {
|
||||||
|
CaseSensitivity::CaseSensitive
|
||||||
|
};
|
||||||
|
element.has_id(id, case_sensitivity)
|
||||||
}
|
}
|
||||||
Component::Class(ref class) => {
|
Component::Class(ref class) => {
|
||||||
element.has_class(class)
|
let case_sensitivity = if element.in_quirks_mode_document() {
|
||||||
|
CaseSensitivity::AsciiCaseInsensitive
|
||||||
|
} else {
|
||||||
|
CaseSensitivity::CaseSensitive
|
||||||
|
};
|
||||||
|
element.has_class(class, case_sensitivity)
|
||||||
}
|
}
|
||||||
Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => {
|
Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => {
|
||||||
let is_html = element.is_html_element_in_html_document();
|
let is_html = element.is_html_element_in_html_document();
|
||||||
|
|
|
@ -1385,7 +1385,10 @@ fn parse_attribute_flags<'i, 't, E>(input: &mut CssParser<'i, 't>)
|
||||||
-> Result<ParsedCaseSensitivity,
|
-> Result<ParsedCaseSensitivity,
|
||||||
ParseError<'i, SelectorParseError<'i, E>>> {
|
ParseError<'i, SelectorParseError<'i, E>>> {
|
||||||
match input.next() {
|
match input.next() {
|
||||||
Err(_) => Ok(ParsedCaseSensitivity::CaseSensitive),
|
Err(_) => {
|
||||||
|
// Selectors spec says language-defined, but HTML says sensitive.
|
||||||
|
Ok(ParsedCaseSensitivity::CaseSensitive)
|
||||||
|
}
|
||||||
Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
|
Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
|
||||||
Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
|
Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
|
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
|
||||||
//! between layout and style.
|
//! between layout and style.
|
||||||
|
|
||||||
use attr::{AttrSelectorOperation, NamespaceConstraint};
|
use attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
|
use matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
|
||||||
use parser::SelectorImpl;
|
use parser::SelectorImpl;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -63,9 +63,20 @@ pub trait Element: Sized + Debug {
|
||||||
/// Whether this element is a `link`.
|
/// Whether this element is a `link`.
|
||||||
fn is_link(&self) -> bool;
|
fn is_link(&self) -> bool;
|
||||||
|
|
||||||
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
|
/// Whether this element is in a document that is in quirks mode.
|
||||||
|
///
|
||||||
|
/// https://dom.spec.whatwg.org/#concept-document-quirks
|
||||||
|
fn in_quirks_mode_document(&self) -> bool;
|
||||||
|
|
||||||
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
|
fn has_id(&self,
|
||||||
|
id: &<Self::Impl as SelectorImpl>::Identifier,
|
||||||
|
case_sensitivity: CaseSensitivity)
|
||||||
|
-> bool;
|
||||||
|
|
||||||
|
fn has_class(&self,
|
||||||
|
name: &<Self::Impl as SelectorImpl>::ClassName,
|
||||||
|
case_sensitivity: CaseSensitivity)
|
||||||
|
-> bool;
|
||||||
|
|
||||||
/// Returns whether this element matches `:empty`.
|
/// Returns whether this element matches `:empty`.
|
||||||
///
|
///
|
||||||
|
|
|
@ -376,6 +376,9 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
||||||
/// Whether this element has an attribute with a given namespace.
|
/// Whether this element has an attribute with a given namespace.
|
||||||
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
|
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
|
||||||
|
|
||||||
|
/// The ID for this element.
|
||||||
|
fn get_id(&self) -> Option<Atom>;
|
||||||
|
|
||||||
/// Internal iterator for the classes of this element.
|
/// Internal iterator for the classes of this element.
|
||||||
fn each_class<F>(&self, callback: F) where F: FnMut(&Atom);
|
fn each_class<F>(&self, callback: F) where F: FnMut(&Atom);
|
||||||
|
|
||||||
|
|
|
@ -164,13 +164,14 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
if !self.has_any(Flags::MaybeClass) {
|
if !self.has_any(Flags::MaybeClass) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot_helpers::has_class(self.as_ptr(),
|
snapshot_helpers::has_class(self.as_ptr(),
|
||||||
name,
|
name,
|
||||||
|
case_sensitivity,
|
||||||
bindings::Gecko_SnapshotClassOrClassList)
|
bindings::Gecko_SnapshotClassOrClassList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
|
|
||||||
//! Element an snapshot common logic.
|
//! Element an snapshot common logic.
|
||||||
|
|
||||||
|
use CaseSensitivityExt;
|
||||||
use gecko_bindings::structs::nsIAtom;
|
use gecko_bindings::structs::nsIAtom;
|
||||||
|
use gecko_string_cache::WeakAtom;
|
||||||
|
use selectors::attr::CaseSensitivity;
|
||||||
use std::{ptr, slice};
|
use std::{ptr, slice};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
|
|
||||||
|
@ -16,6 +19,7 @@ pub type ClassOrClassList<T> = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut
|
||||||
/// element has the class that `name` represents.
|
/// element has the class that `name` represents.
|
||||||
pub fn has_class<T>(item: T,
|
pub fn has_class<T>(item: T,
|
||||||
name: &Atom,
|
name: &Atom,
|
||||||
|
case_sensitivity: CaseSensitivity,
|
||||||
getter: ClassOrClassList<T>) -> bool
|
getter: ClassOrClassList<T>) -> bool
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -24,10 +28,10 @@ pub fn has_class<T>(item: T,
|
||||||
let length = getter(item, &mut class, &mut list);
|
let length = getter(item, &mut class, &mut list);
|
||||||
match length {
|
match length {
|
||||||
0 => false,
|
0 => false,
|
||||||
1 => name.as_ptr() == class,
|
1 => case_sensitivity.eq_atom(name, WeakAtom::new(class)),
|
||||||
n => {
|
n => {
|
||||||
let classes = slice::from_raw_parts(list, n as usize);
|
let classes = slice::from_raw_parts(list, n as usize);
|
||||||
classes.iter().any(|ptr| name.as_ptr() == *ptr)
|
classes.iter().any(|ptr| case_sensitivity.eq_atom(name, WeakAtom::new(*ptr)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
//! style system it's kind of pointless in the Stylo case, and only Servo forces
|
//! style system it's kind of pointless in the Stylo case, and only Servo forces
|
||||||
//! the separation between the style system implementation and everything else.
|
//! the separation between the style system implementation and everything else.
|
||||||
|
|
||||||
|
use CaseSensitivityExt;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use applicable_declarations::ApplicableDeclarationBlock;
|
use applicable_declarations::ApplicableDeclarationBlock;
|
||||||
use atomic_refcell::AtomicRefCell;
|
use atomic_refcell::AtomicRefCell;
|
||||||
|
@ -744,6 +745,23 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_id(&self) -> Option<Atom> {
|
||||||
|
if !self.has_id() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = unsafe {
|
||||||
|
bindings::Gecko_AtomAttrValue(self.0,
|
||||||
|
atom!("id").as_ptr())
|
||||||
|
};
|
||||||
|
|
||||||
|
if ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Atom::from(ptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn each_class<F>(&self, callback: F)
|
fn each_class<F>(&self, callback: F)
|
||||||
where F: FnMut(&Atom)
|
where F: FnMut(&Atom)
|
||||||
{
|
{
|
||||||
|
@ -1574,30 +1592,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
self.get_state().intersects(NonTSPseudoClass::AnyLink.state_flag())
|
self.get_state().intersects(NonTSPseudoClass::AnyLink.state_flag())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn in_quirks_mode_document(&self) -> bool {
|
||||||
if !self.has_id() {
|
self.as_node().owner_doc().mCompatMode == structs::nsCompatibility::eCompatibility_NavQuirks
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ptr = unsafe {
|
|
||||||
bindings::Gecko_AtomAttrValue(self.0,
|
|
||||||
atom!("id").as_ptr())
|
|
||||||
};
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Atom::from(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
|
self.get_id().map_or(false, |atom| case_sensitivity.eq_atom(&atom, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
if !self.may_have_class() {
|
if !self.may_have_class() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot_helpers::has_class(self.0,
|
snapshot_helpers::has_class(self.0,
|
||||||
name,
|
name,
|
||||||
|
case_sensitivity,
|
||||||
Gecko_ClassOrClassList)
|
Gecko_ClassOrClassList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,29 @@ impl WeakAtom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether two atoms are ASCII-case-insensitive matches
|
||||||
|
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = self.as_slice();
|
||||||
|
let b = other.as_slice();
|
||||||
|
a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
|
||||||
|
if a16 <= 0x7F && b16 <= 0x7F {
|
||||||
|
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
|
||||||
|
} else {
|
||||||
|
a16 == b16
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether this atom is an ASCII-case-insensitive match for the given string
|
||||||
|
pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
|
||||||
|
self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
|
||||||
|
.eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for WeakAtom {
|
impl fmt::Debug for WeakAtom {
|
||||||
|
@ -258,29 +281,6 @@ impl Atom {
|
||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether two atoms are ASCII-case-insensitive matches
|
|
||||||
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
|
||||||
if self == other {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let a = self.as_slice();
|
|
||||||
let b = other.as_slice();
|
|
||||||
a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
|
|
||||||
if a16 <= 0x7F && b16 <= 0x7F {
|
|
||||||
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
|
|
||||||
} else {
|
|
||||||
a16 == b16
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether this atom is an ASCII-case-insensitive match for the given string
|
|
||||||
pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
|
|
||||||
self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
|
|
||||||
.eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Atom {
|
impl Hash for Atom {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use data::StoredRestyleHint;
|
||||||
use dom::{TElement, TNode};
|
use dom::{TElement, TNode};
|
||||||
use fnv::FnvHashSet;
|
use fnv::FnvHashSet;
|
||||||
use selector_parser::SelectorImpl;
|
use selector_parser::SelectorImpl;
|
||||||
|
use selectors::attr::CaseSensitivity;
|
||||||
use selectors::parser::{Component, Selector};
|
use selectors::parser::{Component, Selector};
|
||||||
use shared_lock::SharedRwLockReadGuard;
|
use shared_lock::SharedRwLockReadGuard;
|
||||||
use stylesheets::{CssRule, Stylesheet};
|
use stylesheets::{CssRule, Stylesheet};
|
||||||
|
@ -37,7 +38,7 @@ impl InvalidationScope {
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
InvalidationScope::Class(ref class) => {
|
InvalidationScope::Class(ref class) => {
|
||||||
element.has_class(class)
|
element.has_class(class, CaseSensitivity::CaseSensitive)
|
||||||
}
|
}
|
||||||
InvalidationScope::ID(ref id) => {
|
InvalidationScope::ID(ref id) => {
|
||||||
match element.get_id() {
|
match element.get_id() {
|
||||||
|
|
|
@ -217,3 +217,21 @@ pub fn serialize_comma_separated_list<W, T>(dest: &mut W,
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gecko")] use gecko_string_cache::WeakAtom;
|
||||||
|
#[cfg(feature = "servo")] use servo_atoms::Atom as WeakAtom;
|
||||||
|
|
||||||
|
/// Extension methods for selectors::attr::CaseSensitivity
|
||||||
|
pub trait CaseSensitivityExt {
|
||||||
|
/// Return whether two atoms compare equal according to this case sensitivity.
|
||||||
|
fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CaseSensitivityExt for selectors::attr::CaseSensitivity {
|
||||||
|
fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool {
|
||||||
|
match self {
|
||||||
|
selectors::attr::CaseSensitivity::CaseSensitive => a == b,
|
||||||
|
selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use Atom;
|
use Atom;
|
||||||
|
use CaseSensitivityExt;
|
||||||
use LocalName;
|
use LocalName;
|
||||||
use Namespace;
|
use Namespace;
|
||||||
use context::{SharedStyleContext, ThreadLocalStyleContext};
|
use context::{SharedStyleContext, ThreadLocalStyleContext, QuirksMode};
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -19,7 +20,7 @@ use heapsize::HeapSizeOf;
|
||||||
use selector_map::{SelectorMap, SelectorMapEntry};
|
use selector_map::{SelectorMap, SelectorMapEntry};
|
||||||
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
|
use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
|
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
|
||||||
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
|
use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
|
||||||
use selectors::parser::{AncestorHashes, Combinator, Component};
|
use selectors::parser::{AncestorHashes, Combinator, Component};
|
||||||
|
@ -549,7 +550,7 @@ pub trait ElementSnapshot : Sized {
|
||||||
|
|
||||||
/// Whether this snapshot contains the class `name`. Should only be called
|
/// Whether this snapshot contains the class `name`. Should only be called
|
||||||
/// if `has_attrs()` returns true.
|
/// if `has_attrs()` returns true.
|
||||||
fn has_class(&self, name: &Atom) -> bool;
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
|
||||||
|
|
||||||
/// A callback that should be called for each class of the snapshot. Should
|
/// A callback that should be called for each class of the snapshot. Should
|
||||||
/// only be called if `has_attrs()` returns true.
|
/// only be called if `has_attrs()` returns true.
|
||||||
|
@ -820,19 +821,25 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn in_quirks_mode_document(&self) -> bool {
|
||||||
|
self.element.in_quirks_mode_document()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
match self.snapshot() {
|
match self.snapshot() {
|
||||||
Some(snapshot) if snapshot.has_attrs()
|
Some(snapshot) if snapshot.has_attrs() => {
|
||||||
=> snapshot.id_attr(),
|
snapshot.id_attr().map_or(false, |atom| case_sensitivity.eq_atom(&atom, id))
|
||||||
_ => self.element.get_id()
|
}
|
||||||
|
_ => self.element.has_id(id, case_sensitivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
match self.snapshot() {
|
match self.snapshot() {
|
||||||
Some(snapshot) if snapshot.has_attrs()
|
Some(snapshot) if snapshot.has_attrs() => {
|
||||||
=> snapshot.has_class(name),
|
snapshot.has_class(name, case_sensitivity)
|
||||||
_ => self.element.has_class(name)
|
}
|
||||||
|
_ => self.element.has_class(name, case_sensitivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,7 +1013,8 @@ pub enum HintComputationContext<'a, E: 'a>
|
||||||
|
|
||||||
impl DependencySet {
|
impl DependencySet {
|
||||||
/// Adds a selector to this `DependencySet`.
|
/// Adds a selector to this `DependencySet`.
|
||||||
pub fn note_selector(&mut self, selector_and_hashes: &SelectorAndHashes<SelectorImpl>) {
|
pub fn note_selector(&mut self, selector_and_hashes: &SelectorAndHashes<SelectorImpl>,
|
||||||
|
quirks_mode: QuirksMode) {
|
||||||
let mut combinator = None;
|
let mut combinator = None;
|
||||||
let mut iter = selector_and_hashes.selector.iter();
|
let mut iter = selector_and_hashes.selector.iter();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
@ -1073,7 +1081,7 @@ impl DependencySet {
|
||||||
selector: selector_and_hashes.selector.clone(),
|
selector: selector_and_hashes.selector.clone(),
|
||||||
selector_offset: sequence_start,
|
selector_offset: sequence_start,
|
||||||
hashes: hashes,
|
hashes: hashes,
|
||||||
});
|
}, quirks_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
combinator = iter.next_sequence();
|
combinator = iter.next_sequence();
|
||||||
|
@ -1149,7 +1157,7 @@ impl DependencySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot.each_class(|c| {
|
snapshot.each_class(|c| {
|
||||||
if !el.has_class(c) {
|
if !el.has_class(c, CaseSensitivity::CaseSensitive) {
|
||||||
additional_classes.push(c.clone())
|
additional_classes.push(c.clone())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1172,8 +1180,9 @@ impl DependencySet {
|
||||||
*el
|
*el
|
||||||
};
|
};
|
||||||
|
|
||||||
self.dependencies
|
self.dependencies.lookup_with_additional(
|
||||||
.lookup_with_additional(lookup_element, additional_id, &additional_classes, &mut |dep| {
|
lookup_element, shared_context.quirks_mode, additional_id, &additional_classes,
|
||||||
|
&mut |dep| {
|
||||||
trace!("scanning dependency: {:?}", dep);
|
trace!("scanning dependency: {:?}", dep);
|
||||||
|
|
||||||
if !dep.sensitivities.sensitive_to(attrs_changed,
|
if !dep.sensitivities.sensitive_to(attrs_changed,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
use {Atom, LocalName};
|
use {Atom, LocalName};
|
||||||
use applicable_declarations::ApplicableDeclarationBlock;
|
use applicable_declarations::ApplicableDeclarationBlock;
|
||||||
|
use context::QuirksMode;
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use pdqsort::sort_by;
|
use pdqsort::sort_by;
|
||||||
|
@ -16,8 +17,8 @@ use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlag
|
||||||
use selectors::parser::{AncestorHashes, Component, Combinator, SelectorAndHashes, SelectorIter};
|
use selectors::parser::{AncestorHashes, Component, Combinator, SelectorAndHashes, SelectorIter};
|
||||||
use selectors::parser::LocalName as LocalNameSelector;
|
use selectors::parser::LocalName as LocalNameSelector;
|
||||||
use smallvec::VecLike;
|
use smallvec::VecLike;
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::hash_map;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use stylist::Rule;
|
use stylist::Rule;
|
||||||
|
|
||||||
|
@ -66,9 +67,9 @@ impl SelectorMapEntry for SelectorAndHashes<SelectorImpl> {
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct SelectorMap<T: SelectorMapEntry> {
|
pub struct SelectorMap<T: SelectorMapEntry> {
|
||||||
/// A hash from an ID to rules which contain that ID selector.
|
/// A hash from an ID to rules which contain that ID selector.
|
||||||
pub id_hash: FnvHashMap<Atom, Vec<T>>,
|
pub id_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
|
||||||
/// A hash from a class name to rules which contain that class selector.
|
/// A hash from a class name to rules which contain that class selector.
|
||||||
pub class_hash: FnvHashMap<Atom, Vec<T>>,
|
pub class_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
|
||||||
/// A hash from local name to rules which contain that local name selector.
|
/// A hash from local name to rules which contain that local name selector.
|
||||||
pub local_name_hash: FnvHashMap<LocalName, Vec<T>>,
|
pub local_name_hash: FnvHashMap<LocalName, Vec<T>>,
|
||||||
/// Rules that don't have ID, class, or element selectors.
|
/// Rules that don't have ID, class, or element selectors.
|
||||||
|
@ -86,8 +87,8 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
/// Trivially constructs an empty `SelectorMap`.
|
/// Trivially constructs an empty `SelectorMap`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SelectorMap {
|
SelectorMap {
|
||||||
id_hash: HashMap::default(),
|
id_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||||
class_hash: HashMap::default(),
|
class_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||||
local_name_hash: HashMap::default(),
|
local_name_hash: HashMap::default(),
|
||||||
other: Vec::new(),
|
other: Vec::new(),
|
||||||
count: 0,
|
count: 0,
|
||||||
|
@ -115,6 +116,7 @@ impl SelectorMap<Rule> {
|
||||||
rule_hash_target: &E,
|
rule_hash_target: &E,
|
||||||
matching_rules_list: &mut V,
|
matching_rules_list: &mut V,
|
||||||
context: &mut MatchingContext,
|
context: &mut MatchingContext,
|
||||||
|
quirks_mode: QuirksMode,
|
||||||
flags_setter: &mut F,
|
flags_setter: &mut F,
|
||||||
cascade_level: CascadeLevel)
|
cascade_level: CascadeLevel)
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
|
@ -128,32 +130,35 @@ impl SelectorMap<Rule> {
|
||||||
// At the end, we're going to sort the rules that we added, so remember where we began.
|
// At the end, we're going to sort the rules that we added, so remember where we began.
|
||||||
let init_len = matching_rules_list.len();
|
let init_len = matching_rules_list.len();
|
||||||
if let Some(id) = rule_hash_target.get_id() {
|
if let Some(id) = rule_hash_target.get_id() {
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
if let Some(rules) = self.id_hash.get(&id, quirks_mode) {
|
||||||
&self.id_hash,
|
SelectorMap::get_matching_rules(element,
|
||||||
&id,
|
rules,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
context,
|
context,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
cascade_level)
|
cascade_level)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rule_hash_target.each_class(|class| {
|
rule_hash_target.each_class(|class| {
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
if let Some(rules) = self.class_hash.get(&class, quirks_mode) {
|
||||||
&self.class_hash,
|
SelectorMap::get_matching_rules(element,
|
||||||
class,
|
rules,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
context,
|
context,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
cascade_level);
|
cascade_level)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
SelectorMap::get_matching_rules_from_hash(element,
|
if let Some(rules) = self.local_name_hash.get(rule_hash_target.get_local_name()) {
|
||||||
&self.local_name_hash,
|
SelectorMap::get_matching_rules(element,
|
||||||
rule_hash_target.get_local_name(),
|
rules,
|
||||||
matching_rules_list,
|
matching_rules_list,
|
||||||
context,
|
context,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
cascade_level);
|
cascade_level)
|
||||||
|
}
|
||||||
|
|
||||||
SelectorMap::get_matching_rules(element,
|
SelectorMap::get_matching_rules(element,
|
||||||
&self.other,
|
&self.other,
|
||||||
|
@ -167,12 +172,6 @@ impl SelectorMap<Rule> {
|
||||||
|block| (block.specificity, block.source_order()));
|
|block| (block.specificity, block.source_order()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether we have rules for the given id
|
|
||||||
#[inline]
|
|
||||||
pub fn has_rules_for_id(&self, id: &Atom) -> bool {
|
|
||||||
self.id_hash.get(id).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
|
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
|
||||||
/// `self` sorted by specificity and source order.
|
/// `self` sorted by specificity and source order.
|
||||||
pub fn get_universal_rules(&self,
|
pub fn get_universal_rules(&self,
|
||||||
|
@ -196,30 +195,6 @@ impl SelectorMap<Rule> {
|
||||||
rules_list
|
rules_list
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
|
|
||||||
element: &E,
|
|
||||||
hash: &FnvHashMap<Str, Vec<Rule>>,
|
|
||||||
key: &BorrowedStr,
|
|
||||||
matching_rules: &mut Vector,
|
|
||||||
context: &mut MatchingContext,
|
|
||||||
flags_setter: &mut F,
|
|
||||||
cascade_level: CascadeLevel)
|
|
||||||
where E: TElement,
|
|
||||||
Str: Borrow<BorrowedStr> + Eq + Hash,
|
|
||||||
BorrowedStr: Eq + Hash,
|
|
||||||
Vector: VecLike<ApplicableDeclarationBlock>,
|
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
|
||||||
{
|
|
||||||
if let Some(rules) = hash.get(key) {
|
|
||||||
SelectorMap::get_matching_rules(element,
|
|
||||||
rules,
|
|
||||||
matching_rules,
|
|
||||||
context,
|
|
||||||
flags_setter,
|
|
||||||
cascade_level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
||||||
fn get_matching_rules<E, V, F>(element: &E,
|
fn get_matching_rules<E, V, F>(element: &E,
|
||||||
rules: &[Rule],
|
rules: &[Rule],
|
||||||
|
@ -247,16 +222,16 @@ impl SelectorMap<Rule> {
|
||||||
|
|
||||||
impl<T: SelectorMapEntry> SelectorMap<T> {
|
impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
/// Inserts into the correct hash, trying id, class, and localname.
|
/// Inserts into the correct hash, trying id, class, and localname.
|
||||||
pub fn insert(&mut self, entry: T) {
|
pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) {
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
|
||||||
if let Some(id_name) = get_id_name(entry.selector()) {
|
if let Some(id_name) = get_id_name(entry.selector()) {
|
||||||
find_push(&mut self.id_hash, id_name, entry);
|
self.id_hash.entry(id_name, quirks_mode).or_insert_with(Vec::new).push(entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(class_name) = get_class_name(entry.selector()) {
|
if let Some(class_name) = get_class_name(entry.selector()) {
|
||||||
find_push(&mut self.class_hash, class_name, entry);
|
self.class_hash.entry(class_name, quirks_mode).or_insert_with(Vec::new).push(entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,13 +268,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
|
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
|
||||||
/// but that function is extremely hot and I'd rather not rearrange it.
|
/// but that function is extremely hot and I'd rather not rearrange it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup<E, F>(&self, element: E, f: &mut F) -> bool
|
pub fn lookup<E, F>(&self, element: E, quirks_mode: QuirksMode, f: &mut F) -> bool
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
F: FnMut(&T) -> bool
|
F: FnMut(&T) -> bool
|
||||||
{
|
{
|
||||||
// Id.
|
// Id.
|
||||||
if let Some(id) = element.get_id() {
|
if let Some(id) = element.get_id() {
|
||||||
if let Some(v) = self.id_hash.get(&id) {
|
if let Some(v) = self.id_hash.get(&id, quirks_mode) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -312,7 +287,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
element.each_class(|class| {
|
element.each_class(|class| {
|
||||||
if !done {
|
if !done {
|
||||||
if let Some(v) = self.class_hash.get(class) {
|
if let Some(v) = self.class_hash.get(class, quirks_mode) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
done = true;
|
done = true;
|
||||||
|
@ -355,6 +330,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lookup_with_additional<E, F>(&self,
|
pub fn lookup_with_additional<E, F>(&self,
|
||||||
element: E,
|
element: E,
|
||||||
|
quirks_mode: QuirksMode,
|
||||||
additional_id: Option<Atom>,
|
additional_id: Option<Atom>,
|
||||||
additional_classes: &[Atom],
|
additional_classes: &[Atom],
|
||||||
f: &mut F)
|
f: &mut F)
|
||||||
|
@ -363,13 +339,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
F: FnMut(&T) -> bool
|
F: FnMut(&T) -> bool
|
||||||
{
|
{
|
||||||
// Do the normal lookup.
|
// Do the normal lookup.
|
||||||
if !self.lookup(element, f) {
|
if !self.lookup(element, quirks_mode, f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the additional id.
|
// Check the additional id.
|
||||||
if let Some(id) = additional_id {
|
if let Some(id) = additional_id {
|
||||||
if let Some(v) = self.id_hash.get(&id) {
|
if let Some(v) = self.id_hash.get(&id, quirks_mode) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -380,7 +356,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||||
|
|
||||||
// Check the additional classes.
|
// Check the additional classes.
|
||||||
for class in additional_classes {
|
for class in additional_classes {
|
||||||
if let Some(v) = self.class_hash.get(class) {
|
if let Some(v) = self.class_hash.get(class, quirks_mode) {
|
||||||
for entry in v.iter() {
|
for entry in v.iter() {
|
||||||
if !f(&entry) {
|
if !f(&entry) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -472,3 +448,32 @@ fn find_push<Str: Eq + Hash, V>(map: &mut FnvHashMap<Str, Vec<V>>,
|
||||||
value: V) {
|
value: V) {
|
||||||
map.entry(key).or_insert_with(Vec::new).push(value)
|
map.entry(key).or_insert_with(Vec::new).push(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper for FnvHashMap that does ASCII-case-insensitive lookup in quirks mode.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct MaybeCaseInsensitiveHashMap<K: Hash + Eq, V>(FnvHashMap<K, V>);
|
||||||
|
|
||||||
|
impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||||
|
/// Empty map
|
||||||
|
pub fn new() -> Self {
|
||||||
|
MaybeCaseInsensitiveHashMap(FnvHashMap::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HashMap::entry
|
||||||
|
pub fn entry(&mut self, mut key: Atom, quirks_mode: QuirksMode) -> hash_map::Entry<Atom, V> {
|
||||||
|
if quirks_mode == QuirksMode::Quirks {
|
||||||
|
key = key.to_ascii_lowercase()
|
||||||
|
}
|
||||||
|
self.0.entry(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HashMap::get
|
||||||
|
pub fn get(&self, key: &Atom, quirks_mode: QuirksMode) -> Option<&V> {
|
||||||
|
if quirks_mode == QuirksMode::Quirks {
|
||||||
|
self.0.get(&key.to_ascii_lowercase())
|
||||||
|
} else {
|
||||||
|
self.0.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
//! Servo's selector parser.
|
//! Servo's selector parser.
|
||||||
|
|
||||||
use {Atom, Prefix, Namespace, LocalName};
|
use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt};
|
||||||
use attr::{AttrIdentifier, AttrValue};
|
use attr::{AttrIdentifier, AttrValue};
|
||||||
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
|
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
|
||||||
use dom::{OpaqueNode, TElement, TNode};
|
use dom::{OpaqueNode, TElement, TNode};
|
||||||
|
@ -15,7 +15,7 @@ use fnv::FnvHashMap;
|
||||||
use restyle_hints::ElementSnapshot;
|
use restyle_hints::ElementSnapshot;
|
||||||
use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
|
use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
|
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
|
||||||
use selectors::parser::{SelectorMethods, SelectorParseError};
|
use selectors::parser::{SelectorMethods, SelectorParseError};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
@ -584,9 +584,9 @@ impl ElementSnapshot for ServoElementSnapshot {
|
||||||
self.get_attr(&ns!(), &local_name!("id")).map(|v| v.as_atom().clone())
|
self.get_attr(&ns!(), &local_name!("id")).map(|v| v.as_atom().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_class(&self, name: &Atom) -> bool {
|
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||||
self.get_attr(&ns!(), &local_name!("class"))
|
self.get_attr(&ns!(), &local_name!("class"))
|
||||||
.map_or(false, |v| v.as_tokens().iter().any(|atom| atom == name))
|
.map_or(false, |v| v.as_tokens().iter().any(|atom| case_sensitivity.eq_atom(atom, name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn each_class<F>(&self, mut callback: F)
|
fn each_class<F>(&self, mut callback: F)
|
||||||
|
|
|
@ -477,15 +477,18 @@ impl Stylist {
|
||||||
self.element_map.borrow_for_origin(&stylesheet.origin)
|
self.element_map.borrow_for_origin(&stylesheet.origin)
|
||||||
};
|
};
|
||||||
|
|
||||||
map.insert(Rule::new(selector_and_hashes.selector.clone(),
|
map.insert(
|
||||||
selector_and_hashes.hashes.clone(),
|
Rule::new(selector_and_hashes.selector.clone(),
|
||||||
locked.clone(),
|
selector_and_hashes.hashes.clone(),
|
||||||
self.rules_source_order));
|
locked.clone(),
|
||||||
|
self.rules_source_order),
|
||||||
|
self.quirks_mode);
|
||||||
|
|
||||||
self.dependencies.note_selector(selector_and_hashes);
|
self.dependencies.note_selector(selector_and_hashes, self.quirks_mode);
|
||||||
if needs_revalidation(&selector_and_hashes.selector) {
|
if needs_revalidation(&selector_and_hashes.selector) {
|
||||||
self.selectors_for_cache_revalidation.insert(
|
self.selectors_for_cache_revalidation.insert(
|
||||||
RevalidationSelectorAndHashes::new(&selector_and_hashes));
|
RevalidationSelectorAndHashes::new(&selector_and_hashes),
|
||||||
|
self.quirks_mode);
|
||||||
}
|
}
|
||||||
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
|
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
|
||||||
attribute_dependencies: &mut self.attribute_dependencies,
|
attribute_dependencies: &mut self.attribute_dependencies,
|
||||||
|
@ -946,6 +949,7 @@ impl Stylist {
|
||||||
element,
|
element,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
|
self.quirks_mode,
|
||||||
&mut dummy_flag_setter,
|
&mut dummy_flag_setter,
|
||||||
CascadeLevel::XBL);
|
CascadeLevel::XBL);
|
||||||
}
|
}
|
||||||
|
@ -1006,6 +1010,7 @@ impl Stylist {
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
context,
|
context,
|
||||||
|
self.quirks_mode,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
CascadeLevel::UANormal);
|
CascadeLevel::UANormal);
|
||||||
debug!("UA normal: {:?}", context.relations);
|
debug!("UA normal: {:?}", context.relations);
|
||||||
|
@ -1045,6 +1050,7 @@ impl Stylist {
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
context,
|
context,
|
||||||
|
self.quirks_mode,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
CascadeLevel::UserNormal);
|
CascadeLevel::UserNormal);
|
||||||
debug!("user normal: {:?}", context.relations);
|
debug!("user normal: {:?}", context.relations);
|
||||||
|
@ -1066,6 +1072,7 @@ impl Stylist {
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
context,
|
context,
|
||||||
|
self.quirks_mode,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
CascadeLevel::AuthorNormal);
|
CascadeLevel::AuthorNormal);
|
||||||
debug!("author normal: {:?}", context.relations);
|
debug!("author normal: {:?}", context.relations);
|
||||||
|
@ -1176,15 +1183,17 @@ impl Stylist {
|
||||||
// the lookups, which means that the bitvecs are comparable. We verify
|
// the lookups, which means that the bitvecs are comparable. We verify
|
||||||
// this in the caller by asserting that the bitvecs are same-length.
|
// this in the caller by asserting that the bitvecs are same-length.
|
||||||
let mut results = BitVec::new();
|
let mut results = BitVec::new();
|
||||||
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector_and_hashes| {
|
self.selectors_for_cache_revalidation.lookup(
|
||||||
results.push(matches_selector(&selector_and_hashes.selector,
|
*element, self.quirks_mode, &mut |selector_and_hashes| {
|
||||||
selector_and_hashes.selector_offset,
|
results.push(matches_selector(&selector_and_hashes.selector,
|
||||||
&selector_and_hashes.hashes,
|
selector_and_hashes.selector_offset,
|
||||||
element,
|
&selector_and_hashes.hashes,
|
||||||
&mut matching_context,
|
element,
|
||||||
flags_setter));
|
&mut matching_context,
|
||||||
true
|
flags_setter));
|
||||||
});
|
true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* 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 style::context::QuirksMode;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_restyle_hints() {
|
fn smoke_restyle_hints() {
|
||||||
use cssparser::{Parser, ParserInput};
|
use cssparser::{Parser, ParserInput};
|
||||||
|
@ -23,6 +25,6 @@ fn smoke_restyle_hints() {
|
||||||
assert_eq!((selectors.0).len(), 1);
|
assert_eq!((selectors.0).len(), 1);
|
||||||
|
|
||||||
let selector = (selectors.0).first().unwrap();
|
let selector = (selectors.0).first().unwrap();
|
||||||
dependencies.note_selector(selector);
|
dependencies.note_selector(selector, QuirksMode::NoQuirks);
|
||||||
assert_eq!(dependencies.len(), 1);
|
assert_eq!(dependencies.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ fn get_mock_map(selectors: &[&str]) -> (SelectorMap<Rule>, SharedRwLock) {
|
||||||
|
|
||||||
for rules in selector_rules.into_iter() {
|
for rules in selector_rules.into_iter() {
|
||||||
for rule in rules.into_iter() {
|
for rule in rules.into_iter() {
|
||||||
map.insert(rule)
|
map.insert(rule, QuirksMode::NoQuirks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,11 +217,11 @@ fn test_get_local_name() {
|
||||||
fn test_insert() {
|
fn test_insert() {
|
||||||
let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]);
|
let (rules_list, _) = get_mock_rules(&[".intro.foo", "#top"]);
|
||||||
let mut selector_map = SelectorMap::new();
|
let mut selector_map = SelectorMap::new();
|
||||||
selector_map.insert(rules_list[1][0].clone());
|
selector_map.insert(rules_list[1][0].clone(), QuirksMode::NoQuirks);
|
||||||
assert_eq!(1, selector_map.id_hash.get(&Atom::from("top")).unwrap()[0].source_order);
|
assert_eq!(1, selector_map.id_hash.get(&Atom::from("top"), QuirksMode::NoQuirks).unwrap()[0].source_order);
|
||||||
selector_map.insert(rules_list[0][0].clone());
|
selector_map.insert(rules_list[0][0].clone(), QuirksMode::NoQuirks);
|
||||||
assert_eq!(0, selector_map.class_hash.get(&Atom::from("foo")).unwrap()[0].source_order);
|
assert_eq!(0, selector_map.class_hash.get(&Atom::from("foo"), QuirksMode::NoQuirks).unwrap()[0].source_order);
|
||||||
assert!(selector_map.class_hash.get(&Atom::from("intro")).is_none());
|
assert!(selector_map.class_hash.get(&Atom::from("intro"), QuirksMode::NoQuirks).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue