Web Inspector: make search in DOM panel scale.
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Nov 2011 15:22:36 +0000 (15:22 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Nov 2011 15:22:36 +0000 (15:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=72118

Reviewed by Yury Semikhatsky.

Source/WebCore:

* inspector/Inspector.json:
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::InspectorDOMAgent):
(WebCore::InspectorDOMAgent::reset):
(WebCore::InspectorDOMAgent::performSearch):
(WebCore::InspectorDOMAgent::getSearchResults):
(WebCore::InspectorDOMAgent::discardSearchResults):
* inspector/InspectorDOMAgent.h:
* inspector/front-end/DOMAgent.js:
(WebInspector.DOMAgent.prototype.performSearch.callback):
(WebInspector.DOMAgent.prototype.performSearch):
(WebInspector.DOMAgent.prototype.searchResult.mycallback):
(WebInspector.DOMAgent.prototype.searchResult):
(WebInspector.DOMAgent.prototype.cancelSearch):
(WebInspector.DOMDispatcher.prototype.childNodeRemoved):
* inspector/front-end/ElementsPanel.js:
(WebInspector.ElementsPanel.prototype.searchCanceled):
(WebInspector.ElementsPanel.prototype.performSearch.resultCountCallback):
(WebInspector.ElementsPanel.prototype.performSearch):
(WebInspector.ElementsPanel.prototype.jumpToNextSearchResult):
(WebInspector.ElementsPanel.prototype.jumpToPreviousSearchResult):
(WebInspector.ElementsPanel.prototype._highlightCurrentSearchResult.):
(WebInspector.ElementsPanel.prototype._highlightCurrentSearchResult):
(WebInspector.ElementsPanel.prototype._hideSearchHighlights):
* inspector/front-end/NetworkPanel.js:
(WebInspector.NetworkLogView.prototype._highlightNthMatchedResource):
* inspector/front-end/ResourcesPanel.js:
(WebInspector.ResourcesPanel.prototype._showSearchResult.callback):
(WebInspector.ResourcesPanel.prototype._showSearchResult):
* inspector/front-end/ScriptsPanel.js:
(WebInspector.ScriptsPanel.prototype.performSearch.finishedCallback):
(WebInspector.ScriptsPanel.prototype.performSearch):
(WebInspector.ScriptsPanel.prototype.jumpToNextSearchResult):
(WebInspector.ScriptsPanel.prototype.jumpToPreviousSearchResult):
* inspector/front-end/SearchController.js:
(WebInspector.SearchController.prototype._updateSearchMatchesCountAndCurrentMatchIndex):

LayoutTests:

* inspector/elements/elements-panel-search-expected.txt:
* inspector/elements/elements-panel-search.html:

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/elements/elements-panel-search-expected.txt
LayoutTests/inspector/elements/elements-panel-search.html
Source/WebCore/ChangeLog
Source/WebCore/inspector/Inspector.json
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebCore/inspector/InspectorDOMAgent.h
Source/WebCore/inspector/front-end/DOMAgent.js
Source/WebCore/inspector/front-end/ElementsPanel.js
Source/WebCore/inspector/front-end/NetworkPanel.js
Source/WebCore/inspector/front-end/ResourcesPanel.js
Source/WebCore/inspector/front-end/ScriptsPanel.js
Source/WebCore/inspector/front-end/SearchController.js

index b0c927c..4fd3f22 100644 (file)
@@ -1,3 +1,13 @@
+2011-11-11  Pavel Feldman  <pfeldman@google.com>
+
+        Web Inspector: make search in DOM panel scale.
+        https://bugs.webkit.org/show_bug.cgi?id=72118
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/elements/elements-panel-search-expected.txt:
+        * inspector/elements/elements-panel-search.html:
+
 2011-11-11  Alexander Pavlov  <apavlov@chromium.org>
 
         Web Inspector: Option to configure indentation of pretty-printed Javascript
index d7eeef5..4747a90 100644 (file)
@@ -2,14 +2,30 @@ Tests that elements panel search is returning proper results.
 
 FooBar
 
-====================================
-Results:
-FooBar
-FooBar
-<input value="InputVal">
-<input value="InputVal">
-<input value="InputVal">
-<input value="InputVal">
-<input value="InputVal">
-<div attr="foo"></div>
+
+Running: testSetUp
+
+Running: testPlainText
+F o o B a r
+
+Running: testPartialText
+F o o B a r
+
+Running: testStartTag
+< i n p u t   v a l u e = " I n p u t V a l " >
+
+Running: testPartialTag
+< i n p u t   v a l u e = " I n p u t V a l " >
+
+Running: testExactAttributeName
+< i n p u t   v a l u e = " I n p u t V a l " >
+
+Running: testExactAttributeVal_ue
+< i n p u t   v a l u e = " I n p u t V a l " >
+
+Running: testPartialAttributeVal_ue
+< i n p u t   v a l u e = " I n p u t V a l " >
+
+Running: testXPathAttribute
+< d i v   a t t r = " f o o " > < / d i v >
 
index 0bed42c..46ad330 100644 (file)
@@ -7,56 +7,73 @@ function test()
 {
     WebInspector.showPanel("elements");
 
-    var searchResults = [];
-    function addSearchResult(error, markupValue)
+    function searchCallback(next, resultCount)
     {
-        if (error)
-            return;
-
-        if (markupValue.indexOf("ter" + "mi" + "nator") !== -1) {
-            InspectorTest.addResult("====================================");
-            InspectorTest.addResult("Results:");
-            InspectorTest.addResults(searchResults);
-            InspectorTest.completeTest();
-            return;
-         }
-        searchResults.push(markupValue);
-    }
+        for (var i = 0; i < resultCount; ++i)
+            WebInspector.domAgent.searchResult(i, searchResultCallback.bind(this, i + 1 === resultCount));
 
-    function addNodesToSearchResult(nodeIds)
-    {
-        for (var i = 0; i < nodeIds.length; ++i) {
-            var node = WebInspector.domAgent.nodeForId(nodeIds[i]);
-            if (node.nodeType() === Node.TEXT_NODE)
-                searchResults.push(node.nodeValue());
-            else
-                node.getOuterHTML(addSearchResult);
+        function searchResultCallback(isLastItem, node)
+        {
+            node.getOuterHTML(addSearchResult.bind(this, isLastItem));
+        }
+
+        function addSearchResult(isLastItem, error, markupValue)
+        {
+            InspectorTest.addResult(markupValue.split("").join(" "));
+            if (isLastItem) {
+                WebInspector.domAgent.cancelSearch();
+                next();
+            }
         }
     }
 
-    WebInspector.domAgent.requestDocument(step1);
+    InspectorTest.runTestSuite([
+        function testSetUp(next)
+        {
+            WebInspector.domAgent.requestDocument(next);
+        },
+
+        function testPlainText(next)
+        {
+            WebInspector.domAgent.performSearch("Foo" + "Bar", searchCallback.bind(this, next));
+        },
+
+        function testPartialText(next)
+        {
+            WebInspector.domAgent.performSearch("oo" + "Ba", searchCallback.bind(this, next));
+        },
+
+        function testStartTag(next)
+        {
+            WebInspector.domAgent.performSearch("<inpu" + "t", searchCallback.bind(this, next));
+        },
+
+        function testPartialTag(next)
+        {
+            WebInspector.domAgent.performSearch("npu" + "t", searchCallback.bind(this, next));
+        },
+
+        function testExactAttributeName(next)
+        {
+            WebInspector.domAgent.performSearch("valu" + "e", searchCallback.bind(this, next));
+        },
+
+        function testExactAttributeVal_ue(next)
+        {
+            WebInspector.domAgent.performSearch("In" + "putVa" + "l", searchCallback.bind(this, next));
+        },
+
+        function testPartialAttributeVal_ue(next)
+        {
+            WebInspector.domAgent.performSearch("n" + "putVa" + "l", searchCallback.bind(this, next));
+        },
+
+        function testXPathAttribute(next)
+        {
+            WebInspector.domAgent.performSearch("//html" + "//@attr", searchCallback.bind(this, next));
+        }
+    ]);
 
-    function step1()
-    {
-        // Plain text.
-        WebInspector.domAgent.performSearch("Foo" + "Bar", addNodesToSearchResult, true);
-        // Partial text.
-        WebInspector.domAgent.performSearch("oo" + "Ba", addNodesToSearchResult, true);
-        // Start tag.
-        WebInspector.domAgent.performSearch("<inpu" + "t", addNodesToSearchResult, true);
-        // Partial tag.
-        WebInspector.domAgent.performSearch("npu" + "t", addNodesToSearchResult, true);
-        // Exact attribute name.
-        WebInspector.domAgent.performSearch("valu" + "e", addNodesToSearchResult, true);
-        // Exact attribute val<>ue.
-        WebInspector.domAgent.performSearch("In" + "putVa" + "l", addNodesToSearchResult, true);
-        // Partial attribute val<>ue.
-        WebInspector.domAgent.performSearch("n" + "putVa" + "l", addNodesToSearchResult, true);
-        // XPath attribute.
-        WebInspector.domAgent.performSearch("//html" + "//@attr", addNodesToSearchResult, true);
-        // Terminator.
-        WebInspector.domAgent.performSearch("ter" + "mi" + "nator", addNodesToSearchResult, true);
-    }
 }
 </script>
 </head>
index b4447b3..aeec82f 100644 (file)
@@ -1,3 +1,47 @@
+2011-11-11  Pavel Feldman  <pfeldman@google.com>
+
+        Web Inspector: make search in DOM panel scale.
+        https://bugs.webkit.org/show_bug.cgi?id=72118
+
+        Reviewed by Yury Semikhatsky.
+
+        * inspector/Inspector.json:
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::InspectorDOMAgent):
+        (WebCore::InspectorDOMAgent::reset):
+        (WebCore::InspectorDOMAgent::performSearch):
+        (WebCore::InspectorDOMAgent::getSearchResults):
+        (WebCore::InspectorDOMAgent::discardSearchResults):
+        * inspector/InspectorDOMAgent.h:
+        * inspector/front-end/DOMAgent.js:
+        (WebInspector.DOMAgent.prototype.performSearch.callback):
+        (WebInspector.DOMAgent.prototype.performSearch):
+        (WebInspector.DOMAgent.prototype.searchResult.mycallback):
+        (WebInspector.DOMAgent.prototype.searchResult):
+        (WebInspector.DOMAgent.prototype.cancelSearch):
+        (WebInspector.DOMDispatcher.prototype.childNodeRemoved):
+        * inspector/front-end/ElementsPanel.js:
+        (WebInspector.ElementsPanel.prototype.searchCanceled):
+        (WebInspector.ElementsPanel.prototype.performSearch.resultCountCallback):
+        (WebInspector.ElementsPanel.prototype.performSearch):
+        (WebInspector.ElementsPanel.prototype.jumpToNextSearchResult):
+        (WebInspector.ElementsPanel.prototype.jumpToPreviousSearchResult):
+        (WebInspector.ElementsPanel.prototype._highlightCurrentSearchResult.):
+        (WebInspector.ElementsPanel.prototype._highlightCurrentSearchResult):
+        (WebInspector.ElementsPanel.prototype._hideSearchHighlights):
+        * inspector/front-end/NetworkPanel.js:
+        (WebInspector.NetworkLogView.prototype._highlightNthMatchedResource):
+        * inspector/front-end/ResourcesPanel.js:
+        (WebInspector.ResourcesPanel.prototype._showSearchResult.callback):
+        (WebInspector.ResourcesPanel.prototype._showSearchResult):
+        * inspector/front-end/ScriptsPanel.js:
+        (WebInspector.ScriptsPanel.prototype.performSearch.finishedCallback):
+        (WebInspector.ScriptsPanel.prototype.performSearch):
+        (WebInspector.ScriptsPanel.prototype.jumpToNextSearchResult):
+        (WebInspector.ScriptsPanel.prototype.jumpToPreviousSearchResult):
+        * inspector/front-end/SearchController.js:
+        (WebInspector.SearchController.prototype._updateSearchMatchesCountAndCurrentMatchIndex):
+
 2011-11-11  Pierre Rossi  <pierre.rossi@gmail.com>
 
         [Qt] Remove the QStyle dependency in Qt's mobile theme
index 197f2d3..8dae6e7 100644 (file)
             {
                 "name": "performSearch",
                 "parameters": [
-                    { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." },
-                    { "name": "runSynchronously", "type": "boolean", "optional": true, "description": "When set to true, performing search synchronously (can block user interaction)." }
+                    { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." }
                 ],
-                "description": "Starts asynchronous search for a given string in the DOM tree. Use <code>cancelSearch</code> to stop given asynchronous search task.",
+                "returns": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "resultCount", "type": "integer", "description": "Number of search results." }
+                ],
+                "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.",
+                "hidden": true
+            },
+            {
+                "name": "getSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." },
+                    { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." },
+                    { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." }
+                ],
+                "returns": [
+                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." }
+                ],
+                "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.",
                 "hidden": true
             },
             {
-                "name": "cancelSearch",
-                "description": "Cancels asynchronous search started with <code>performSearch</code>.",
+                "name": "discardSearchResults",
+                "parameters": [
+                    { "name": "searchId", "type": "string", "description": "Unique search session identifier." }
+                ],
+                "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.",
                 "hidden": true
             },
             {
                     { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." }
                 ],
                 "description": "Mirrors <code>DOMNodeRemoved</code> event."
-            },
-            {
-                "name": "searchResults",
-                "parameters": [
-                    { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." }
-                ],
-                "description": "Pushes search results initiated using <code>performSearch</code> to the client.",
-                "hidden": true
             }
         ]
     },
