[CSS] The parser should not get rid of empty namespace specification in front of...
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Aug 2016 06:22:40 +0000 (06:22 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Aug 2016 06:22:40 +0000 (06:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160936

Reviewed by Chris Dumez.

LayoutTests/imported/w3c:

* web-platform-tests/dom/nodes/ParentNode-querySelector-All-expected.txt:
* web-platform-tests/dom/nodes/ParentNode-querySelector-All-xht-expected.txt:

Source/WebCore:

There are two places where you can specify an empty namespace
in selectors:
    - Element name (e.g. "|name")
    - Attribute name (e.g. "[|name]")
In the first case, if we have an empty namespace, the selector
should match that.
In the second case, the default namespace of attribute is already
empty so it is just the same as "[name]".

Our code was just discarding any empty namespace. This is fine
for attributes but for names that is transforming "|name" into "name"
which is invalid.

This patch updates those cases to differentiate:
- Null prefix -> There was no namespace specified.
- Empty prefix -> There was a namespace prefix and it is empty.

Test: fast/selectors/empty-namespace-with-element-selector.html

* css/CSSGrammar.y.in:
* css/CSSParser.cpp:
(WebCore::CSSParser::determineNameInNamespace):
(WebCore::CSSParser::rewriteSpecifiersWithNamespaceIfNeeded):
(WebCore::CSSParser::rewriteSpecifiersWithElementName):
* css/CSSParser.h:
* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText):
* css/CSSSelectorList.cpp:
(WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):
* css/StyleSheetContents.cpp:
(WebCore::StyleSheetContents::determineNamespace): Deleted.

LayoutTests:

* fast/css/css-selector-text-expected.txt:
* fast/css/css-selector-text.html:
* fast/css/css-set-selector-text-expected.txt:
* fast/css/css-set-selector-text.html:
* fast/selectors/empty-namespace-with-element-selector-expected.txt: Added.
* fast/selectors/empty-namespace-with-element-selector.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@204591 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/css-selector-text-expected.txt
LayoutTests/fast/css/css-selector-text.html
LayoutTests/fast/css/css-set-selector-text-expected.txt
LayoutTests/fast/css/css-set-selector-text.html
LayoutTests/fast/selectors/empty-namespace-with-element-selector-expected.txt [new file with mode: 0644]
LayoutTests/fast/selectors/empty-namespace-with-element-selector.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/dom/nodes/ParentNode-querySelector-All-expected.txt
LayoutTests/imported/w3c/web-platform-tests/dom/nodes/ParentNode-querySelector-All-xht-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/css/CSSGrammar.y.in
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSParser.h
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/css/CSSSelectorList.cpp
Source/WebCore/css/StyleSheetContents.cpp

index 7977aee..fc68aba 100644 (file)
@@ -1,3 +1,17 @@
+2016-08-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        [CSS] The parser should not get rid of empty namespace specification in front of element name selectors
+        https://bugs.webkit.org/show_bug.cgi?id=160936
+
+        Reviewed by Chris Dumez.
+
+        * fast/css/css-selector-text-expected.txt:
+        * fast/css/css-selector-text.html:
+        * fast/css/css-set-selector-text-expected.txt:
+        * fast/css/css-set-selector-text.html:
+        * fast/selectors/empty-namespace-with-element-selector-expected.txt: Added.
+        * fast/selectors/empty-namespace-with-element-selector.html: Added.
+
 2016-08-17  Chris Fleizach  <cfleizach@apple.com>
 
         AX: Support abbreviations in iOS
index 216c072..6dc7ca1 100644 (file)
@@ -26,8 +26,11 @@ PASS parseThenSerializeRule('*|a { }') is '*|a { }'
 PASS parseThenSerializeRule('n|a { }') is 'n|a { }'
 PASS parseThenSerializeRule('*|* { }') is '*|* { }'
 PASS parseThenSerializeRule('n|* { }') is 'n|* { }'
+PASS parseThenSerializeRule('|a { }') is '|a { }'
+PASS parseThenSerializeRule('|* { }') is '|* { }'
 PASS parseThenSerializeRule('[*|a] { }') is '[*|a] { }'
 PASS parseThenSerializeRule('[n|a] { }') is '[n|a] { }'
+PASS parseThenSerializeRule('[|a] { }') is '[a] { }'
 
 PASS parseThenSerializeRule('a:active { }') is 'a:active { }'
 PASS parseThenSerializeRule('a b { }') is 'a b { }'
@@ -253,7 +256,6 @@ PASS parseThenSerializeRule(':any-link + div { }') is ':any-link + div { }'
 PASS parseThenSerializeRule(':not(:any-link) { }') is ':not(:any-link) { }'
 
 PASS parseThenSerializeRule('*:active { }') is ':active { }'
-PASS parseThenSerializeRule('|a { }') is 'a { }'
 
 PASS parseThenSerializeRule('input[type=file]:focus { }') is 'input[type="file"]:focus { }'
 
index d55b502..f72b346 100644 (file)
@@ -77,8 +77,11 @@ testSelectorRoundTrip('*|a');
 testSelectorRoundTrip('n|a');
 testSelectorRoundTrip('*|*');
 testSelectorRoundTrip('n|*');
+testSelectorRoundTrip('|a');
+testSelectorRoundTrip('|*');
 testSelectorRoundTrip('[*|a]');
 testSelectorRoundTrip('[n|a]');
+shouldBe("parseThenSerializeRule('[|a] { }')", "'[a] { }'");
 
 debug('');
 
@@ -354,7 +357,6 @@ testSelectorRoundTrip(':not(:any-link)');
 debug('');
 
 shouldBe("parseThenSerializeRule('*:active { }')", "':active { }'");
-shouldBe("parseThenSerializeRule('|a { }')", "'a { }'");
 
 debug('');
 
index d2b726e..f869e5e 100644 (file)
@@ -38,6 +38,10 @@ PASS setThenReadSelectorText('*|*') is '*|*'
 PASS setThenReadSelectorText('n|*') is 'n|*'
 PASS setThenReadSelectorText('[*|a]') is '[*|a]'
 PASS setThenReadSelectorText('[n|a]') is '[n|a]'
+PASS setThenReadSelectorText('|*') is '|*'
+PASS setThenReadSelectorText('[*|a]') is '[*|a]'
+PASS setThenReadSelectorText('[n|a]') is '[n|a]'
+PASS setThenReadSelectorText('[|a]') is '[a]'
 
 PASS setThenReadSelectorText('a:active') is 'a:active'
 PASS setThenReadSelectorText('a b') is 'a b'
@@ -248,7 +252,6 @@ PASS setThenReadSelectorText(':-webkit-any(.class1:hover)') is ':-webkit-any(.cl
 PASS setThenReadSelectorText(':-webkit-any(a.class1.class2.class3:hover)') is ':-webkit-any(a.class1.class2.class3:hover)'
 
 PASS setThenReadSelectorText('*:active') is ':active'
-PASS setThenReadSelectorText('|a') is 'a'
 
 PASS setThenReadSelectorText('input[type=file]:focus') is 'input[type="file"]:focus'
 
index 8baf820..39a44bd 100644 (file)
@@ -96,6 +96,10 @@ testSelectorRoundTrip('*|*');
 testSelectorRoundTrip('n|*');
 testSelectorRoundTrip('[*|a]');
 testSelectorRoundTrip('[n|a]');
+testSelectorRoundTrip('|*');
+testSelectorRoundTrip('[*|a]');
+testSelectorRoundTrip('[n|a]');
+shouldBe("setThenReadSelectorText('[|a]')", "'[a]'");
 
 debug('');
 
@@ -351,7 +355,6 @@ testSelectorRoundTrip(':-webkit-any(a.class1.class2.class3:hover)');
 debug('');
 
 shouldBe("setThenReadSelectorText('*:active')", "':active'");
-shouldBe("setThenReadSelectorText('|a')", "'a'");
 
 debug('');
 
diff --git a/LayoutTests/fast/selectors/empty-namespace-with-element-selector-expected.txt b/LayoutTests/fast/selectors/empty-namespace-with-element-selector-expected.txt
new file mode 100644 (file)
index 0000000..7eb039f
--- /dev/null
@@ -0,0 +1,22 @@
+Verify that "|name" matches the empty namespace while "*|name" and "name" match any namespace.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Trivial Selector used as rightmost
+PASS root.querySelectorAll("|div").length is 1
+PASS root.querySelectorAll("|div")[0].getAttribute("data-case") is "2"
+PASS root.querySelectorAll("*|div").length is 2
+PASS root.querySelectorAll("*|div")[0].getAttribute("data-case") is "1"
+PASS root.querySelectorAll("*|div")[1].getAttribute("data-case") is "2"
+PASS root.querySelectorAll("div").length is 2
+PASS root.querySelectorAll("div")[0].getAttribute("data-case") is "1"
+PASS root.querySelectorAll("div")[1].getAttribute("data-case") is "2"
+PASS getComputedStyle(document.querySelector("[|data-case='1' i]")).color is "rgb(4, 5, 6)"
+PASS getComputedStyle(document.querySelector("[|data-case='1' i]")).backgroundColor is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelector("[|data-case='2' i]")).color is "rgb(1, 2, 3)"
+PASS getComputedStyle(document.querySelector("[|data-case='2' i]")).backgroundColor is "rgb(4, 5, 6)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/selectors/empty-namespace-with-element-selector.html b/LayoutTests/fast/selectors/empty-namespace-with-element-selector.html
new file mode 100644 (file)
index 0000000..f4053f5
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <script src="../../resources/js-test-pre.js"></script>
+        <style>
+            @namespace uh url(http://www.w3.org/1999/xhtml);
+            #root div {
+                background-color: rgb(1, 2, 3);
+                color: rgb(1, 2, 3);
+            }
+            #root |div {
+                background-color: rgb(4, 5, 6);
+            }
+            #root uh|div {
+                color: rgb(4, 5, 6);
+            }
+        </style>
+    </head>
+    <body>
+        <div id="root" style="display:none;">
+            <div data-case="1">Default namespace</div>
+        </div>
+        <script>
+            description("Verify that \"|name\" matches the empty namespace while \"*|name\" and \"name\" match any namespace.");
+
+            var root = document.getElementById("root");
+            var elementWithoutNamespace = document.createElementNS("", "div");
+            elementWithoutNamespace.setAttribute("data-case", "2");
+            root.appendChild(elementWithoutNamespace);
+
+            function test(selector, expectedCases)
+            {
+                shouldBe('root.querySelectorAll("' + selector + '").length', '' + expectedCases.length);
+                let queryResult = root.querySelectorAll(selector);
+                for (let i = 0; i < queryResult.length; ++i) {
+                    shouldBeEqualToString('root.querySelectorAll("' + selector + '")[' + i + '].getAttribute("data-case")', '' + expectedCases[i]);
+                }
+            }
+
+            debug("Trivial Selector used as rightmost");
+            test("|div", [2]);
+            test("*|div", [1, 2]);
+            test("div", [1, 2]);
+
+            shouldBeEqualToString('getComputedStyle(document.querySelector("[|data-case=\'1\' i]")).color', 'rgb(4, 5, 6)');
+            shouldBeEqualToString('getComputedStyle(document.querySelector("[|data-case=\'1\' i]")).backgroundColor', 'rgb(1, 2, 3)');
+            shouldBeEqualToString('getComputedStyle(document.querySelector("[|data-case=\'2\' i]")).color', 'rgb(1, 2, 3)');
+            shouldBeEqualToString('getComputedStyle(document.querySelector("[|data-case=\'2\' i]")).backgroundColor', 'rgb(4, 5, 6)');
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </body>
+</html>
index 6c61ab9..7dbf179 100644 (file)
@@ -1,3 +1,13 @@
+2016-08-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        [CSS] The parser should not get rid of empty namespace specification in front of element name selectors
+        https://bugs.webkit.org/show_bug.cgi?id=160936
+
+        Reviewed by Chris Dumez.
+
+        * web-platform-tests/dom/nodes/ParentNode-querySelector-All-expected.txt:
+        * web-platform-tests/dom/nodes/ParentNode-querySelector-All-xht-expected.txt:
+
 2016-08-16  Chris Dumez  <cdumez@apple.com>
 
         Add support for ShadowRoot.mode attribute
