diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index d5b10fcb01e..e75958d974a 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -113,6 +113,64 @@ pub fn is_token(s: &[u8]) -> bool { }) } +/// 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. diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 5bbef292bd3..eeff67ed318 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -25,7 +25,7 @@ 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; +use dom::bindings::str::{DOMString, extended_filtering}; use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::characterdata::CharacterData; @@ -2402,7 +2402,7 @@ impl<'a> ::selectors::Element for Root { // 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 - NonTSPseudoClass::Lang(ref lang) => lang.eq_ignore_ascii_case(&self.get_lang()), + NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.get_lang(), &*lang), NonTSPseudoClass::ReadOnly => !Element::state(self).contains(pseudo_class.state_flag()), diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 66a77e892e1..994f77aa1d5 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -34,6 +34,7 @@ use atomic_refcell::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}; @@ -53,7 +54,6 @@ use selectors::matching::ElementSelectorFlags; use selectors::parser::{AttrSelector, NamespaceConstraint}; use servo_atoms::Atom; use servo_url::ServoUrl; -use std::ascii::AsciiExt; use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; @@ -621,7 +621,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { // 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 - NonTSPseudoClass::Lang(ref lang) => lang.eq_ignore_ascii_case(&self.element.get_lang_for_layout()), + 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")) { diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-005.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-005.htm.ini deleted file mode 100644 index 8013fb0289b..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-005.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-005.htm] - type: testharness - [A :lang value will match a lang attribute value when the latter contains additional subtags.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-009.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-009.htm.ini deleted file mode 100644 index c775fd4443b..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-009.htm.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-009.htm] - type: testharness - [A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-014.htm.ini b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-014.htm.ini new file mode 100644 index 00000000000..689b761d56b --- /dev/null +++ b/tests/wpt/metadata-css/selectors-3_dev/html/css3-selectors-lang-014.htm.ini @@ -0,0 +1,4 @@ +[css3-selectors-lang-014.htm] + type: testharness + [A :lang value with language and region subtags will NOT match a lang attribute value with language, script and region subtags.] + expected: FAIL \ No newline at end of file diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-005.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-005.xht.ini deleted file mode 100644 index 47a66c53921..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-005.xht.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-005.xht] - type: testharness - [A :lang value will match a lang attribute value when the latter contains additional subtags.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-009.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-009.xht.ini deleted file mode 100644 index 11bfe983178..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-009.xht.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-009.xht] - type: testharness - [A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-014.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-014.xht.ini new file mode 100644 index 00000000000..c62441a9a7b --- /dev/null +++ b/tests/wpt/metadata-css/selectors-3_dev/xhtml1/css3-selectors-lang-014.xht.ini @@ -0,0 +1,4 @@ +[css3-selectors-lang-014.xht] + type: testharness + [A :lang value with language and region subtags will NOT match a lang attribute value with language, script and region subtags.] + expected: FAIL diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-005.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-005.xht.ini deleted file mode 100644 index 47a66c53921..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-005.xht.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-005.xht] - type: testharness - [A :lang value will match a lang attribute value when the latter contains additional subtags.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-009.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-009.xht.ini deleted file mode 100644 index 11bfe983178..00000000000 --- a/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-009.xht.ini +++ /dev/null @@ -1,5 +0,0 @@ -[css3-selectors-lang-009.xht] - type: testharness - [A :lang value with a multiple subtags will match a lang attribute value with multiple subtags as long as the first part is the same.] - expected: FAIL - diff --git a/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-014.xht.ini b/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-014.xht.ini new file mode 100644 index 00000000000..c62441a9a7b --- /dev/null +++ b/tests/wpt/metadata-css/selectors-3_dev/xhtml1print/css3-selectors-lang-014.xht.ini @@ -0,0 +1,4 @@ +[css3-selectors-lang-014.xht] + type: testharness + [A :lang value with language and region subtags will NOT match a lang attribute value with language, script and region subtags.] + expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/Element-matches.html.ini b/tests/wpt/metadata/dom/nodes/Element-matches.html.ini index 3387708af6c..1ab2c42ffbb 100644 --- a/tests/wpt/metadata/dom/nodes/Element-matches.html.ini +++ b/tests/wpt/metadata/dom/nodes/Element-matches.html.ini @@ -1,14 +1,5 @@ [Element-matches.html] type: testharness - [In-document Element.matches: :lang pseudo-class selector, matching specified language with partial value (with no refNodes): #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Detached Element.matches: :lang pseudo-class selector, matching specified language with partial value (with no refNodes): #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Fragment Element.matches: :lang pseudo-class selector, matching specified language with partial value (with no refNodes): #pseudo-lang-div3:lang(en)] - expected: FAIL - [In-document Element.matches: Universal selector, matching all children of the specified reference element (with refNode Element): >*] expected: FAIL @@ -18,9 +9,6 @@ [In-document Element.matches: Universal selector, matching all descendants of the specified reference element (with refNode Element): *] expected: FAIL - [In-document Element.matches: :lang pseudo-class selector, matching specified language with partial value (1) (with no refNodes): #pseudo-lang-div3:lang(en)] - expected: FAIL - [In-document Element.matches: Class selector, matching element with class value using non-ASCII characters (with no refNodes): .台北Táiběi] expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All-xht.xht.ini b/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All-xht.xht.ini index 50ca976a95d..4ab75dca734 100644 --- a/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All-xht.xht.ini +++ b/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All-xht.xht.ini @@ -1,11 +1,5 @@ [ParentNode-querySelector-All-xht.xht] type: testharness - [Document.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Document.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Document.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -30,12 +24,6 @@ [Document.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [Detached Element.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Detached Element.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Detached Element.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -60,12 +48,6 @@ [Detached Element.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [Fragment.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Fragment.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Fragment.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -90,12 +72,6 @@ [Fragment.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [In-document Element.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [In-document Element.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [In-document Element.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All.html.ini b/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All.html.ini index 80ddef94811..f40be05c0d4 100644 --- a/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All.html.ini +++ b/tests/wpt/metadata/dom/nodes/ParentNode-querySelector-All.html.ini @@ -1,11 +1,5 @@ [ParentNode-querySelector-All.html] type: testharness - [Document.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Document.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Document.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -30,12 +24,6 @@ [Document.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [Detached Element.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Detached Element.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Detached Element.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -60,12 +48,6 @@ [Detached Element.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [Fragment.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [Fragment.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [Fragment.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL @@ -90,12 +72,6 @@ [Fragment.querySelector: ::first-letter pseudo-element (two-colon syntax) selector, not matching any elements: #pseudo-element::first-letter] expected: FAIL - [In-document Element.querySelectorAll: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - - [In-document Element.querySelector: :lang pseudo-class selector, matching specified language with partial value: #pseudo-lang-div3:lang(en)] - expected: FAIL - [In-document Element.querySelectorAll: :first-line pseudo-element (one-colon syntax) selector, not matching any elements: #pseudo-element:first-line] expected: FAIL