index 02511a3..92a3e65 100644 (file)
@@ -62,6 +62,7 @@
 #include "HitTestResult.h"
 #include "HTMLElement.h"
 #include "HTMLFrameOwnerElement.h"
+#include "IdentifiersFactory.h"
 #include "InjectedScriptManager.h"
 #include "InspectorClient.h"
 #include "InspectorFrontend.h"
@@ -96,6 +97,9 @@ namespace DOMAgentState {
 static const char documentRequested[] = "documentRequested";
 };
 
+static const size_t maxTextSize = 10000;
+static const UChar ellipsisUChar[] = { 0x2026, 0 };
+
 static Color parseColor(const RefPtr<InspectorObject>* colorObject)
 {
     if (!colorObject || !(*colorObject))
@@ -146,7 +150,7 @@ protected:
             resultCollector.add(nodes->item(i));
     }
 
-    RefPtr<Document> m_document;
+    Document* m_document;
     String m_query;
 };
 
@@ -234,7 +238,7 @@ public:
             return;
 
         ExceptionCode ec = 0;
-        RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
+        RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
         if (ec || !result)
             return;
 
@@ -295,7 +299,6 @@ InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, I
     , m_frontend(0)
     , m_domListener(0)
     , m_lastNodeId(1)
-    , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer)
     , m_searchingForNode(false)
 {
 }