index debd1ff..9ce35c3 100644 (file)
@@ -603,10 +603,10 @@ PASS Document.querySelectorAll: ID selector, matching element with id with escap
 PASS Document.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Document.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Document.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Document.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Document.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Document.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Document.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Document.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Document.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Document.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Document.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Document.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Document.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Document.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: body #descendant-div1 
@@ -1021,10 +1021,10 @@ PASS Detached Element.querySelectorAll: ID selector, matching element with id wi
 PASS Detached Element.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Detached Element.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Detached Element.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Detached Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Detached Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Detached Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Detached Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Detached Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Detached Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Detached Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Detached Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Detached Element.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Detached Element.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Detached Element.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: div #descendant-div1 
@@ -1437,10 +1437,10 @@ PASS Fragment.querySelectorAll: ID selector, matching element with id with escap
 PASS Fragment.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Fragment.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Fragment.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Fragment.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Fragment.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Fragment.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Fragment.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Fragment.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Fragment.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Fragment.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Fragment.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Fragment.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Fragment.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Fragment.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: div #descendant-div1 
@@ -1853,10 +1853,10 @@ PASS In-document Element.querySelectorAll: ID selector, matching element with id
 PASS In-document Element.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS In-document Element.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS In-document Element.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL In-document Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL In-document Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL In-document Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL In-document Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS In-document Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS In-document Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS In-document Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS In-document Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS In-document Element.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS In-document Element.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS In-document Element.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: body #descendant-div1 
