WebCore: Fix for https://bugs.webkit.org/show_bug.cgi?id=29703
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 24 Sep 2009 17:47:32 +0000 (17:47 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 24 Sep 2009 17:47:32 +0000 (17:47 +0000)
Add a function to element to check whether it matches a CSS selector

Reviewed by Dan Bernstein.

Implement Element.webkitMatchesSelector.

* css/CSSSelectorList.cpp:
(WebCore::forEachTagSelector):
(WebCore::forEachSelector):
(WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):
(WebCore::CSSSelectorList::selectorsNeedNamespaceResolution):
* css/CSSSelectorList.h:
Moved code to iterate the CSSSelectorList and determine if any
selectors need namespace resolution from a static function in
Node.cpp to CSSSelectorList so that it can be used by webkitMatchesSelector
as well as querySelector/querySelectorAll.

* dom/Element.cpp:
(WebCore::Element::webkitMatchesSelector):
* dom/Element.h:
* dom/Element.idl:
Implement the new function. Handles exceptional cases identically to
querySelector/querySelectorAll.

* dom/Node.cpp:
(WebCore::Node::querySelector):
(WebCore::Node::querySelectorAll):
Moved selectorsNeedNamespaceResolution to CSSSelectorList from here.

LayoutTests: Update tests for https://bugs.webkit.org/show_bug.cgi?id=29703
Add a function to element to check whether it matches a CSS selector

Reviewed by Dan Bernstein.

Add webkitMatchesSelector to SelectorAPI tests.

* fast/dom/SelectorAPI/caseID-almost-strict-expected.txt:
* fast/dom/SelectorAPI/caseID-almost-strict.html:
* fast/dom/SelectorAPI/caseID-expected.txt:
* fast/dom/SelectorAPI/caseID-strict-expected.txt:
* fast/dom/SelectorAPI/caseID-strict.html:
* fast/dom/SelectorAPI/caseID.html:
* fast/dom/SelectorAPI/caseTag-expected.txt:
* fast/dom/SelectorAPI/caseTag.html:
* fast/dom/SelectorAPI/caseTagX-expected.txt:
* fast/dom/SelectorAPI/caseTagX.xhtml:
* fast/dom/SelectorAPI/detached-element-expected.txt:
* fast/dom/SelectorAPI/not-supported-namespace-in-selector-expected.txt:
* fast/dom/SelectorAPI/not-supported-namespace-in-selector.html:
* fast/dom/SelectorAPI/script-tests/detached-element.js:
* fast/dom/SelectorAPI/script-tests/undefined-null-stringify.js:
* fast/dom/SelectorAPI/script-tests/viewless-document.js:
* fast/dom/SelectorAPI/undefined-null-stringify-expected.txt:
* fast/dom/SelectorAPI/viewless-document-expected.txt:
* fast/dom/Window/window-properties-expected.txt:
* fast/dom/domListEnumeration-expected.txt:
* fast/dom/script-tests/domListEnumeration.js:

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

29 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/SelectorAPI/caseID-almost-strict-expected.txt
LayoutTests/fast/dom/SelectorAPI/caseID-almost-strict.html
LayoutTests/fast/dom/SelectorAPI/caseID-expected.txt
LayoutTests/fast/dom/SelectorAPI/caseID-strict-expected.txt
LayoutTests/fast/dom/SelectorAPI/caseID-strict.html
LayoutTests/fast/dom/SelectorAPI/caseID.html
LayoutTests/fast/dom/SelectorAPI/caseTag-expected.txt
LayoutTests/fast/dom/SelectorAPI/caseTag.html
LayoutTests/fast/dom/SelectorAPI/caseTagX-expected.txt
LayoutTests/fast/dom/SelectorAPI/caseTagX.xhtml
LayoutTests/fast/dom/SelectorAPI/detached-element-expected.txt
LayoutTests/fast/dom/SelectorAPI/not-supported-namespace-in-selector-expected.txt
LayoutTests/fast/dom/SelectorAPI/not-supported-namespace-in-selector.html
LayoutTests/fast/dom/SelectorAPI/script-tests/detached-element.js
LayoutTests/fast/dom/SelectorAPI/script-tests/undefined-null-stringify.js
LayoutTests/fast/dom/SelectorAPI/script-tests/viewless-document.js
LayoutTests/fast/dom/SelectorAPI/undefined-null-stringify-expected.txt
LayoutTests/fast/dom/SelectorAPI/viewless-document-expected.txt
LayoutTests/fast/dom/Window/window-properties-expected.txt
LayoutTests/fast/dom/domListEnumeration-expected.txt
LayoutTests/fast/dom/script-tests/domListEnumeration.js
WebCore/ChangeLog
WebCore/css/CSSSelectorList.cpp
WebCore/css/CSSSelectorList.h
WebCore/dom/Element.cpp
WebCore/dom/Element.h
WebCore/dom/Element.idl
WebCore/dom/Node.cpp

index d9074fe..45687ce 100644 (file)
@@ -1,3 +1,34 @@
+2009-09-24  Sam Weinig  <sam@webkit.org>
+
+        Reviewed by Dan Bernstein.
+
+        Update tests for https://bugs.webkit.org/show_bug.cgi?id=29703
+        Add a function to element to check whether it matches a CSS selector
+
+        Add webkitMatchesSelector to SelectorAPI tests.
+
+        * fast/dom/SelectorAPI/caseID-almost-strict-expected.txt:
+        * fast/dom/SelectorAPI/caseID-almost-strict.html:
+        * fast/dom/SelectorAPI/caseID-expected.txt:
+        * fast/dom/SelectorAPI/caseID-strict-expected.txt:
+        * fast/dom/SelectorAPI/caseID-strict.html:
+        * fast/dom/SelectorAPI/caseID.html:
+        * fast/dom/SelectorAPI/caseTag-expected.txt:
+        * fast/dom/SelectorAPI/caseTag.html:
+        * fast/dom/SelectorAPI/caseTagX-expected.txt:
+        * fast/dom/SelectorAPI/caseTagX.xhtml:
+        * fast/dom/SelectorAPI/detached-element-expected.txt:
+        * fast/dom/SelectorAPI/not-supported-namespace-in-selector-expected.txt:
+        * fast/dom/SelectorAPI/not-supported-namespace-in-selector.html:
+        * fast/dom/SelectorAPI/script-tests/detached-element.js:
+        * fast/dom/SelectorAPI/script-tests/undefined-null-stringify.js:
+        * fast/dom/SelectorAPI/script-tests/viewless-document.js:
+        * fast/dom/SelectorAPI/undefined-null-stringify-expected.txt:
+        * fast/dom/SelectorAPI/viewless-document-expected.txt:
+        * fast/dom/Window/window-properties-expected.txt:
+        * fast/dom/domListEnumeration-expected.txt:
+        * fast/dom/script-tests/domListEnumeration.js:
+
 2009-09-24  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by NOBODY(rollout)
index c52f792..611b8e7 100644 (file)
@@ -2,6 +2,10 @@ PASS document.querySelector('#lower1').textContent is 'lower 1'
 PASS document.querySelector('#LOWER2') is null
 PASS document.querySelector('#UPPER1').textContent is 'UPPER 1'
 PASS document.querySelector('#upper2') is null
+PASS document.getElementById('lower1').webkitMatchesSelector('#lower1') is true
+PASS document.getElementById('lower2').webkitMatchesSelector('#LOWER2') is false
+PASS document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1') is true
+PASS document.getElementById('UPPER2').webkitMatchesSelector('#upper2') is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 7e48d60..c48a6b4 100644 (file)
         shouldBe("document.querySelector('#UPPER1').textContent", "'UPPER 1'");
         shouldBeNull("document.querySelector('#upper2')");
 
+        shouldBeTrue("document.getElementById('lower1').webkitMatchesSelector('#lower1')");
+        shouldBeFalse("document.getElementById('lower2').webkitMatchesSelector('#LOWER2')");
+        shouldBeTrue("document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1')");
+        shouldBeFalse("document.getElementById('UPPER2').webkitMatchesSelector('#upper2')");
+
         var successfullyParsed = true;
     </script>
     <script src="../../js/resources/js-test-post.js"></script>
index ebe23a4..2b60689 100644 (file)
@@ -2,6 +2,10 @@ PASS document.querySelector('#lower1').textContent is 'lower 1'
 PASS document.querySelector('#LOWER2').textContent is 'lower 2'
 PASS document.querySelector('#UPPER1').textContent is 'UPPER 1'
 PASS document.querySelector('#upper2').textContent is 'UPPER 2'
+PASS document.getElementById('lower1').webkitMatchesSelector('#lower1') is true
+PASS document.getElementById('lower2').webkitMatchesSelector('#LOWER2') is true
+PASS document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1') is true
+PASS document.getElementById('UPPER2').webkitMatchesSelector('#upper2') is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index c52f792..611b8e7 100644 (file)
@@ -2,6 +2,10 @@ PASS document.querySelector('#lower1').textContent is 'lower 1'
 PASS document.querySelector('#LOWER2') is null
 PASS document.querySelector('#UPPER1').textContent is 'UPPER 1'
 PASS document.querySelector('#upper2') is null
+PASS document.getElementById('lower1').webkitMatchesSelector('#lower1') is true
+PASS document.getElementById('lower2').webkitMatchesSelector('#LOWER2') is false
+PASS document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1') is true
+PASS document.getElementById('UPPER2').webkitMatchesSelector('#upper2') is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 5bf958b..36e1f7e 100644 (file)
         shouldBe("document.querySelector('#UPPER1').textContent", "'UPPER 1'");
         shouldBeNull("document.querySelector('#upper2')");
 
+        shouldBeTrue("document.getElementById('lower1').webkitMatchesSelector('#lower1')");
+        shouldBeFalse("document.getElementById('lower2').webkitMatchesSelector('#LOWER2')");
+        shouldBeTrue("document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1')");
+        shouldBeFalse("document.getElementById('UPPER2').webkitMatchesSelector('#upper2')");
+
         var successfullyParsed = true;
     </script>
     <script src="../../js/resources/js-test-post.js"></script>
index d429f24..4bf2c65 100644 (file)
         shouldBe("document.querySelector('#UPPER1').textContent", "'UPPER 1'");
         shouldBe("document.querySelector('#upper2').textContent", "'UPPER 2'");
 
+        shouldBeTrue("document.getElementById('lower1').webkitMatchesSelector('#lower1')");
+        shouldBeTrue("document.getElementById('lower2').webkitMatchesSelector('#LOWER2')");
+        shouldBeTrue("document.getElementById('UPPER1').webkitMatchesSelector('#UPPER1')");
+        shouldBeTrue("document.getElementById('UPPER2').webkitMatchesSelector('#upper2')");
+
         var successfullyParsed = true;
     </script>
     <script src="../../js/resources/js-test-post.js"></script>
index b733f30..fe5b148 100644 (file)
@@ -1,5 +1,7 @@
 PASS document.querySelector('div SPAN').textContent is 'lower'
 PASS document.querySelector('div p').textContent is 'UPPER'
+PASS document.getElementById('lower1').webkitMatchesSelector('div SPAN') is true
+PASS document.getElementById('UPPER1').webkitMatchesSelector('div p') is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index a69f510..dba4ff9 100644 (file)
@@ -14,6 +14,9 @@
         shouldBe("document.querySelector('div SPAN').textContent", "'lower'");
         shouldBe("document.querySelector('div p').textContent", "'UPPER'");
 
+        shouldBeTrue("document.getElementById('lower1').webkitMatchesSelector('div SPAN')");
+        shouldBeTrue("document.getElementById('UPPER1').webkitMatchesSelector('div p')");
+
         var successfullyParsed = true;
     </script>
     <script src="../../js/resources/js-test-post.js"></script>
index e24e085..b332440 100644 (file)
@@ -1,4 +1,5 @@
 PASS document.querySelector('div SPAN') is null
+PASS document.getElementById('lower1').webkitMatchesSelector('div SPAN') is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 2233a26..e2107c9 100644 (file)
@@ -18,6 +18,8 @@
     <![CDATA[ 
         shouldBeNull("document.querySelector('div SPAN')");
 
+        shouldBeFalse("document.getElementById('lower1').webkitMatchesSelector('div SPAN')");
+
         var successfullyParsed = true;
     ]]> 
     </script>
index 0855c4e..52d5d65 100644 (file)
@@ -1,4 +1,4 @@
-This tests that querySelector and querySelectorAll work with elements that are not in a document yet.
+This tests that querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) work with elements that are not in a document yet.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
@@ -11,6 +11,8 @@ PASS root.querySelectorAll('#testId').length is 1
 PASS root.querySelectorAll('#testId').item(0) is correctNode
 PASS noChild.querySelector('div') is null
 PASS noChild.querySelectorAll('div').length is 0