@@ -359,7 +362,7 @@ Node* InspectorDOMAgent::highlightedNode() const
 void InspectorDOMAgent::reset()
 {
     ErrorString error;
-    cancelSearch(&error);
+    m_searchResults.clear();
     discardBindings();
     if (m_revalidateStyleAttrTask)
         m_revalidateStyleAttrTask->reset();
@@ -896,7 +899,7 @@ void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPt
     }
 }
 
-void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, const bool* const runSynchronously)
+void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount)
 {
     // FIXME: Few things are missing here:
     // 1) Search works with node granularity - number of matches within node is not calculated.
@@ -922,31 +925,29 @@ void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespa
     String escapedTagNameQuery = tagNameQuery;
     escapedTagNameQuery.replace("'", "\\'");
 
-    // Clear pending jobs.
-    cancelSearch(error);
-
     // Find all frames, iframes and object elements to search their documents.
     Vector<Document*> docs = documents();
+    Vector<MatchJob*> matchJobs;
     for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
         Document* document = *it;
 
         if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
-            m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
-            m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
+            matchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
+            matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
             continue;
         }
 
         if (!tagNameQuery.isEmpty() && startTagFound) {
-            m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
-            m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
+            matchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
+            matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
             continue;
         }
 
         if (!tagNameQuery.isEmpty() && endTagFound) {
             // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
             // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
-            m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
-            m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
+            matchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
+            matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
             continue;
         }
 