index 05e4907..4b186bd 100644 (file)
@@ -603,10 +603,10 @@ PASS Document.querySelectorAll: ID selector, matching element with id with escap
 PASS Document.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Document.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Document.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Document.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Document.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Document.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Document.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Document.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Document.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Document.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Document.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Document.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Document.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Document.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: body #descendant-div1 
@@ -1021,10 +1021,10 @@ PASS Detached Element.querySelectorAll: ID selector, matching element with id wi
 PASS Detached Element.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Detached Element.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Detached Element.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Detached Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Detached Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Detached Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Detached Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Detached Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Detached Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Detached Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Detached Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Detached Element.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Detached Element.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Detached Element.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: div #descendant-div1 
@@ -1437,10 +1437,10 @@ PASS Fragment.querySelectorAll: ID selector, matching element with id with escap
 PASS Fragment.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS Fragment.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS Fragment.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL Fragment.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Fragment.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL Fragment.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL Fragment.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS Fragment.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Fragment.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS Fragment.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS Fragment.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS Fragment.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Fragment.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS Fragment.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: div #descendant-div1 
@@ -1853,10 +1853,10 @@ PASS In-document Element.querySelectorAll: ID selector, matching element with id
 PASS In-document Element.querySelector: ID selector, matching element with id with escaped character: #test\.foo\[5\]bar 
 PASS In-document Element.querySelectorAll: Namespace selector, matching element with any namespace: #any-namespace *|div 
 PASS In-document Element.querySelector: Namespace selector, matching element with any namespace: #any-namespace *|div 
-FAIL In-document Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL In-document Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
-FAIL In-document Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the expected number of matches. expected 1 but got 4
-FAIL In-document Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* assert_equals: The method should return the first match. expected "no-namespace-div3" but got "no-namespace-div1"
+PASS In-document Element.querySelectorAll: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS In-document Element.querySelector: Namespace selector, matching div elements in no namespace only: #no-namespace |div 
+PASS In-document Element.querySelectorAll: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
+PASS In-document Element.querySelector: Namespace selector, matching any elements in no namespace only: #no-namespace |* 
 PASS In-document Element.querySelectorAll: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS In-document Element.querySelector: Descendant combinator, matching element that is a descendant of an element with id: #descendant div 
 PASS In-document Element.querySelectorAll: Descendant combinator, matching element with id that is a descendant of an element: body #descendant-div1 
index 3ed3057..1cbcb55 100644 (file)
@@ -1,3 +1,42 @@
+2016-08-17  Benjamin Poulain  <benjamin@webkit.org>
+
+        [CSS] The parser should not get rid of empty namespace specification in front of element name selectors
+        https://bugs.webkit.org/show_bug.cgi?id=160936
+
+        Reviewed by Chris Dumez.
+
+        There are two places where you can specify an empty namespace
+        in selectors:
+            - Element name (e.g. "|name")
+            - Attribute name (e.g. "[|name]")
+        In the first case, if we have an empty namespace, the selector
+        should match that.
+        In the second case, the default namespace of attribute is already
+        empty so it is just the same as "[name]".
+
+        Our code was just discarding any empty namespace. This is fine
+        for attributes but for names that is transforming "|name" into "name"
+        which is invalid.
+
+        This patch updates those cases to differentiate:
+        - Null prefix -> There was no namespace specified.
+        - Empty prefix -> There was a namespace prefix and it is empty.
+
+        Test: fast/selectors/empty-namespace-with-element-selector.html
+
+        * css/CSSGrammar.y.in:
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::determineNameInNamespace):
+        (WebCore::CSSParser::rewriteSpecifiersWithNamespaceIfNeeded):
+        (WebCore::CSSParser::rewriteSpecifiersWithElementName):
+        * css/CSSParser.h:
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::selectorText):
+        * css/CSSSelectorList.cpp:
+        (WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):
+        * css/StyleSheetContents.cpp:
+        (WebCore::StyleSheetContents::determineNamespace): Deleted.
+
 2016-08-17  Chris Dumez  <cdumez@apple.com>
 
         [Web IDL] Add support for dictionary members of dictionary types
