Diff
Modified: trunk/LayoutTests/ChangeLog (164923 => 164924)
--- trunk/LayoutTests/ChangeLog 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/LayoutTests/ChangeLog 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,3 +1,13 @@
+2014-03-01 Benjamin Poulain <benja...@webkit.org>
+
+ Optimized querySelector(All) when selector contains #id
+ https://bugs.webkit.org/show_bug.cgi?id=116502
+
+ Reviewed by Andreas Kling.
+
+ * fast/selectors/querySelector-id-filtering-expected.txt: Added.
+ * fast/selectors/querySelector-id-filtering.html: Added.
+
2014-02-28 Alexey Proskuryakov <a...@apple.com>
Node::compareDocumentPosition leaks memory structure
Added: trunk/LayoutTests/fast/selectors/querySelector-id-filtering-expected.txt (0 => 164924)
--- trunk/LayoutTests/fast/selectors/querySelector-id-filtering-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/selectors/querySelector-id-filtering-expected.txt 2014-03-01 22:09:48 UTC (rev 164924)
@@ -0,0 +1,44 @@
+Test various cases when we constrain a selector matching to a subtree selected by #ID
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Missing id.
+PASS document.body.querySelectorAll("#notThere *").length is 0
+
+Trivial filtering.
+PASS document.body.querySelectorAll("#simple li").length is 1
+PASS document.body.querySelectorAll("#simple li")[0].id is "simpleTarget"
+
+ID is adjacent of target.
+PASS document.body.querySelectorAll("#directAdjacentRoot+div li").length is 1
+PASS document.body.querySelectorAll("#directAdjacentRoot+div li")[0].id is "directAdjacentTarget"
+PASS document.body.querySelectorAll("#indirectAdjacentRoot~div li").length is 1
+PASS document.body.querySelectorAll("#indirectAdjacentRoot~div li")[0].id is "indirectAdjacentTarget"
+
+Duplicated IDs.
+PASS document.body.querySelectorAll("#duplicated li").length is 3
+PASS document.body.querySelectorAll("#duplicated li")[0].id is "duplicatedTarget1"
+PASS document.body.querySelectorAll("#duplicated li")[1].id is "duplicatedTarget2"
+PASS document.body.querySelectorAll("#duplicated li")[2].id is "duplicatedTarget3"
+PASS document.body.querySelectorAll("#duplicated+div li").length is 2
+PASS document.body.querySelectorAll("#duplicated+div li")[0].id is "siblingOfDuplicated1"
+PASS document.body.querySelectorAll("#duplicated+div li")[1].id is "siblingOfDuplicated2"
+
+Duplicated IDs with a non-duplicated ancestor.
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li").length is 3
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[0].id is "deduplicatedTarget1"
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[1].id is "deduplicatedTarget2"
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[2].id is "deduplicatedTarget3"
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li").length is 2
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li")[0].id is "siblingOfDeduplicated1"
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li")[1].id is "siblingOfDeduplicated2"
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate+div li").length is 1
+PASS document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate+div li")[0].id is "siblingOfNonDuplicatedParentOfDuplicate"
+
+Sibling of HTML document.
+PASS document.body.querySelectorAll("#htmlDocument~* *").length is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/selectors/querySelector-id-filtering.html (0 => 164924)
--- trunk/LayoutTests/fast/selectors/querySelector-id-filtering.html (rev 0)
+++ trunk/LayoutTests/fast/selectors/querySelector-id-filtering.html 2014-03-01 22:09:48 UTC (rev 164924)
@@ -0,0 +1,138 @@
+<!doctype html>
+<html id=htmlDocument>
+<head>
+<script src=""
+</head>
+<body>
+<div style="display:none">
+ <!-- Simple case -->
+ <div id=simple>
+ <ul>
+ <li id=simpleTarget></li>
+ </ul>
+ </div>
+
+ <!-- Duplicated IDs case -->
+ <div id=duplicated>
+ <ul>
+ <li id=duplicatedTarget1></li>
+ </ul>
+ <div id=duplicated>
+ <ul>
+ <li id=duplicatedTarget2></li>
+ </ul>
+ </div>
+ </div>
+ <div>
+ <ul>
+ <li id=siblingOfDuplicated1></li>
+ </ul>
+ </div>
+
+ <div id=duplicated>
+ <ul>
+ <li id=duplicatedTarget3></li>
+ </ul>
+ </div>
+ <div>
+ <ul>
+ <li id=siblingOfDuplicated2></li>
+ </ul>
+ </div>
+
+ <!-- Duplicated IDs in a non-duplicated ID -->
+ <div id=nonDuplicatedParentOfDuplicate>
+ <div id=deduplicated>
+ <ul>
+ <li id=deduplicatedTarget1></li>
+ </ul>
+ <div id=deduplicated>
+ <ul>
+ <li id=deduplicatedTarget2></li>
+ </ul>
+ </div>
+ </div>
+ <div>
+ <ul>
+ <li id=siblingOfDeduplicated1></li>
+ </ul>
+ </div>
+
+ <div id=deduplicated>
+ <ul>
+ <li id=deduplicatedTarget3></li>
+ </ul>
+ </div>
+ <div>
+ <ul>
+ <li id=siblingOfDeduplicated2></li>
+ </ul>
+ </div>
+ </div>
+ <div>
+ <ul>
+ <li id=siblingOfNonDuplicatedParentOfDuplicate></li>
+ </ul>
+ </div>
+
+ <!-- ID adjacent of target -->
+ <div id=directAdjacentRoot></div>
+ <div>
+ <ul>
+ <li id="directAdjacentTarget"></li>
+ </ul>
+ </div>
+
+ <!-- ID indirect adjacent of target -->
+ <div id=indirectAdjacentRoot></div>
+ <div></div>
+ <div></div>
+ <div>
+ <ul>
+ <li id="indirectAdjacentTarget"></li>
+ </ul>
+ </div>
+</div>
+</body>
+<script>
+description('Test various cases when we constrain a selector matching to a subtree selected by #ID');
+
+debug("Missing id.");
+shouldBe('document.body.querySelectorAll("#notThere *").length', '0');
+
+debug("<br>Trivial filtering.");
+shouldBe('document.body.querySelectorAll("#simple li").length', '1');
+shouldBeEqualToString('document.body.querySelectorAll("#simple li")[0].id', 'simpleTarget');
+
+debug("<br>ID is adjacent of target.");
+shouldBe('document.body.querySelectorAll("#directAdjacentRoot+div li").length', '1');
+shouldBeEqualToString('document.body.querySelectorAll("#directAdjacentRoot+div li")[0].id', 'directAdjacentTarget');
+shouldBe('document.body.querySelectorAll("#indirectAdjacentRoot~div li").length', '1');
+shouldBeEqualToString('document.body.querySelectorAll("#indirectAdjacentRoot~div li")[0].id', 'indirectAdjacentTarget');
+
+debug("<br>Duplicated IDs.");
+shouldBe('document.body.querySelectorAll("#duplicated li").length', '3');
+shouldBeEqualToString('document.body.querySelectorAll("#duplicated li")[0].id', 'duplicatedTarget1');
+shouldBeEqualToString('document.body.querySelectorAll("#duplicated li")[1].id', 'duplicatedTarget2');
+shouldBeEqualToString('document.body.querySelectorAll("#duplicated li")[2].id', 'duplicatedTarget3');
+shouldBe('document.body.querySelectorAll("#duplicated+div li").length', '2');
+shouldBeEqualToString('document.body.querySelectorAll("#duplicated+div li")[0].id', 'siblingOfDuplicated1');
+shouldBeEqualToString('document.body.querySelectorAll("#duplicated+div li")[1].id', 'siblingOfDuplicated2');
+
+debug("<br>Duplicated IDs with a non-duplicated ancestor.");
+shouldBe('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li").length', '3');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[0].id', 'deduplicatedTarget1');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[1].id', 'deduplicatedTarget2');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated li")[2].id', 'deduplicatedTarget3');
+shouldBe('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li").length', '2');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li")[0].id', 'siblingOfDeduplicated1');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate #deduplicated+div li")[1].id', 'siblingOfDeduplicated2');
+shouldBe('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate+div li").length', '1');
+shouldBeEqualToString('document.body.querySelectorAll("#nonDuplicatedParentOfDuplicate+div li")[0].id', 'siblingOfNonDuplicatedParentOfDuplicate');
+
+debug("<br>Sibling of HTML document.");
+shouldBe('document.body.querySelectorAll("#htmlDocument~* *").length', '0');
+
+</script>
+<script src=""
+</html>
Modified: trunk/Source/WebCore/ChangeLog (164923 => 164924)
--- trunk/Source/WebCore/ChangeLog 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/ChangeLog 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,3 +1,42 @@
+2014-03-01 Benjamin Poulain <benja...@webkit.org>
+
+ Optimized querySelector(All) when selector contains #id
+ https://bugs.webkit.org/show_bug.cgi?id=116502
+
+ Reviewed by Andreas Kling.
+
+ Test: fast/selectors/querySelector-id-filtering.html
+
+ The idea of this patch is to perform querySelector on a subtree
+ rooted at the last element with #id matching. For example, if we have the selector
+ "#foobar div a", we start by looking for the element with ID foobar in the TreeScope
+ cache, and start matching the children from there.
+
+ The idea comes from Rune for
+ https://chromium.googlesource.com/chromium/blink/+/1cd83d3588973a02ab15d94b1b05a28620853624
+ but the code as diverged too much so it had to be reimplemented specifically for WebKit.
+
+ * css/CSSSelectorList.cpp:
+ (WebCore::CSSSelectorList::CSSSelectorList):
+ (WebCore::CSSSelectorList::operator=):
+ * css/CSSSelectorList.h:
+ (WebCore::CSSSelectorList::~CSSSelectorList):
+ * css/StyleRule.h:
+ (WebCore::StyleRule::wrapperAdoptSelectorList):
+ (WebCore::StyleRulePage::wrapperAdoptSelectorList):
+ * dom/SelectorQuery.cpp:
+ (WebCore::isSingleTagNameSelector):
+ (WebCore::isSingleClassNameSelector):
+ (WebCore::findIdMatchingType):
+ (WebCore::SelectorDataList::SelectorDataList):
+ (WebCore::filterRootById):
+ (WebCore::SelectorDataList::executeCompiledSimpleSelectorChecker):
+ (WebCore::SelectorDataList::execute):
+ (WebCore::SelectorQuery::SelectorQuery):
+ (WebCore::SelectorQueryCache::add):
+ * dom/SelectorQuery.h:
+ (WebCore::SelectorDataList::SelectorData::SelectorData):
+
2014-02-28 Alexey Proskuryakov <a...@apple.com>
Node::compareDocumentPosition leaks memory structure
Modified: trunk/Source/WebCore/css/CSSSelectorList.cpp (164923 => 164924)
--- trunk/Source/WebCore/css/CSSSelectorList.cpp 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/css/CSSSelectorList.cpp 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2012, 2013, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,11 +32,6 @@
namespace WebCore {
-CSSSelectorList::~CSSSelectorList()
-{
- deleteSelectors();
-}
-
CSSSelectorList::CSSSelectorList(const CSSSelectorList& other)
{
unsigned otherComponentCount = other.componentCount();
@@ -45,11 +40,10 @@
new (NotNull, &m_selectorArray[i]) CSSSelector(other.m_selectorArray[i]);
}
-void CSSSelectorList::adopt(CSSSelectorList& list)
+CSSSelectorList::CSSSelectorList(CSSSelectorList&& other)
+ : m_selectorArray(other.m_selectorArray)
{
- deleteSelectors();
- m_selectorArray = list.m_selectorArray;
- list.m_selectorArray = 0;
+ other.m_selectorArray = nullptr;
}
void CSSSelectorList::adoptSelectorVector(Vector<std::unique_ptr<CSSParserSelector>>& selectorVector)
@@ -97,6 +91,14 @@
return (current - m_selectorArray) + 1;
}
+CSSSelectorList& CSSSelectorList::operator=(CSSSelectorList&& other)
+{
+ deleteSelectors();
+ m_selectorArray = other.m_selectorArray;
+ other.m_selectorArray = nullptr;
+ return *this;
+}
+
void CSSSelectorList::deleteSelectors()
{
if (!m_selectorArray)
Modified: trunk/Source/WebCore/css/CSSSelectorList.h (164923 => 164924)
--- trunk/Source/WebCore/css/CSSSelectorList.h 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/css/CSSSelectorList.h 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,10 +38,10 @@
public:
CSSSelectorList() : m_selectorArray(0) { }
CSSSelectorList(const CSSSelectorList&);
+ CSSSelectorList(CSSSelectorList&&);
- ~CSSSelectorList();
+ ~CSSSelectorList() { deleteSelectors(); }
- void adopt(CSSSelectorList& list);
void adoptSelectorVector(Vector<std::unique_ptr<CSSParserSelector>>& selectorVector);
void adoptSelectorArray(CSSSelector* selectors) { ASSERT(!m_selectorArray); m_selectorArray = selectors; }
@@ -67,6 +67,8 @@
unsigned componentCount() const;
+ CSSSelectorList& operator=(CSSSelectorList&&);
+
private:
void deleteSelectors();
Modified: trunk/Source/WebCore/css/StyleRule.h (164923 => 164924)
--- trunk/Source/WebCore/css/StyleRule.h 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/css/StyleRule.h 2014-03-01 22:09:48 UTC (rev 164924)
@@ -118,7 +118,7 @@
MutableStyleProperties& mutableProperties();
void parserAdoptSelectorVector(Vector<std::unique_ptr<CSSParserSelector>>& selectors) { m_selectorList.adoptSelectorVector(selectors); }
- void wrapperAdoptSelectorList(CSSSelectorList& selectors) { m_selectorList.adopt(selectors); }
+ void wrapperAdoptSelectorList(CSSSelectorList& selectors) { m_selectorList = std::move(selectors); }
void parserAdoptSelectorArray(CSSSelector* selectors) { m_selectorList.adoptSelectorArray(selectors); }
PassRef<StyleRule> copy() const { return adoptRef(*new StyleRule(*this)); }
@@ -173,7 +173,7 @@
MutableStyleProperties& mutableProperties();
void parserAdoptSelectorVector(Vector<std::unique_ptr<CSSParserSelector>>& selectors) { m_selectorList.adoptSelectorVector(selectors); }
- void wrapperAdoptSelectorList(CSSSelectorList& selectors) { m_selectorList.adopt(selectors); }
+ void wrapperAdoptSelectorList(CSSSelectorList& selectors) { m_selectorList = std::move(selectors); }
PassRef<StyleRulePage> copy() const { return adoptRef(*new StyleRulePage(*this)); }
Modified: trunk/Source/WebCore/dom/SelectorQuery.cpp (164923 => 164924)
--- trunk/Source/WebCore/dom/SelectorQuery.cpp 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/dom/SelectorQuery.cpp 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,10 +35,41 @@
namespace WebCore {
-void SelectorDataList::initialize(const CSSSelectorList& selectorList)
+#if !ASSERT_DISABLED
+static bool isSingleTagNameSelector(const CSSSelector& selector)
{
- ASSERT(m_selectors.isEmpty());
+ return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Tag;
+}
+static bool isSingleClassNameSelector(const CSSSelector& selector)
+{
+ return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Class;
+}
+#endif
+
+enum class IdMatchingType : uint8_t {
+ None,
+ Rightmost,
+ Filter
+};
+
+static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
+{
+ bool inRightmost = true;
+ for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
+ if (selector->m_match == CSSSelector::Id) {
+ if (inRightmost)
+ return IdMatchingType::Rightmost;
+ return IdMatchingType::Filter;
+ }
+ if (selector->relation() != CSSSelector::SubSelector)
+ inRightmost = false;
+ }
+ return IdMatchingType::None;
+}
+
+SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
+{
unsigned selectorCount = 0;
for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
selectorCount++;
@@ -46,6 +77,39 @@
m_selectors.reserveInitialCapacity(selectorCount);
for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
m_selectors.uncheckedAppend(SelectorData(selector, SelectorCheckerFastPath::canUse(selector)));
+
+ if (selectorCount == 1) {
+ const CSSSelector& selector = *m_selectors.first().selector;
+ if (selector.isLastInTagHistory()) {
+ switch (selector.m_match) {
+ case CSSSelector::Tag:
+ m_matchType = TagNameMatch;
+ break;
+ case CSSSelector::Class:
+ m_matchType = ClassNameMatch;
+ break;
+ case CSSSelector::Id:
+ m_matchType = RightMostWithIdMatch;
+ break;
+ default:
+ m_matchType = CompilableSingle;
+ break;
+ }
+ } else {
+ switch (findIdMatchingType(selector)) {
+ case IdMatchingType::None:
+ m_matchType = CompilableSingle;
+ break;
+ case IdMatchingType::Rightmost:
+ m_matchType = RightMostWithIdMatch;
+ break;
+ case IdMatchingType::Filter:
+ m_matchType = CompilableSingleWithRootFilter;
+ break;
+ }
+ }
+ } else
+ m_matchType = MultipleSelectorMatch;
}
inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
@@ -157,10 +221,45 @@
SelectorQueryTrait::appendOutputForElement(output, element);
}
-static bool isSingleTagNameSelector(const CSSSelector& selector)
+#if ENABLE(CSS_SELECTOR_JIT)
+static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
{
- return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Tag;
+ if (!rootNode.inDocument())
+ return rootNode;
+ if (rootNode.document().inQuirksMode())
+ return rootNode;
+
+ // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
+ // Thus we can skip the rightmost match.
+ const CSSSelector* selector = &firstSelector;
+ do {
+ ASSERT(selector->m_match != CSSSelector::Id);
+ if (selector->relation() != CSSSelector::SubSelector)
+ break;
+ selector = selector->tagHistory();
+ } while (selector);
+
+ bool inAdjacentChain = false;
+ for (; selector; selector = selector->tagHistory()) {
+ if (selector->m_match == CSSSelector::Id) {
+ const AtomicString& idToMatch = selector->value();
+ if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
+ if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
+ if (inAdjacentChain)
+ searchRoot = searchRoot->parentNode();
+ if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(&rootNode)))
+ return *searchRoot;
+ }
+ }
+ }
+ if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
+ inAdjacentChain = true;
+ else
+ inAdjacentChain = false;
+ }
+ return rootNode;
}
+#endif
template <typename SelectorQueryTrait>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomicString& localName, typename SelectorQueryTrait::OutputType& output)
@@ -215,11 +314,6 @@
}
}
-static bool isSingleClassNameSelector(const CSSSelector& selector)
-{
- return selector.isLastInTagHistory() && selector.m_match == CSSSelector::Class;
-}
-
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
@@ -278,59 +372,91 @@
}
}
}
-
-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 : descendantsOfType<Element>(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
{
- if (m_selectors.size() == 1) {
- const SelectorData& selectorData = m_selectors[0];
- if (const CSSSelector* idSelector = selectorForIdLookup(rootNode, *selectorData.selector))
- executeFastPathForIdSelector<SelectorQueryTrait>(rootNode, selectorData, idSelector, output);
- else if (isSingleTagNameSelector(*selectorData.selector))
- executeSingleTagNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
- else if (isSingleClassNameSelector(*selectorData.selector))
- executeSingleClassNameSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
- else {
+ ContainerNode* searchRootNode = &rootNode;
+ switch (m_matchType) {
+ case RightMostWithIdMatch:
+ if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *m_selectors.first().selector)) {
+ executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
+ break;
+ }
+ FALLTHROUGH;
+ case CompilableSingleWithRootFilter:
+ case CompilableSingle:
+ {
#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, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
- RELEASE_ASSERT(selectorData.compilationStatus != SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
- compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
- }
+ const SelectorData& selectorData = m_selectors.first();
+ ASSERT(m_matchType == RightMostWithIdMatch || selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
- if (compiledSelectorChecker) {
- SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
- executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(rootNode, selectorChecker, output);
- return;
+ JSC::VM* vm = searchRootNode->document().scriptExecutionContext()->vm();
+ selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, vm, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
+ RELEASE_ASSERT(selectorData.compilationStatus != SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
+
+ if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
+ if (m_matchType == CompilableSingle) {
+ m_matchType = CompiledSingle;
+ goto CompiledSingleCase;
}
+ if (m_matchType == CompilableSingleWithRootFilter) {
+ m_matchType = CompiledSingleWithRootFilter;
+ goto CompiledSingleWithRootFilterCase;
+ }
+ goto CompiledSingleCase;
+ }
+ if (m_matchType != RightMostWithIdMatch)
+ m_matchType = SingleSelector;
+ goto SingleSelectorCase;
+ ASSERT_NOT_REACHED();
+ break;
+#else
+ FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
-
- executeSingleSelectorData<SelectorQueryTrait>(rootNode, selectorData, output);
}
- return;
+ case CompiledSingleWithRootFilter:
+#if ENABLE(CSS_SELECTOR_JIT)
+ CompiledSingleWithRootFilterCase:
+ searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
+#endif // ENABLE(CSS_SELECTOR_JIT)
+ FALLTHROUGH;
+ case CompiledSingle:
+#if ENABLE(CSS_SELECTOR_JIT)
+ {
+ CompiledSingleCase:
+ const SelectorData& selectorData = m_selectors.first();
+ void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
+ SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
+ executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output);
+ break;
+ }
+#else
+ FALLTHROUGH;
+#endif // ENABLE(CSS_SELECTOR_JIT)
+ case SingleSelector:
+#if ENABLE(CSS_SELECTOR_JIT)
+ SingleSelectorCase:
+#endif
+ executeSingleSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
+ break;
+ case TagNameMatch:
+ executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
+ break;
+ case ClassNameMatch:
+ executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
+ break;
+ case MultipleSelectorMatch:
+ executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
+ break;
}
- executeSingleMultiSelectorData<SelectorQueryTrait>(rootNode, output);
}
-SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList)
+SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
: m_selectorList(selectorList)
+ , m_selectors(m_selectorList)
{
- m_selectors.initialize(m_selectorList);
}
SelectorQuery* SelectorQueryCache::add(const String& selectors, Document& document, ExceptionCode& ec)
@@ -358,7 +484,7 @@
if (m_entries.size() == maximumSelectorQueryCacheSize)
m_entries.remove(m_entries.begin());
- return m_entries.add(selectors, std::make_unique<SelectorQuery>(selectorList)).iterator->value.get();
+ return m_entries.add(selectors, std::make_unique<SelectorQuery>(std::move(selectorList))).iterator->value.get();
}
void SelectorQueryCache::invalidate()
Modified: trunk/Source/WebCore/dom/SelectorQuery.h (164923 => 164924)
--- trunk/Source/WebCore/dom/SelectorQuery.h 2014-03-01 19:57:40 UTC (rev 164923)
+++ trunk/Source/WebCore/dom/SelectorQuery.h 2014-03-01 22:09:48 UTC (rev 164924)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -46,21 +46,25 @@
class SelectorDataList {
public:
- void initialize(const CSSSelectorList&);
+ explicit SelectorDataList(const CSSSelectorList&);
bool matches(Element&) const;
RefPtr<NodeList> queryAll(ContainerNode& rootNode) const;
Element* queryFirst(ContainerNode& rootNode) const;
private:
struct SelectorData {
- SelectorData(const CSSSelector* selector, bool isFastCheckable) : selector(selector), isFastCheckable(isFastCheckable) { }
+ SelectorData(const CSSSelector* selector, bool isFastCheckable)
+ : selector(selector)
+ , isFastCheckable(isFastCheckable)
+ {
+ }
+
const CSSSelector* selector;
- bool isFastCheckable;
-
#if ENABLE(CSS_SELECTOR_JIT)
+ mutable JSC::MacroAssemblerCodeRef compiledSelectorCodeRef;
mutable SelectorCompilationStatus compilationStatus;
- mutable JSC::MacroAssemblerCodeRef compiledSelectorCodeRef;
#endif // ENABLE(CSS_SELECTOR_JIT)
+ bool isFastCheckable;
};
bool selectorMatches(const SelectorData&, Element&, const ContainerNode& rootNode) const;
@@ -73,10 +77,20 @@
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;
+ mutable enum MatchType {
+ CompilableSingle,
+ CompilableSingleWithRootFilter,
+ CompiledSingle,
+ CompiledSingleWithRootFilter,
+ SingleSelector,
+ RightMostWithIdMatch,
+ TagNameMatch,
+ ClassNameMatch,
+ MultipleSelectorMatch,
+ } m_matchType;
};
class SelectorQuery {
@@ -84,14 +98,14 @@
WTF_MAKE_FAST_ALLOCATED;
public:
- explicit SelectorQuery(const CSSSelectorList&);
+ explicit SelectorQuery(CSSSelectorList&&);
bool matches(Element&) const;
RefPtr<NodeList> queryAll(ContainerNode& rootNode) const;
Element* queryFirst(ContainerNode& rootNode) const;
private:
+ CSSSelectorList m_selectorList;
SelectorDataList m_selectors;
- CSSSelectorList m_selectorList;
};
class SelectorQueryCache {