@@ -954,42 +955,58 @@ void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespa
         if (matchesEveryNode) {
             // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
             // so limit the search functions list to plain text and attribute matching for these.
-            m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
-            m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
+            matchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
+            matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
             continue;
         }
 
-        m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
-        m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
-        m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
-        m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
-        m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
-        m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
+        matchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
+        matchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
+        matchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
+        matchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
+        matchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
+        matchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
         if (!tagNameQuery.isEmpty())
-            m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
-        m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
-        m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
+            matchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
+        matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
+        matchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
+    }
+
+    ListHashSet<Node*> resultCollector;
+    for (Vector<MatchJob*>::iterator it = matchJobs.begin(); it != matchJobs.end(); ++it)
+        (*it)->match(resultCollector);
+    deleteAllValues(matchJobs);
+
+    *searchId = IdentifiersFactory::createIdentifier();
+    SearchResults::iterator resultsIt = m_searchResults.add(*searchId, Vector<RefPtr<Node> >()).first;
+
+    for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
+        resultsIt->second.append(*it);
+
+    *resultCount = resultsIt->second.size();
+}
+
+void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<InspectorArray>* nodeIds)
+{
+    SearchResults::iterator it = m_searchResults.find(searchId);
+    if (it == m_searchResults.end()) {
+        *errorString = "No search session with given id found";
+        return;
     }
 
-    if (runSynchronously && *runSynchronously) {
-        // For tests.
-        ListHashSet<Node*> resultCollector;
-        for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it)
-            (*it)->match(resultCollector);
-        reportNodesAsSearchResults(resultCollector);
-        cancelSearch(error);
+    int size = it->second.size();
+    if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
+        *errorString = "Invalid search result range";
         return;
     }
-    m_matchJobsTimer.startOneShot(0);
+
+    for (int i = fromIndex; i < toIndex; ++i)
+        (*nodeIds)->pushNumber(pushNodePathToFrontend((it->second)[i].get()));
 }
 
-void InspectorDOMAgent::cancelSearch(ErrorString*)
+void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
 {
-    if (m_matchJobsTimer.isActive())
-        m_matchJobsTimer.stop();
-    deleteAllValues(m_pendingMatchJobs);
-    m_pendingMatchJobs.clear();
-    m_searchResults.clear();
+    m_searchResults.remove(searchId);
 }
 
 bool InspectorDOMAgent::handleMousePress()
@@ -1232,6 +1249,10 @@ PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, in
         case Node::COMMENT_NODE:
         case Node::CDATA_SECTION_NODE:
             nodeValue = node->nodeValue();
+            if (nodeValue.length() > maxTextSize) {
+                nodeValue = nodeValue.left(maxTextSize);
+                nodeValue.append(ellipsisUChar);
+            }
             break;
         case Node::ATTRIBUTE_NODE:
             localName = node->localName();
@@ -1575,36 +1596,6 @@ Node* InspectorDOMAgent::nodeForPath(const String& path)
     return node;
 }
 
-void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*)
-{
-    if (!m_pendingMatchJobs.size()) {
-        ErrorString error;
-        cancelSearch(&error);
-        return;
-    }
-
-    ListHashSet<Node*> resultCollector;
-    MatchJob* job = m_pendingMatchJobs.takeFirst();
-    job->match(resultCollector);
-    delete job;
-
-    reportNodesAsSearchResults(resultCollector);
-
-    m_matchJobsTimer.startOneShot(0.025);
-}
-
-void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector)
-{
-    RefPtr<InspectorArray> nodeIds = InspectorArray::create();
-    for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) {
-        if (m_searchResults.contains(*it))
-            continue;
-        m_searchResults.add(*it);
-        nodeIds->pushNumber(pushNodePathToFrontend(*it));
-    }
-    m_frontend->searchResults(nodeIds.release());
-}
-
 void InspectorDOMAgent::copyNode(ErrorString*, int nodeId)
 {
     Node* node = nodeForId(nodeId);
index 2985132..c4620f7 100644 (file)
@@ -126,8 +126,9 @@ public:
     void setOuterHTML(ErrorString*, int nodeId, const String& outerHTML, int* newId);
     void setNodeValue(ErrorString*, int nodeId, const String& value);
     void getEventListenersForNode(ErrorString*, int nodeId, RefPtr<InspectorArray>* listenersArray);
-    void performSearch(ErrorString*, const String& whitespaceTrimmedQuery, const bool* const runSynchronously);
-    void cancelSearch(ErrorString*);
+    void performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount);
+    void getSearchResults(ErrorString*, const String& searchId, int fromIndex, int toIndex, RefPtr<InspectorArray>*);
+    void discardSearchResults(ErrorString*, const String& searchId);
     void resolveNode(ErrorString*, int nodeId, const String* const objectGroup, RefPtr<InspectorObject>* result);
     void getAttributes(ErrorString*, int nodeId, RefPtr<InspectorArray>* result);
     void setInspectModeEnabled(ErrorString*, bool enabled, const RefPtr<InspectorObject>* highlightConfig);
