script: Move extended_filtering to the style crate.
draft
script: Move extended_filtering to the style crate.
We'll need to call it from the style crate in later patches, when matching
:lang() against element snapshots.
MozReview-Commit-ID: 1jSn8dO4L1F
--- a/servo/components/script/dom/bindings/str.rs
+++ b/servo/components/script/dom/bindings/str.rs
@@ -108,73 +108,16 @@ pub fn is_token(s: &[u8]) -> bool {
125 |
32 => false, // separators
x if x > 127 => false, // non-CHARs
_ => true,
}
})
}
-/// Returns whether the language is matched, as defined by
-/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).
-pub fn extended_filtering(tag: &str, range: &str) -> bool {
- let lang_ranges: Vec<&str> = range.split(',').collect();
-
- lang_ranges.iter().any(|&lang_range| {
- // step 1
- let range_subtags: Vec<&str> = lang_range.split('\x2d').collect();
- let tag_subtags: Vec<&str> = tag.split('\x2d').collect();
-
- let mut range_iter = range_subtags.iter();
- let mut tag_iter = tag_subtags.iter();
-
- // step 2
- // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card
- if let (Some(range_subtag), Some(tag_subtag)) = (range_iter.next(), tag_iter.next()) {
- if !(range_subtag.eq_ignore_ascii_case(tag_subtag) || range_subtag.eq_ignore_ascii_case("*")) {
- return false;
- }
- }
-
- let mut current_tag_subtag = tag_iter.next();
-
- // step 3
- for range_subtag in range_iter {
- // step 3a
- if range_subtag.eq_ignore_ascii_case("*") {
- continue;
- }
- match current_tag_subtag.clone() {
- Some(tag_subtag) => {
- // step 3c
- if range_subtag.eq_ignore_ascii_case(tag_subtag) {
- current_tag_subtag = tag_iter.next();
- continue;
- } else {
- // step 3d
- if tag_subtag.len() == 1 {
- return false;
- } else {
- // else step 3e - continue with loop
- current_tag_subtag = tag_iter.next();
- if current_tag_subtag.is_none() {
- return false;
- }
- }
- }
- },
- // step 3b
- None => { return false; }
- }
- }
- // step 4
- true
- })
-}
-
/// A DOMString.
///
/// This type corresponds to the [`DOMString`](idl) type in WebIDL.
///
/// [idl]: https://heycam.github.io/webidl/#idl-DOMString
///
/// Cenceptually, a DOMString has the same value space as a JavaScript String,
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -21,17 +21,17 @@ use dom::bindings::codegen::Bindings::Wi
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::conversions::DerivedFrom;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::{JS, LayoutJS, MutNullableJS};
use dom::bindings::js::{Root, RootedReference};
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::DomObject;
-use dom::bindings::str::{DOMString, extended_filtering};
+use dom::bindings::str::DOMString;
use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type};
use dom::bindings::xmlname::XMLName::InvalidXMLName;
use dom::characterdata::CharacterData;
use dom::create::create_element;
use dom::document::{Document, LayoutDocumentHelpers};
use dom::documentfragment::DocumentFragment;
use dom::domrect::DOMRect;
use dom::domtokenlist::DOMTokenList;
@@ -101,16 +101,17 @@ use std::rc::Rc;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::{QuirksMode, ReflowGoal};
use style::element_state::*;
use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
use style::restyle_hints::RestyleHint;
use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
+use style::selector_parser::extended_filtering;
use style::shared_lock::{SharedRwLock, Locked};
use style::sink::Push;
use style::stylearc::Arc;
use style::stylist::ApplicableDeclarationBlock;
use style::thread_state;
use style::values::{CSSFloat, Either};
use style::values::specified::{self, CSSColor};
use stylesheet_loader::StylesheetOwner;
@@ -2453,18 +2454,20 @@ impl<'a> ::selectors::Element for Root<E
}
},
NonTSPseudoClass::ServoCaseSensitiveTypeAttr(ref expected_value) => {
self.get_attribute(&ns!(), &local_name!("type"))
.map_or(false, |attr| attr.value().eq(expected_value))
}
- // FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647
- // https://tools.ietf.org/html/rfc4647#section-3.3.2
+ // FIXME(heycam): This is wrong, since extended_filtering accepts
+ // a string containing commas (separating each language tag in
+ // a list) but the pseudo-class instead should be parsing and
+ // storing separate <ident> or <string>s for each language tag.
NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.get_lang(), &*lang),
NonTSPseudoClass::ReadOnly =>
!Element::state(self).contains(pseudo_class.state_flag()),
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Fullscreen |
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -29,17 +29,16 @@
//! `html_element_in_html_document_for_layout()`.
#![allow(unsafe_code)]
use atomic_refcell::{AtomicRef, AtomicRefCell};
use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::LayoutJS;
-use dom::bindings::str::extended_filtering;
use dom::characterdata::LayoutCharacterDataHelpers;
use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle};
use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_DIRTY_DESCENDANTS, IS_IN_DOC};
use dom::node::{HANDLED_SNAPSHOT, HAS_SNAPSHOT};
use dom::node::{LayoutNodeHelpers, Node};
use dom::text::Text;
use gfx_traits::ByteIndex;
@@ -65,17 +64,17 @@ use style::attr::AttrValue;
use style::computed_values::display;
use style::context::{QuirksMode, SharedStyleContext};
use style::data::ElementData;
use style::dom::{DescendantsBit, DirtyDescendants, LayoutIterator, NodeInfo, OpaqueNode};
use style::dom::{PresentationalHintsSynthesizer, TElement, TNode, UnsafeNode};
use style::element_state::*;
use style::font_metrics::ServoMetricsProvider;
use style::properties::{ComputedValues, PropertyDeclarationBlock};
-use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
+use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, extended_filtering};
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
use style::sink::Push;
use style::str::is_whitespace;
use style::stylearc::Arc;
use style::stylist::ApplicableDeclarationBlock;
#[derive(Copy, Clone)]
pub struct ServoLayoutNode<'a> {
@@ -686,18 +685,20 @@ impl<'le> ::selectors::Element for Servo
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => self.is_link(),
NonTSPseudoClass::Visited => false,
- // FIXME(#15746): This is wrong, we need to instead use extended filtering as per RFC4647
- // https://tools.ietf.org/html/rfc4647#section-3.3.2
+ // FIXME(heycam): This is wrong, since extended_filtering accepts
+ // a string containing commas (separating each language tag in
+ // a list) but the pseudo-class instead should be parsing and
+ // storing separate <ident> or <string>s for each language tag.
NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.element.get_lang_for_layout(), &*lang),
NonTSPseudoClass::ServoNonZeroBorder => unsafe {
match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &local_name!("border")) {
None | Some(&AttrValue::UInt(_, 0)) => false,
_ => true,
}
},
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -13,16 +13,17 @@ use dom::{OpaqueNode, TElement, TNode};
use element_state::ElementState;
use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::Element;
use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
use selectors::parser::SelectorMethods;
use selectors::visitor::SelectorVisitor;
+use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::fmt;
use std::fmt::Debug;
use std::mem;
use std::ops::{Deref, DerefMut};
/// A pseudo-element, both public and private.
///
@@ -606,8 +607,65 @@ impl ServoElementSnapshot {
}
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
true
}
}
+
+/// Returns whether the language is matched, as defined by
+/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).
+pub fn extended_filtering(tag: &str, range: &str) -> bool {
+ let lang_ranges: Vec<&str> = range.split(',').collect();
+
+ lang_ranges.iter().any(|&lang_range| {
+ // step 1
+ let range_subtags: Vec<&str> = lang_range.split('\x2d').collect();
+ let tag_subtags: Vec<&str> = tag.split('\x2d').collect();
+
+ let mut range_iter = range_subtags.iter();
+ let mut tag_iter = tag_subtags.iter();
+
+ // step 2
+ // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card
+ if let (Some(range_subtag), Some(tag_subtag)) = (range_iter.next(), tag_iter.next()) {
+ if !(range_subtag.eq_ignore_ascii_case(tag_subtag) || range_subtag.eq_ignore_ascii_case("*")) {
+ return false;
+ }
+ }
+
+ let mut current_tag_subtag = tag_iter.next();
+
+ // step 3
+ for range_subtag in range_iter {
+ // step 3a
+ if range_subtag.eq_ignore_ascii_case("*") {
+ continue;
+ }
+ match current_tag_subtag.clone() {
+ Some(tag_subtag) => {
+ // step 3c
+ if range_subtag.eq_ignore_ascii_case(tag_subtag) {
+ current_tag_subtag = tag_iter.next();
+ continue;
+ } else {
+ // step 3d
+ if tag_subtag.len() == 1 {
+ return false;
+ } else {
+ // else step 3e - continue with loop
+ current_tag_subtag = tag_iter.next();
+ if current_tag_subtag.is_none() {
+ return false;
+ }
+ }
+ }
+ },
+ // step 3b
+ None => { return false; }
+ }
+ }
+ // step 4
+ true
+ })
+}