index 439f89b..ad98c21 100644 (file)
@@ -1165,7 +1165,10 @@ complex_selector:
     ;
 
 namespace_selector:
-    '|' { $$.clear(); }
+    '|' {
+        static LChar emptyString = '\0';
+        $$.init(&emptyString, 0);
+    }
     | '*' '|' { static LChar star = '*'; $$.init(&star, 1); }
     | IDENT '|'
 ;
@@ -1176,8 +1179,10 @@ compound_selector:
     }
     | element_name specifier_list {
         $$ = $2;
-        if ($$)
-            parser->rewriteSpecifiersWithElementName(nullAtom, $1, *$$);
+        if ($$) {
+            QualifiedName elementName(nullAtom, $1, parser->m_defaultNamespace);
+            parser->rewriteSpecifiersWithElementName(elementName, *$$);
+        }
     }
     | specifier_list {
         $$ = $1;
index 10ed58c..0f62a38 100644 (file)
@@ -13089,6 +13089,13 @@ void CSSParser::addNamespace(const AtomicString& prefix, const AtomicString& uri
 
 QualifiedName CSSParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName)
 {
+    if (prefix.isNull())
+        return QualifiedName(nullAtom, localName, nullAtom); // No namespace. If an element/attribute has a namespace, we won't match it.
+    if (prefix.isEmpty())
+        return QualifiedName(emptyAtom, localName, emptyAtom); // Empty namespace.
+    if (prefix == starAtom)
+        return QualifiedName(prefix, localName, starAtom); // We'll match any namespace.
+
     if (!m_styleSheet)
         return QualifiedName(prefix, localName, m_defaultNamespace);
     return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix));
@@ -13096,15 +13103,20 @@ QualifiedName CSSParser::determineNameInNamespace(const AtomicString& prefix, co
 
 void CSSParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector& specifiers)
 {
-    if (m_defaultNamespace != starAtom || specifiers.isCustomPseudoElement())
-        rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIsForNamespaceRule*/true);
+    if (m_defaultNamespace != starAtom || specifiers.isCustomPseudoElement()) {
+        QualifiedName elementName(nullAtom, starAtom, m_defaultNamespace);
+        rewriteSpecifiersWithElementName(elementName, specifiers, /*tagIsForNamespaceRule*/true);
+    }
 }
 
-void CSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector& specifiers, bool tagIsForNamespaceRule)
+void CSSParser::rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector& specifiers)
 {
-    AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace;
-    QualifiedName tag(namespacePrefix, elementName, determinedNamespace);
+    QualifiedName tag(determineNameInNamespace(namespacePrefix, elementName));
+    rewriteSpecifiersWithElementName(tag, specifiers, false);
+}
 
+void CSSParser::rewriteSpecifiersWithElementName(const QualifiedName& tag, CSSParserSelector& specifiers, bool tagIsForNamespaceRule)
+{
     if (!specifiers.isCustomPseudoElement()) {
         if (tag == anyQName())
             return;
index 9aa69af..fa968f7 100644 (file)
@@ -403,7 +403,8 @@ public:
     void addNamespace(const AtomicString& prefix, const AtomicString& uri);
     QualifiedName determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName);
 
-    void rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector&, bool isNamespacePlaceholder = false);
+    void rewriteSpecifiersWithElementName(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector&);
+    void rewriteSpecifiersWithElementName(const QualifiedName& tagName, CSSParserSelector&, bool isNamespacePlaceholder = false);
     void rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector&);
     std::unique_ptr<CSSParserSelector> rewriteSpecifiers(std::unique_ptr<CSSParserSelector>, std::unique_ptr<CSSParserSelector>);
 
index 57ef4c9..2a2281f 100644 (file)
@@ -668,7 +668,7 @@ String CSSSelector::selectorText(const String& rightSide) const
         } else if (cs->isAttributeSelector()) {
             str.append('[');
             const AtomicString& prefix = cs->attribute().prefix();
-            if (!prefix.isNull()) {
+            if (!prefix.isEmpty()) {
                 str.append(prefix);
                 str.append('|');
             }
index 36aadef..53ae69b 100644 (file)
@@ -173,9 +173,9 @@ class SelectorNeedsNamespaceResolutionFunctor {
 public:
     bool operator()(const CSSSelector* selector)
     {
-        if (selector->match() == CSSSelector::Tag && selector->tagQName().prefix() != nullAtom && selector->tagQName().prefix() != starAtom)
+        if (selector->match() == CSSSelector::Tag && !selector->tagQName().prefix().isEmpty() && selector->tagQName().prefix() != starAtom)
             return true;
-        if (selector->isAttributeSelector() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
+        if (selector->isAttributeSelector() && !selector->attribute().prefix().isEmpty() && selector->attribute().prefix() != starAtom)
             return true;
         return false;
     }
index 9b363b4..128d780 100644 (file)
@@ -279,10 +279,6 @@ void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const At
 
 const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& prefix)
 {
-    if (prefix.isNull())
-        return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
-    if (prefix == starAtom)
-        return starAtom; // We'll match any namespace.
     PrefixNamespaceURIMap::const_iterator it = m_namespaces.find(prefix);
     if (it == m_namespaces.end())
         return nullAtom;