@@ -209,9 +210,6 @@ private:
     PassRefPtr<InspectorArray> buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
     PassRefPtr<InspectorObject> buildObjectForEventListener(const RegisteredEventListener&, const AtomicString& eventType, Node*);
 
-    void onMatchJobsTimer(Timer<InspectorDOMAgent>*);
-    void reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector);
-
     Node* nodeForPath(const String& path);
 
     void discardBindings();
@@ -231,9 +229,8 @@ private:
     HashSet<int> m_childrenRequested;
     int m_lastNodeId;
     RefPtr<Document> m_document;
-    Deque<MatchJob*> m_pendingMatchJobs;
-    Timer<InspectorDOMAgent> m_matchJobsTimer;
-    HashSet<RefPtr<Node> > m_searchResults;
+    typedef HashMap<String, Vector<RefPtr<Node> > > SearchResults;
+    SearchResults m_searchResults;
     OwnPtr<RevalidateStyleAttributeTask> m_revalidateStyleAttrTask;
     OwnPtr<HighlightData> m_highlightData;
     RefPtr<Node> m_nodeToFocus;
index 2369a23..8e233cc 100644 (file)
@@ -832,19 +832,59 @@ WebInspector.DOMAgent.prototype = {
 
     /**
      * @param {string} query
-     * @param {function(Array.<DOMAgent.Node>)} searchResultCollector
-     * @param {boolean=} searchSynchronously
+     * @param {function(number)} searchCallback
      */
-    performSearch: function(query, searchResultCollector, searchSynchronously)
+    performSearch: function(query, searchCallback)
     {
-        this._searchResultCollector = searchResultCollector;
-        DOMAgent.performSearch(query, !!searchSynchronously);
+        this.cancelSearch();
+
+        /**
+         * @param {?Protocol.Error} error
+         * @param {string} searchId
+         * @param {number} resultsCount
+         */
+        function callback(error, searchId, resultsCount)
+        {
+            this._searchId = searchId;
+            searchCallback(resultsCount);
+        }
+        DOMAgent.performSearch(query, callback.bind(this));
+    },
+
+    /**
+     * @param {number} index
+     * @param {?function(DOMAgent.Node)} callback
+     */
+    searchResult: function(index, callback)
+    {
+        if (this._searchId) {
+            /**
+             * @param {?Protocol.Error} error
+             * @param {Array.<number>} nodeIds
+             */
+            function mycallback(error, nodeIds)
+            {
+                if (error) {
+                    console.error(error);
+                    callback(null);
+                    return;
+                }
+                if (nodeIds.length != 1)
+                    return;
+
+                callback(this._idToDOMNode[nodeIds[0]]);
+            }
+            DOMAgent.getSearchResults(this._searchId, index, index + 1, mycallback.bind(this));
+        } else
+            callback(null);
     },
 
     cancelSearch: function()
     {
-        delete this._searchResultCollector;
-        DOMAgent.cancelSearch();
+        if (this._searchId) {
+            DOMAgent.discardSearchResults(this._searchId);
+            delete this._searchId;
+        }
     },
 
     /**
@@ -1020,15 +1060,6 @@ WebInspector.DOMDispatcher.prototype = {
     childNodeRemoved: function(parentNodeId, nodeId)
     {
         this._domAgent._childNodeRemoved(parentNodeId, nodeId);
-    },
-
-    /**
-     * @param {Array.<DOMAgent.NodeId>} nodeIds
-     */
-    searchResults: function(nodeIds)
-    {
-        if (this._domAgent._searchResultCollector)
-            this._domAgent._searchResultCollector(nodeIds);
     }
 }
 
