Title: [164924] trunk
Revision
164924
Author
benja...@webkit.org
Date
2014-03-01 14:09:48 -0800 (Sat, 01 Mar 2014)

Log Message

Optimized querySelector(All) when selector contains #id
https://bugs.webkit.org/show_bug.cgi?id=116502

Reviewed by Andreas Kling.

Source/WebCore: 

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):

LayoutTests: 

* fast/selectors/querySelector-id-filtering-expected.txt: Added.
* fast/selectors/querySelector-id-filtering.html: Added.

Modified Paths

Added Paths

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 {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to