+PASS correctNode.webkitMatchesSelector('div') is true
+PASS correctNode.webkitMatchesSelector('#testId') is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 9269ef0..70a9ff3 100644 (file)
@@ -2,26 +2,38 @@ This tests that we throw a NAMESPACE_ERR when parsing a selector string for quer
 
 PASS: document.querySelector('bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelectorAll('bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
+PASS: document.body.webkitMatchesSelector('bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelector('*|pre') did not throw
 PASS: document.querySelectorAll('*|pre') did not throw
+PASS: document.body.webkitMatchesSelector('*|pre') did not throw
 PASS: document.querySelector('|pre') did not throw
 PASS: document.querySelectorAll('|pre') did not throw
+PASS: document.body.webkitMatchesSelector('|pre') did not throw
 PASS: document.querySelector('div bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelectorAll('div bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
+PASS: document.body.webkitMatchesSelector('div bbb|pre') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelector('div *|pre') did not throw
 PASS: document.querySelectorAll('div *|pre') did not throw
+PASS: document.body.webkitMatchesSelector('div *|pre') did not throw
 PASS: document.querySelector('div |pre') did not throw
 PASS: document.querySelectorAll('div |pre') did not throw
+PASS: document.body.webkitMatchesSelector('div |pre') did not throw
 PASS: document.querySelector('[bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelectorAll('[bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
+PASS: document.body.webkitMatchesSelector('[bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelector('[*|name=value]') did not throw
 PASS: document.querySelectorAll('[*|name=value]') did not throw
+PASS: document.body.webkitMatchesSelector('[*|name=value]') did not throw
 PASS: document.querySelector('[|name=value]') did not throw
 PASS: document.querySelectorAll('[|name=value]') did not throw
+PASS: document.body.webkitMatchesSelector('[|name=value]') did not throw
 PASS: document.querySelector('div [bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelectorAll('div [bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
+PASS: document.body.webkitMatchesSelector('div [bbb|name=value]') throws: Error: NAMESPACE_ERR: DOM Exception 14
 PASS: document.querySelector('div [*|name=value]') did not throw
 PASS: document.querySelectorAll('div [*|name=value]') did not throw
+PASS: document.body.webkitMatchesSelector('div [*|name=value]') did not throw
 PASS: document.querySelector('div [|name=value]') did not throw
 PASS: document.querySelectorAll('div [|name=value]') did not throw
+PASS: document.body.webkitMatchesSelector('div [|name=value]') did not throw
 
index 916f6aa..dc53935 100644 (file)
     {
         shouldThrow("document.querySelector('bbb|pre')");
         shouldThrow("document.querySelectorAll('bbb|pre')");
+        shouldThrow("document.body.webkitMatchesSelector('bbb|pre')");
         shouldNotThrow("document.querySelector('*|pre')");
         shouldNotThrow("document.querySelectorAll('*|pre')");
+        shouldNotThrow("document.body.webkitMatchesSelector('*|pre')");
         shouldNotThrow("document.querySelector('|pre')");
         shouldNotThrow("document.querySelectorAll('|pre')");
+        shouldNotThrow("document.body.webkitMatchesSelector('|pre')");
 
         shouldThrow("document.querySelector('div bbb|pre')");
         shouldThrow("document.querySelectorAll('div bbb|pre')");
+        shouldThrow("document.body.webkitMatchesSelector('div bbb|pre')");
         shouldNotThrow("document.querySelector('div *|pre')");
         shouldNotThrow("document.querySelectorAll('div *|pre')");
+        shouldNotThrow("document.body.webkitMatchesSelector('div *|pre')");
         shouldNotThrow("document.querySelector('div |pre')");
         shouldNotThrow("document.querySelectorAll('div |pre')");
+        shouldNotThrow("document.body.webkitMatchesSelector('div |pre')");
 
         shouldThrow("document.querySelector('[bbb|name=value]')");
         shouldThrow("document.querySelectorAll('[bbb|name=value]')");
+        shouldThrow("document.body.webkitMatchesSelector('[bbb|name=value]')");
         shouldNotThrow("document.querySelector('[*|name=value]')");
         shouldNotThrow("document.querySelectorAll('[*|name=value]')");
+        shouldNotThrow("document.body.webkitMatchesSelector('[*|name=value]')");
         shouldNotThrow("document.querySelector('[|name=value]')");
         shouldNotThrow("document.querySelectorAll('[|name=value]')");
+        shouldNotThrow("document.body.webkitMatchesSelector('[|name=value]')");
 
         shouldThrow("document.querySelector('div [bbb|name=value]')");
         shouldThrow("document.querySelectorAll('div [bbb|name=value]')");
+        shouldThrow("document.body.webkitMatchesSelector('div [bbb|name=value]')");
         shouldNotThrow("document.querySelector('div [*|name=value]')");
         shouldNotThrow("document.querySelectorAll('div [*|name=value]')");
+        shouldNotThrow("document.body.webkitMatchesSelector('div [*|name=value]')");
         shouldNotThrow("document.querySelector('div [|name=value]')");
         shouldNotThrow("document.querySelectorAll('div [|name=value]')");
+        shouldNotThrow("document.body.webkitMatchesSelector('div [|name=value]')");
     }
 </script>
 </head>
index 3a4676a..1c27930 100644 (file)
@@ -1,5 +1,5 @@
 description(
-"This tests that querySelector and querySelectorAll work with elements that are not in a document yet."
+"This tests that querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) work with elements that are not in a document yet."
 );
 
 var root = document.createElement('div');
@@ -19,4 +19,7 @@ shouldBe("root.querySelectorAll('#testId').item(0)", "correctNode");
 shouldBeNull("noChild.querySelector('div')");
 shouldBe("noChild.querySelectorAll('div').length", "0");
 
+shouldBeTrue("correctNode.webkitMatchesSelector('div')");
+shouldBeTrue("correctNode.webkitMatchesSelector('#testId')");
+
 var successfullyParsed = true;
index 1ce62a4..0e9127e 100644 (file)
@@ -1,5 +1,5 @@
 description(
-"This tests that the querySelector and querySelectorAll correctly stringify null and undefined to \"null\" and \"undefined\"."
+"This tests that the querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) correctly stringify null and undefined to \"null\" and \"undefined\"."
 );
 
 var root = document.createElement('div');
@@ -17,4 +17,7 @@ shouldBe("document.querySelectorAll(null).item(0)", "nullNode");
 shouldBe("document.querySelectorAll(undefined).length", "1");
 shouldBe("document.querySelectorAll(undefined).item(0)", "undefinedNode");
 
+shouldBeTrue("nullNode.webkitMatchesSelector(null)");
+shouldBeTrue("undefinedNode.webkitMatchesSelector(undefined)");
+
 var successfullyParsed = true;
index fa8ae6d..64061b2 100644 (file)
@@ -1,5 +1,5 @@
 description(
-"This tests that querySelector and querySelectorAll don't crash when used in a viewless document."
+"This tests that querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) don't crash when used in a viewless document."
 );
 
 var testDoc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html");
@@ -10,6 +10,7 @@ testDoc.body.appendChild(testDoc.createElement("span")).id = "s2";
 testDoc.body.appendChild(testDoc.createElement("div")).className = "d1";
 
 var p1 = testDoc.getElementById("p1");
+var s1 = testDoc.getElementById("s1");
 var s2 = testDoc.getElementById("s2");
 var d1 = testDoc.body.lastChild;
 
@@ -19,4 +20,9 @@ shouldBe("testDoc.querySelectorAll('span').item(1)", "s2");
 shouldBe("testDoc.querySelector('.d1')", "d1");
 shouldBe("testDoc.querySelectorAll('p span').length", "1");
 
+shouldBeTrue("p1.webkitMatchesSelector('p')");
+shouldBeTrue("s1.webkitMatchesSelector('p span')");
+shouldBeTrue("s2.webkitMatchesSelector('#s2')");
+shouldBeTrue("d1.webkitMatchesSelector('.d1')");
+
 var successfullyParsed = true;
index 1032a57..6e97ed3 100644 (file)
@@ -1,4 +1,4 @@
-This tests that the querySelector and querySelectorAll correctly stringify null and undefined to "null" and "undefined".
+This tests that the querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) correctly stringify null and undefined to "null" and "undefined".
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
@@ -9,6 +9,8 @@ PASS document.querySelectorAll(null).length is 1
 PASS document.querySelectorAll(null).item(0) is nullNode
 PASS document.querySelectorAll(undefined).length is 1
 PASS document.querySelectorAll(undefined).item(0) is undefinedNode
+PASS nullNode.webkitMatchesSelector(null) is true
+PASS undefinedNode.webkitMatchesSelector(undefined) is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 06ba824..011c114 100644 (file)
@@ -1,4 +1,4 @@
-This tests that querySelector and querySelectorAll don't crash when used in a viewless document.
+This tests that querySelector, querySelectorAll and matchesSelector (webkitMatchesSelector) don't crash when used in a viewless document.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
@@ -8,6 +8,10 @@ PASS testDoc.querySelectorAll('span').length is 2
 PASS testDoc.querySelectorAll('span').item(1) is s2
 PASS testDoc.querySelector('.d1') is d1
 PASS testDoc.querySelectorAll('p span').length is 1
+PASS p1.webkitMatchesSelector('p') is true
+PASS s1.webkitMatchesSelector('p span') is true
+PASS s2.webkitMatchesSelector('#s2') is true
+PASS d1.webkitMatchesSelector('.d1') is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index b906ee9..c34542c 100644 (file)
@@ -121,6 +121,7 @@ window.Audio.prototype.setAttribute [function]
 window.Audio.prototype.setAttributeNS [function]
 window.Audio.prototype.setAttributeNode [function]
 window.Audio.prototype.setAttributeNodeNS [function]
+window.Audio.prototype.webkitMatchesSelector [function]
 window.CDATASection [object CDATASectionConstructor]
 window.CDATASection.prototype [object CDATASectionPrototype]
 window.CDATASection.prototype.ATTRIBUTE_NODE [number]
@@ -734,6 +735,7 @@ window.Element.prototype.setAttribute [function]
 window.Element.prototype.setAttributeNS [function]
 window.Element.prototype.setAttributeNode [function]
 window.Element.prototype.setAttributeNodeNS [function]
+window.Element.prototype.webkitMatchesSelector [function]
 window.Entity [object EntityConstructor]
 window.Entity.prototype [object EntityPrototype]
 window.Entity.prototype.ATTRIBUTE_NODE [number]
index 507bfce..159d697 100644 (file)
@@ -32,7 +32,7 @@ PASS resultArray[2].i is '2'
 PASS resultArray[2].item is namedNodeMap.item(2)
 
 [object HTMLFormElement]
-PASS resultArray.length is 133
+PASS resultArray.length is 134
 PASS resultArray[0].i is '0'
 PASS resultArray[0].item is document.getElementsByTagName('select')[0]
 PASS resultArray[1].i is '1'
@@ -41,7 +41,7 @@ PASS resultArray[2].i is '2'
 PASS resultArray[2].item is document.getElementsByTagName('select')[2]
 
 [object HTMLSelectElement]
-PASS resultArray.length is 139
+PASS resultArray.length is 140
 PASS resultArray[0].i is '0'
 PASS resultArray[0].item is document.getElementsByTagName('option')[0]
 PASS resultArray[1].i is '1'
index afe1023..47cf16f 100644 (file)
@@ -130,7 +130,7 @@ shouldBe("resultArray[2].item", "namedNodeMap.item(2)");
 // HTMLFormElement
 var htmlFormElement = document.getElementsByTagName('form')[0];
 resultArray = iterateList(htmlFormElement);
-shouldBe("resultArray.length", "133");
+shouldBe("resultArray.length", "134");
 shouldBe("resultArray[0].i", "'0'");
 shouldBe("resultArray[0].item", "document.getElementsByTagName('select')[0]");
 shouldBe("resultArray[1].i", "'1'");
@@ -141,7 +141,7 @@ shouldBe("resultArray[2].item", "document.getElementsByTagName('select')[2]");
 // HTMLSelectElement
 var htmlSelectElement = document.getElementsByTagName('select')[0];
 resultArray = iterateList(htmlSelectElement);
-shouldBe("resultArray.length", "139");
+shouldBe("resultArray.length", "140");
 shouldBe("resultArray[0].i", "'0'");
 shouldBe("resultArray[0].item", "document.getElementsByTagName('option')[0]");
 shouldBe("resultArray[1].i", "'1'");
index 910dcec..2a69ff7 100644 (file)
@@ -1,3 +1,35 @@
+2009-09-24  Sam Weinig  <sam@webkit.org>
+
+        Reviewed by Dan Bernstein.
+
+        Fix for https://bugs.webkit.org/show_bug.cgi?id=29703
+        Add a function to element to check whether it matches a CSS selector
+
+        Implement Element.webkitMatchesSelector.
+
+        * css/CSSSelectorList.cpp:
+        (WebCore::forEachTagSelector):
+        (WebCore::forEachSelector):
+        (WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):
+        (WebCore::CSSSelectorList::selectorsNeedNamespaceResolution):
+        * css/CSSSelectorList.h:
+        Moved code to iterate the CSSSelectorList and determine if any
+        selectors need namespace resolution from a static function in
+        Node.cpp to CSSSelectorList so that it can be used by webkitMatchesSelector
+        as well as querySelector/querySelectorAll.
+
+        * dom/Element.cpp:
+        (WebCore::Element::webkitMatchesSelector):
+        * dom/Element.h: 
+        * dom/Element.idl:
+        Implement the new function. Handles exceptional cases identically to
+        querySelector/querySelectorAll.
+
+        * dom/Node.cpp:
+        (WebCore::Node::querySelector):
+        (WebCore::Node::querySelectorAll):
+        Moved selectorsNeedNamespaceResolution to CSSSelectorList from here.
+
 2009-09-24  Vitaly Repeshko  <vitalyr@chromium.org>
 
         Reviewed by Dimitri Glazkov.
index f12d64f..7f82ca4 100644 (file)
@@ -89,4 +89,51 @@ void CSSSelectorList::deleteSelectors()
     }
 }
 
+
+template <typename Functor>
+static bool forEachTagSelector(Functor& functor, CSSSelector* selector)
+{
+    ASSERT(selector);
+
+    do {
+        if (functor(selector))
+            return true;
+        if (CSSSelector* simpleSelector = selector->simpleSelector()) {
+            if (forEachTagSelector(functor, simpleSelector))
+                return true;
+        }
+    } while ((selector = selector->tagHistory()));
+
+    return false;
+}
+
+template <typename Functor>
+static bool forEachSelector(Functor& functor, const CSSSelectorList* selectorList)
+{
+    for (CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(selector)) {
+        if (forEachTagSelector(functor, selector))
+            return true;
+    }
+
+    return false;
+}
+
+class SelectorNeedsNamespaceResolutionFunctor {
+public:
+    bool operator()(CSSSelector* selector)
+    {
+        if (selector->hasTag() && selector->m_tag.prefix() != nullAtom && selector->m_tag.prefix() != starAtom)
+            return true;
+        if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
+            return true;
+        return false;
+    }
+};
+
+bool CSSSelectorList::selectorsNeedNamespaceResolution()
+{
+    SelectorNeedsNamespaceResolutionFunctor functor;
+    return forEachSelector(functor, this);
 }
+
+} // namespace WebCore
index 3518139..9e40ef8 100644 (file)
 
 namespace WebCore {
     
-    class CSSSelectorList : public Noncopyable {
-    public:
-        CSSSelectorList() : m_selectorArray(0) { }
-        ~CSSSelectorList();
-
-        void adopt(CSSSelectorList& list);
-        void adoptSelectorVector(Vector<CSSSelector*>& selectorVector);
-        
-        CSSSelector* first() const { return m_selectorArray ? m_selectorArray : 0; }
-        static CSSSelector* next(CSSSelector* previous) { return previous->isLastInSelectorList() ? 0 : previous + 1; }
-        bool hasOneSelector() const { return m_selectorArray ? m_selectorArray->isLastInSelectorList() : false; }
-        
-    private:
-        void deleteSelectors();
-
-        // End of the array is indicated by m_isLastInSelectorList bit in the last item.
-        CSSSelector* m_selectorArray;
-    };
-
-}
-
-#endif
+class CSSSelectorList : public Noncopyable {
+public:
+    CSSSelectorList() : m_selectorArray(0) { }
+    ~CSSSelectorList();
+
+    void adopt(CSSSelectorList& list);
+    void adoptSelectorVector(Vector<CSSSelector*>& selectorVector);
+    
+    CSSSelector* first() const { return m_selectorArray ? m_selectorArray : 0; }
+    static CSSSelector* next(CSSSelector* previous) { return previous->isLastInSelectorList() ? 0 : previous + 1; }
+    bool hasOneSelector() const { return m_selectorArray ? m_selectorArray->isLastInSelectorList() : false; }
+
+    bool selectorsNeedNamespaceResolution();
+
+private:
+    void deleteSelectors();
+
+    // End of the array is indicated by m_isLastInSelectorList bit in the last item.
+    CSSSelector* m_selectorArray;
+};
+
+} // namespace WebCore
+
+#endif // CSSSelectorList_h
index f04723f..2730a43 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "AXObjectCache.h"
 #include "Attr.h"
+#include "CSSParser.h"
+#include "CSSSelectorList.h"
 #include "CSSStyleSelector.h"
 #include "CString.h"
 #include "ClientRect.h"
@@ -1406,6 +1408,39 @@ unsigned Element::childElementCount() const
     return count;
 }
 
+bool Element::webkitMatchesSelector(const String& selector, ExceptionCode& ec)
+{
+    if (selector.isEmpty()) {
+        ec = SYNTAX_ERR;
+        return false;
+    }
+
+    bool strictParsing = !document()->inCompatMode();
+    CSSParser p(strictParsing);
+
+    CSSSelectorList selectorList;
+    p.parseSelector(selector, document(), selectorList);
+
+    if (!selectorList.first()) {
+        ec = SYNTAX_ERR;
+        return false;
+    }
+
+    // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes.
+    if (selectorList.selectorsNeedNamespaceResolution()) {
+        ec = NAMESPACE_ERR;
+        return false;
+    }
+
+    CSSStyleSelector::SelectorChecker selectorChecker(document(), strictParsing);
+    for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
+        if (selectorChecker.checkSelector(selector, this))
+            return true;
+    }
+
+    return false;
+}
+
 KURL Element::getURLAttribute(const QualifiedName& name) const
 {
 #ifndef NDEBUG
index 4ecf932..d27976a 100644 (file)
@@ -231,6 +231,8 @@ public:
     Element* nextElementSibling() const;
     unsigned childElementCount() const;
 
+    bool webkitMatchesSelector(const String& selectors, ExceptionCode&);
+
     virtual bool isFormControlElement() const { return false; }
     virtual bool isEnabledFormControl() const { return true; }
     virtual bool isReadOnlyFormControl() const { return false; }
index cbb36d9..89eea93 100644 (file)
@@ -110,6 +110,11 @@ module core {
         NodeList querySelectorAll(in DOMString selectors)
             raises(DOMException);
 
+        // WebKit extension, pending specification.
+        boolean webkitMatchesSelector(in DOMString selectors)
+            raises(DOMException);
+
+
 #if !defined(LANGUAGE_COM) || !LANGUAGE_COM
         // ElementTraversal API
         readonly attribute Element firstElementChild;
index 2240dd8..c899f3d 100644 (file)
@@ -1626,52 +1626,6 @@ PassRefPtr<NodeList> Node::getElementsByClassName(const String& classNames)
     return ClassNodeList::create(this, classNames, result.first->second.get());
 }
 
-template <typename Functor>
-static bool forEachTagSelector(Functor& functor, CSSSelector* selector)
-{
-    ASSERT(selector);
-
-    do {
-        if (functor(selector))
-            return true;
-        if (CSSSelector* simpleSelector = selector->simpleSelector()) {
-            if (forEachTagSelector(functor, simpleSelector))
-                return true;
-        }
-    } while ((selector = selector->tagHistory()));
-
-    return false;
-}
-
-template <typename Functor>
-static bool forEachSelector(Functor& functor, const CSSSelectorList& selectorList)
-{
-    for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
-        if (forEachTagSelector(functor, selector))
-            return true;
-    }
-
-    return false;
-}
-
-class SelectorNeedsNamespaceResolutionFunctor {
-public:
-    bool operator()(CSSSelector* selector)
-    {
-        if (selector->hasTag() && selector->m_tag.prefix() != nullAtom && selector->m_tag.prefix() != starAtom)
-            return true;
-        if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
-            return true;
-        return false;
-    }
-};
-
-static bool selectorNeedsNamespaceResolution(const CSSSelectorList& selectorList)
-{
-    SelectorNeedsNamespaceResolutionFunctor functor;
-    return forEachSelector(functor, selectorList);
-}
-
 PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& ec)
 {
     if (selectors.isEmpty()) {
@@ -1690,7 +1644,7 @@ PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode&
     }
 
     // throw a NAMESPACE_ERR if the selector includes any namespace prefixes.
-    if (selectorNeedsNamespaceResolution(querySelectorList)) {
+    if (querySelectorList.selectorsNeedNamespaceResolution()) {
         ec = NAMESPACE_ERR;
         return 0;
     }
@@ -1738,7 +1692,7 @@ PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, ExceptionCo
     }
 
     // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes.
-    if (selectorNeedsNamespaceResolution(querySelectorList)) {
+    if (querySelectorList.selectorsNeedNamespaceResolution()) {
         ec = NAMESPACE_ERR;
         return 0;
     }