index 6c1eb81..51fe71a 100644 (file)
@@ -252,7 +252,7 @@ WebInspector.ElementsPanel.prototype = {
         WebInspector.searchController.updateSearchMatchesCount(0, this);
 
         delete this._currentSearchResultIndex;
-        this._searchResults = [];
+        delete this._searchResults;
         WebInspector.domAgent.cancelSearch();
     },
 
@@ -265,11 +265,22 @@ WebInspector.ElementsPanel.prototype = {
         if (!whitespaceTrimmedQuery.length)
             return;
 
-        this._updatedMatchCountOnce = false;
-        this._matchesCountUpdateTimeout = null;
         this._searchQuery = query;
 
-        WebInspector.domAgent.performSearch(whitespaceTrimmedQuery, this._addNodesToSearchResult.bind(this));
+        /**
+         * @param {number} resultCount
+         */
+        function resultCountCallback(resultCount)
+        {
+            WebInspector.searchController.updateSearchMatchesCount(resultCount, this);
+            if (!resultCount)
+                return;
+
+            this._searchResults = new Array(resultCount);
+            this._currentSearchResultIndex = -1;
+            this.jumpToNextSearchResult();
+        }
+        WebInspector.domAgent.performSearch(whitespaceTrimmedQuery, resultCountCallback.bind(this));
     },
 
     _contextMenuEventFired: function(event)
@@ -322,70 +333,55 @@ WebInspector.ElementsPanel.prototype = {
         }
     },
 
-    _updateMatchesCount: function()
-    {
-        WebInspector.searchController.updateSearchMatchesCount(this._searchResults.length, this);
-        this._matchesCountUpdateTimeout = null;
-        this._updatedMatchCountOnce = true;
-    },
-
-    _updateMatchesCountSoon: function()
-    {
-        if (!this._updatedMatchCountOnce)
-            return this._updateMatchesCount();
-        if (this._matchesCountUpdateTimeout)
-            return;
-        // Update the matches count every half-second so it doesn't feel twitchy.
-        this._matchesCountUpdateTimeout = setTimeout(this._updateMatchesCount.bind(this), 500);
-    },
-
-    _addNodesToSearchResult: function(nodeIds)
-    {
-        if (!nodeIds.length)
-            return;
-
-        var oldSearchResultIndex = this._currentSearchResultIndex;
-        for (var i = 0; i < nodeIds.length; ++i) {
-            var nodeId = nodeIds[i];
-            var node = WebInspector.domAgent.nodeForId(nodeId);
-            if (!node)
-                continue;
-
-            this._currentSearchResultIndex = 0;
-            this._searchResults.push(node);
-        }
-
-        // Avoid invocations of highlighting for every chunk of nodeIds.
-        if (oldSearchResultIndex !== this._currentSearchResultIndex)
-            this._highlightCurrentSearchResult();
-        this._updateMatchesCountSoon();
-    },
-
     jumpToNextSearchResult: function()
     {
-        if (!this._searchResults || !this._searchResults.length)
+        if (!this._searchResults)
             return;
 
+        this._hideSearchHighlights();
         if (++this._currentSearchResultIndex >= this._searchResults.length)
             this._currentSearchResultIndex = 0;
-        this._highlightCurrentSearchResult();
+            
+        this._highlightCurrentSearchResult(true);
     },
 
     jumpToPreviousSearchResult: function()
     {
-        if (!this._searchResults || !this._searchResults.length)
+        if (!this._searchResults)
             return;
 
+        this._hideSearchHighlights();
         if (--this._currentSearchResultIndex < 0)
             this._currentSearchResultIndex = (this._searchResults.length - 1);
-        this._highlightCurrentSearchResult();
+
+        this._highlightCurrentSearchResult(false);
     },
 
     _highlightCurrentSearchResult: function()
     {
-        this._hideSearchHighlights();
-        var node = this._searchResults[this._currentSearchResultIndex];
-        var treeElement = this.treeOutline.findTreeElement(node);
+        var index = this._currentSearchResultIndex;
+        var searchResults = this._searchResults;
+        var searchResult = searchResults[index];
+
+        if (searchResult === null) {
+            WebInspector.searchController.updateCurrentMatchIndex(index, this);
+            return;
+        }
+
+        if (typeof searchResult === "undefined") {
+            // No data for slot, request it.
+            function callback(node)
+            {
+                searchResults[index] = node || null;
+                this._highlightCurrentSearchResult();
+            }
+            WebInspector.domAgent.searchResult(index, callback.bind(this));
+            return;
+        }
+
+        WebInspector.searchController.updateCurrentMatchIndex(index, this);
+
+        var treeElement = this.treeOutline.findTreeElement(searchResult);
         if (treeElement) {
             treeElement.highlightSearchResults(this._searchQuery);
             treeElement.reveal();
@@ -394,12 +390,14 @@ WebInspector.ElementsPanel.prototype = {
 
     _hideSearchHighlights: function()
     {
-        for (var i = 0; this._searchResults && i < this._searchResults.length; ++i) {
-            var node = this._searchResults[i];
-            var treeElement = this.treeOutline.findTreeElement(node);
-            if (treeElement)
-                treeElement.hideSearchHighlights();
-        }
+        if (!this._searchResults)
+            return;
+        var searchResult = this._searchResults[this._currentSearchResultIndex];
+        if (!searchResult)
+            return;
+        var treeElement = this.treeOutline.findTreeElement(searchResult);
+        if (treeElement)
+            treeElement.hideSearchHighlights();
     },
 
     selectedDOMNode: function()
index 23eb7bb..58d6a03 100644 (file)
@@ -1120,7 +1120,7 @@ WebInspector.NetworkLogView.prototype = {
                 node.reveal();
             this._currentMatchedResourceIndex = matchedResourceIndex;
         }
-        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedResourceIndex + 1);
+        this.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.SearchIndexUpdated, this._currentMatchedResourceIndex);
     },
 
     performSearch: function(searchQuery, sortOrFilterApplied)
index 3250c5e..4890e37 100644 (file)
@@ -743,7 +743,7 @@ WebInspector.ResourcesPanel.prototype = {
                 return; // User has selected another view while we were searching.
             if (this._lastSearchResultIndex != -1)
                 this.visibleView.jumpToSearchResult(this._lastSearchResultIndex);
-            WebInspector.searchController.updateCurrentMatchIndex(searchResult.currentMatchIndex, this);
+            WebInspector.searchController.updateCurrentMatchIndex(searchResult.currentMatchIndex - 1, this);
         }
 
         // Then run SourceFrame search if needed and jump to search result index when done.
index 5c23f3c..5d442b1 100644 (file)
@@ -1113,7 +1113,7 @@ WebInspector.ScriptsPanel.prototype = {
 
             WebInspector.searchController.updateSearchMatchesCount(searchMatches, this);
             view.jumpToFirstSearchResult();
-            WebInspector.searchController.updateCurrentMatchIndex(view.currentSearchResultIndex + 1, this);
+            WebInspector.searchController.updateCurrentMatchIndex(view.currentSearchResultIndex, this);
         }
 
         this._searchView.performSearch(query, finishedCallback.bind(this));
@@ -1133,7 +1133,7 @@ WebInspector.ScriptsPanel.prototype = {
             this._searchView.jumpToFirstSearchResult();
         else
             this._searchView.jumpToNextSearchResult();
-        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex + 1, this);
+        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
     },
 
     jumpToPreviousSearchResult: function()
@@ -1152,7 +1152,7 @@ WebInspector.ScriptsPanel.prototype = {
             this._searchView.jumpToLastSearchResult();
         else
             this._searchView.jumpToPreviousSearchResult();
-        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex + 1, this);
+        WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
     },
 
     _toggleFormatSource: function()
index f6f6858..0393639 100644 (file)
@@ -164,13 +164,13 @@ WebInspector.SearchController.prototype = {
 
         if (matches) {
             if (matches === 1) {
-                if (currentMatchIndex === 1)
+                if (currentMatchIndex === 0)
                     var matchesString = WebInspector.UIString("1 of 1 match");
                 else
                     var matchesString = WebInspector.UIString("1 match");
             } else {
-                if (currentMatchIndex)
-                    var matchesString = WebInspector.UIString("%d of %d matches", currentMatchIndex, matches);
+                if (typeof currentMatchIndex === "number")
+                    var matchesString = WebInspector.UIString("%d of %d matches", currentMatchIndex + 1, matches);
                 else
                     var matchesString = WebInspector.UIString("%d matches", matches);
             }