Commit b2d9efda authored by benjamin@webkit.org's avatar benjamin@webkit.org

Use the Selector Code Generator for matching in SelectorQuery

https://bugs.webkit.org/show_bug.cgi?id=126185

Reviewed by Ryosuke Niwa.

Source/WebCore: 

Compile selectors on demand and use the generated binary to perform
element matching in SelectorQuery.

Tests: fast/selectors/querySelector-long-adjacent-backtracking.html
       fast/selectors/querySelector-long-child-backtracking.html
       fast/selectors/querySelector-mixed-child-adjacent-backtracking.html
       fast/selectors/querySelector-multiple-simple-child-backtracking.html
       fast/selectors/querySelector-simple-adjacent-backtracking.html
       fast/selectors/querySelector-simple-child-backtracking.html

* dom/SelectorQuery.cpp:
(WebCore::SelectorDataList::executeCompiledSimpleSelectorChecker):
(WebCore::SelectorDataList::executeCompiledSelectorCheckerWithContext):
(WebCore::SelectorDataList::execute):
* dom/SelectorQuery.h:

LayoutTests: 

Add some tests for longer backtracking cases typically not covered by the other tests.

* fast/selectors/querySelector-long-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-long-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-long-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-long-child-backtracking.html: Added.
* fast/selectors/querySelector-mixed-child-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-mixed-child-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-multiple-simple-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-multiple-simple-child-backtracking.html: Added.
* fast/selectors/querySelector-simple-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-simple-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-simple-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-simple-child-backtracking.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@161839 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 800443be
2014-01-12 Benjamin Poulain <benjamin@webkit.org>
Use the Selector Code Generator for matching in SelectorQuery
https://bugs.webkit.org/show_bug.cgi?id=126185
Reviewed by Ryosuke Niwa.
Add some tests for longer backtracking cases typically not covered by the other tests.
* fast/selectors/querySelector-long-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-long-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-long-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-long-child-backtracking.html: Added.
* fast/selectors/querySelector-mixed-child-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-mixed-child-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-multiple-simple-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-multiple-simple-child-backtracking.html: Added.
* fast/selectors/querySelector-simple-adjacent-backtracking-expected.txt: Added.
* fast/selectors/querySelector-simple-adjacent-backtracking.html: Added.
* fast/selectors/querySelector-simple-child-backtracking-expected.txt: Added.
* fast/selectors/querySelector-simple-child-backtracking.html: Added.
2014-01-12 Andreas Kling <akling@apple.com>
REGRESSION(r160806): line-height is not applied when only present in :link style.
Test backtracking with a long sequence of direct sibling relations.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("li+div+li+blockquote+li~span").length is 1
PASS document.querySelectorAll("li+div+li+blockquote+li~span")[0].id is "target"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<ul>
<!-- Correct sequence. -->
<li></li>
<div></div>
<li></li>
<blockquote></blockquote>
<li></li>
<!-- Lacks the ending <li>. -->
<li></li>
<div></div>
<li></li>
<blockquote></blockquote>
<!-- Lacks the <blockquote>. -->
<li></li>
<div></div>
<li></li>
<li></li>
<!-- Lacks the middle <li>. -->
<li></li>
<div></div>
<blockquote></blockquote>
<li></li>
<!-- Lacks the <div>. -->
<li></li>
<li></li>
<blockquote></blockquote>
<li></li>
<span id=target>Target</span>
<div>
</ul>
</div>
</body>
<script>
description('Test backtracking with a long sequence of direct sibling relations.');
shouldBe('document.querySelectorAll("li+div+li+blockquote+li~span").length', '1');
shouldBeEqualToString('document.querySelectorAll("li+div+li+blockquote+li~span")[0].id', 'target');
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
Test backtracking with multiple child selector relations.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("ul>li>div>pre span").length is 3
PASS document.querySelectorAll("ul>li>div>pre span")[0].id is "target1"
PASS document.querySelectorAll("ul>li>div>pre span")[1].id is "target2"
PASS document.querySelectorAll("ul>li>div>pre span")[2].id is "target3"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<!-- Fails on the <li> tag. -->
<ul>
<li>
<div>
<div>
<pre>
<blockquote><span>Fail.</span></blockquote>
</pre>
</div>
</div>
</li>
</ul>
<!-- Fails on the <ul> tag. -->
<div>
<li>
<div>
<pre>
<blockquote><span>Fail.</span></blockquote>
</pre>
</div>
</li>
</div>
<!-- Simple matches. -->
<ul>
<li>
<div>
<pre>
<blockquote><span id="target1">Target 1</span></blockquote>
</pre>
</div>
</li>
</ul>
<ul>
<li>
<div>
<pre>
<pre>
<blockquote><span id="target2">Target 2</span></blockquote>
</pre>
</pre>
</div>
</li>
</ul>
<!-- Multiple failures before a match. -->
<ul>
<li>
<div>
<pre>
<!-- This subtree lacks the ul. -->
<li>
<div>
<div>
<!-- This subtree lacks the li. -->
<div>
<div>
<div>
<blockquote><span id="target3">Target 3</span></blockquote>
</div>
</div>
</div>
</div>
</div>
</li>
</pre>
</div>
</li>
</ul>
</div>
</body>
<script>
description('Test backtracking with multiple child selector relations.');
shouldBe('document.querySelectorAll("ul>li>div>pre span").length', '3');
shouldBeEqualToString('document.querySelectorAll("ul>li>div>pre span")[0].id', 'target1');
shouldBeEqualToString('document.querySelectorAll("ul>li>div>pre span")[1].id', 'target2');
shouldBeEqualToString('document.querySelectorAll("ul>li>div>pre span")[2].id', 'target3');
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
Test backtracking of adjacent and child relation mixed together.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p").length is 8
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[0].id is "target1"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[1].id is "target2"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[2].id is "target3"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[3].id is "target4"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[4].id is "target5"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[5].id is "target6"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[6].id is "target7"
PASS document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[7].id is "target8"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<!-- Direct matching, no need to backtrack. -->
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p></p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li><p id="target1">Target 1</p></li>
</ul>
</blockquote>
<!-- A first block lacks the <p>. -->
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p></p>
<div></div>
<blockquote></blockquote>
<table></table>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li><p id="target2">Target 2</p></li>
</ul>
</blockquote>
<!-- A second block lacks the <h1>. -->
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p></p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li>
<h2></h2>
<blockquote>
<p id="target3">Target 3</p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li><p id="target4">Target 4</p></li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
<!-- A second block lacks the <div>. -->
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p></p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li>
<table></table>
<h1></h1>
<blockquote>
<p id="target5">Target 5</p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li><p id="target6">Target 6</p></li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
<!-- A third block lacks the div. -->
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p></p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li>
<div></div>
<table></table>
<h1></h1>
<blockquote>
<p id="target7">Target 7</p>
<div></div>
<blockquote></blockquote>
<table></table>
<ul>
<li><p id="target8">Target 8</p></li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</div>
</body>
<script>
description('Test backtracking of adjacent and child relation mixed together.');
shouldBe('document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p").length', '8');
for (var i = 0; i < document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p").length; ++i)
shouldBeEqualToString('document.querySelectorAll("html>head+body div>div~h1+blockquote>p+div+blockquote~ul p")[' + i +'].id', 'target' + (i + 1));
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
Test backtracking with multiple chains of child relations.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("div>ul>li>div ul>li ul>li p").length is 1
PASS document.querySelectorAll("div>ul>li>div ul>li ul>li p")[0].id is "target"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<div>
<ul>
<li>
<div>
<ul>
<li>
<div>
<div>
<ul>
<li>
<p id="target"><span>Target.</span></p>
</li>
</ul>
</div>
<div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</body>
<script>
description('Test backtracking with multiple chains of child relations.');
shouldBe('document.querySelectorAll("div>ul>li>div ul>li ul>li p").length', '1');
shouldBeEqualToString('document.querySelectorAll("div>ul>li>div ul>li ul>li p")[0].id', 'target');
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
Test backtracking with a single direct adjacent relation.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("blockquote+li~li").length is 2
PASS document.querySelectorAll("blockquote+li~li")[0].id is "target1"
PASS document.querySelectorAll("blockquote+li~li")[1].id is "target2"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<ul>
<li></li>
<div></div>
<li></li>
<blockquote></blockquote>
<li></li>
<li id=target1>Target 1</li>
<li id=target2>Target 2</li>
</ul>
</div>
</body>
<script>
description('Test backtracking with a single direct adjacent relation.');
shouldBe('document.querySelectorAll("blockquote+li~li").length', '2');
shouldBeEqualToString('document.querySelectorAll("blockquote+li~li")[0].id', 'target1');
shouldBeEqualToString('document.querySelectorAll("blockquote+li~li")[1].id', 'target2');
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
Test backtracking with a single child selector relation.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS document.querySelectorAll("ul>li p").length is 2
PASS document.querySelectorAll("ul>li p")[0].id is "target1"
PASS document.querySelectorAll("ul>li p")[1].id is "target2"
PASS successfullyParsed is true
TEST COMPLETE
<!doctype html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<div style="display:none">
<ul>
<li></li>
<li>
<ol>
<li></li>
<li><p id="target1">Target 1</p></li>
<li></li>
</ol>
</li>
<li></li>
</ul>
<ul>
<li>
<li>
<li><p id="target2">Target 2</p></li>
</li>
</li>
</ul>
</div>
</body>
<script>
description('Test backtracking with a single child selector relation.');
shouldBe('document.querySelectorAll("ul>li p").length', '2');
shouldBeEqualToString('document.querySelectorAll("ul>li p")[0].id', 'target1');
shouldBeEqualToString('document.querySelectorAll("ul>li p")[1].id', 'target2');
</script>
<script src="../../resources/js-test-post.js"></script>
</html>
2014-01-12 Benjamin Poulain <benjamin@webkit.org>
Use the Selector Code Generator for matching in SelectorQuery
https://bugs.webkit.org/show_bug.cgi?id=126185
Reviewed by Ryosuke Niwa.
Compile selectors on demand and use the generated binary to perform
element matching in SelectorQuery.
Tests: fast/selectors/querySelector-long-adjacent-backtracking.html
fast/selectors/querySelector-long-child-backtracking.html
fast/selectors/querySelector-mixed-child-adjacent-backtracking.html
fast/selectors/querySelector-multiple-simple-child-backtracking.html
fast/selectors/querySelector-simple-adjacent-backtracking.html
fast/selectors/querySelector-simple-child-backtracking.html
* dom/SelectorQuery.cpp:
(WebCore::SelectorDataList::executeCompiledSimpleSelectorChecker):
(WebCore::SelectorDataList::executeCompiledSelectorCheckerWithContext):
(WebCore::SelectorDataList::execute):
* dom/SelectorQuery.h:
2014-01-12 Anders Carlsson <andersca@apple.com>
Simplify creation of XMLHttpRequestStaticData
......@@ -29,6 +29,7 @@
#if ENABLE(CSS_SELECTOR_JIT)
#include "SelectorChecker.h"
#include <JavaScriptCore/MacroAssemblerCodeRef.h>
namespace JSC {
class MacroAssemblerCodeRef;
......
......@@ -266,6 +266,32 @@ ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const Contai
}
}
#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& rootNode, SelectorCompiler::SimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output) const
{
for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
if (selectorChecker(&element)) {
SelectorQueryTrait::appendOutputForElement(output, &element);
if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
return;
}
}
}
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithContext(const ContainerNode& rootNode, SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker, const SelectorCompiler::CheckingContext& context, typename SelectorQueryTrait::OutputType& output) const
{
for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
if (selectorChecker(&element, &context)) {
SelectorQueryTrait::appendOutputForElement(output, &element);
if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
return;
}
}
}
#endif // ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
......@@ -277,8 +303,33 @@ ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename S
executeSingleTagNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
else if (isSingleClassNameSelector(*selectorData.selector))
executeSingleClassNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
else
else {
#if ENABLE(CSS_SELECTOR_JIT)
void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
if (!compiledSelectorChecker && selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled) {
JSC::VM* vm = rootNode.document().scriptExecutionContext()->vm();
selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, vm, selectorData.compiledSelectorCodeRef);
}
if (compiledSelectorChecker) {
if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(rootNode, selectorChecker, output);
} else {
ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
SelectorCompiler::CheckingContext context;
context.elementStyle = nullptr;
context.resolvingMode = SelectorChecker::QueryingRules;
executeCompiledSelectorCheckerWithContext<SelectorQueryTrait>(rootNode, selectorChecker, context, output);
}
return;
}
#endif // ENABLE(CSS_SELECTOR_JIT)
executeSingleSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
}
return;
}
executeSingleMultiSelectorData<SelectorQueryTrait>(rootNode, output);
......
......@@ -28,6 +28,7 @@
#include "CSSSelectorList.h"
#include "NodeList.h"
#include "SelectorCompiler.h"
#include <wtf/HashMap.h>
#include <wtf/Vector.h>
#include <wtf/text/AtomicStringHash.h>
......@@ -55,6 +56,11 @@ private:
SelectorData(const CSSSelector* selector, bool isFastCheckable) : selector(selector), isFastCheckable(isFastCheckable) { }
const CSSSelector* selector;
bool isFastCheckable;
#if ENABLE(CSS_SELECTOR_JIT)
mutable SelectorCompilationStatus compilationStatus;
mutable JSC::MacroAssemblerCodeRef compiledSelectorCodeRef;
#endif // ENABLE(CSS_SELECTOR_JIT)
};
bool selectorMatches(const SelectorData&, Element&, const ContainerNode& rootNode) const;
......@@ -65,6 +71,10 @@ private:
template <typename SelectorQueryTrait> void executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const;
template <typename SelectorQueryTrait> void executeSingleSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const;
template <typename SelectorQueryTrait> void executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const;
#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait> void executeCompiledSimpleSelectorChecker(const ContainerNode& rootNode, SelectorCompiler::SimpleSelectorChecker, typename SelectorQueryTrait::OutputType&) const;
template <typename SelectorQueryTrait> void executeCompiledSelectorCheckerWithContext(const ContainerNode& rootNode, SelectorCompiler::SelectorCheckerWithCheckingContext, const SelectorCompiler::CheckingContext&, typename SelectorQueryTrait::OutputType&) const;
#endif // ENABLE(CSS_SELECTOR_JIT)
Vector<SelectorData> m_selectors;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment