Web Inspector: reimplement DOM search using no XPath.
https://bugs.webkit.org/show_bug.cgi?id=72125
Reviewed by Yury Semikhatsky.
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::performSearch):
(WebCore::InspectorDOMAgent::buildObjectForNode):
* inspector/InspectorDOMAgent.h:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@99983 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/inspector/elements/elements-panel-search.html b/LayoutTests/inspector/elements/elements-panel-search.html
index 46ad330..f690639 100644
--- a/LayoutTests/inspector/elements/elements-panel-search.html
+++ b/LayoutTests/inspector/elements/elements-panel-search.html
@@ -17,9 +17,9 @@
node.getOuterHTML(addSearchResult.bind(this, isLastItem));
}
- function addSearchResult(isLastItem, error, markupValue)
+ function addSearchResult(isLastItem, error, markupVa_lue)
{
- InspectorTest.addResult(markupValue.split("").join(" "));
+ InspectorTest.addResult(markupVa_lue.split("").join(" "));
if (isLastItem) {
WebInspector.domAgent.cancelSearch();
next();
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 357349a..9d0009e 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,15 @@
+2011-11-11 Pavel Feldman <pfeldman@google.com>
+
+ Web Inspector: reimplement DOM search using no XPath.
+ https://bugs.webkit.org/show_bug.cgi?id=72125
+
+ Reviewed by Yury Semikhatsky.
+
+ * inspector/InspectorDOMAgent.cpp:
+ (WebCore::InspectorDOMAgent::performSearch):
+ (WebCore::InspectorDOMAgent::buildObjectForNode):
+ * inspector/InspectorDOMAgent.h:
+
2011-11-11 Gavin Peters <gavinp@chromium.org>
Protect Document during error responses
diff --git a/Source/WebCore/inspector/InspectorDOMAgent.cpp b/Source/WebCore/inspector/InspectorDOMAgent.cpp
index ac7a830..9d805ca 100644
--- a/Source/WebCore/inspector/InspectorDOMAgent.cpp
+++ b/Source/WebCore/inspector/InspectorDOMAgent.cpp
@@ -134,26 +134,6 @@
return parseColor(&colorObject);
}
-class MatchJob {
-public:
- virtual void match(ListHashSet<Node*>& resultCollector) = 0;
- virtual ~MatchJob() { }
-
-protected:
- MatchJob(Document* document, const String& query)
- : m_document(document)
- , m_query(query) { }
-
- void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector)
- {
- for (unsigned i = 0; nodes && i < nodes->length(); ++i)
- resultCollector.add(nodes->item(i));
- }
-
- Document* m_document;
- String m_query;
-};
-
class RevalidateStyleAttributeTask {
public:
RevalidateStyleAttributeTask(InspectorDOMAgent*);
@@ -167,105 +147,6 @@
HashSet<RefPtr<Element> > m_elements;
};
-namespace {
-
-class MatchExactIdJob : public WebCore::MatchJob {
-public:
- MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
- virtual ~MatchExactIdJob() { }
-
-protected:
- virtual void match(ListHashSet<Node*>& resultCollector)
- {
- if (m_query.isEmpty())
- return;
-
- Element* element = m_document->getElementById(m_query);
- if (element)
- resultCollector.add(element);
- }
-};
-
-class MatchExactClassNamesJob : public WebCore::MatchJob {
-public:
- MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
- virtual ~MatchExactClassNamesJob() { }
-
- virtual void match(ListHashSet<Node*>& resultCollector)
- {
- if (!m_query.isEmpty())
- addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector);
- }
-};
-
-class MatchExactTagNamesJob : public WebCore::MatchJob {
-public:
- MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
- virtual ~MatchExactTagNamesJob() { }
-
- virtual void match(ListHashSet<Node*>& resultCollector)
- {
- if (!m_query.isEmpty())
- addNodesToResults(m_document->getElementsByName(m_query), resultCollector);
- }
-};
-
-class MatchQuerySelectorAllJob : public WebCore::MatchJob {
-public:
- MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
- virtual ~MatchQuerySelectorAllJob() { }
-
- virtual void match(ListHashSet<Node*>& resultCollector)
- {
- if (m_query.isEmpty())
- return;
-
- ExceptionCode ec = 0;
- RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec);
- if (!ec)
- addNodesToResults(list, resultCollector);
- }
-};
-
-class MatchXPathJob : public WebCore::MatchJob {
-public:
- MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
- virtual ~MatchXPathJob() { }
-
- virtual void match(ListHashSet<Node*>& resultCollector)
- {
- if (m_query.isEmpty())
- return;
-
- ExceptionCode ec = 0;
- RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
- if (ec || !result)
- return;
-
- unsigned long size = result->snapshotLength(ec);
- for (unsigned long i = 0; !ec && i < size; ++i) {
- Node* node = result->snapshotItem(i, ec);
- if (ec)
- break;
-
- if (node->nodeType() == Node::ATTRIBUTE_NODE)
- node = static_cast<Attr*>(node)->ownerElement();
- resultCollector.add(node);
- }
- }
-};
-
-class MatchPlainTextJob : public MatchXPathJob {
-public:
- MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query)
- {
- m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]";
- }
- virtual ~MatchPlainTextJob() { }
-};
-
-}
-
RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
: m_domAgent(domAgent)
, m_timer(this, &RevalidateStyleAttributeTask::onTimer)
@@ -658,7 +539,7 @@
unsigned numAttrs = attrMap->length();
for (unsigned i = 0; i < numAttrs; ++i) {
// Add attribute pair
- const Attribute *attribute = attrMap->attributeItem(i);
+ const Attribute* attribute = attrMap->attributeItem(i);
foundOriginalAttribute = foundOriginalAttribute || (name && attribute->name().toString() == *name);
element->setAttribute(attribute->name(), attribute->value(), ec);
if (ec) {
@@ -911,72 +792,80 @@
bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
String tagNameQuery = whitespaceTrimmedQuery;
- if (startTagFound || endTagFound)
- tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength);
- if (!Document::isValidName(tagNameQuery))
- tagNameQuery = "";
+ if (startTagFound)
+ tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
+ if (endTagFound)
+ tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
- String attributeNameQuery = whitespaceTrimmedQuery;
- if (!Document::isValidName(attributeNameQuery))
- attributeNameQuery = "";
-
- String escapedQuery = whitespaceTrimmedQuery;
- escapedQuery.replace("'", "\\'");
- String escapedTagNameQuery = tagNameQuery;
- escapedTagNameQuery.replace("'", "\\'");
-
- // Find all frames, iframes and object elements to search their documents.
Vector<Document*> docs = documents();
- Vector<MatchJob*> matchJobs;
+ ListHashSet<Node*> resultCollector;
+
for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
Document* document = *it;
+ Node* node = document->documentElement();
- if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
- matchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
- matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
- continue;
+ // Manual plain text search.
+ while ((node = node->traverseNextNode(document->documentElement()))) {
+ switch (node->nodeType()) {
+ case Node::TEXT_NODE:
+ case Node::COMMENT_NODE:
+ case Node::CDATA_SECTION_NODE: {
+ String text = node->nodeValue();
+ if (text.findIgnoringCase(whitespaceTrimmedQuery) != notFound)
+ resultCollector.add(node);
+ break;
+ }
+ case Node::ELEMENT_NODE: {
+ if (node->nodeName().findIgnoringCase(tagNameQuery) != notFound) {
+ resultCollector.add(node);
+ break;
+ }
+ // Go through all attributes and serialize them.
+ const NamedNodeMap* attrMap = static_cast<Element*>(node)->attributes(true);
+ if (!attrMap)
+ break;
+
+ unsigned numAttrs = attrMap->length();
+ for (unsigned i = 0; i < numAttrs; ++i) {
+ // Add attribute pair
+ const Attribute* attribute = attrMap->attributeItem(i);
+ if (attribute->localName().find(whitespaceTrimmedQuery) != notFound) {
+ resultCollector.add(node);
+ break;
+ }
+ if (attribute->value().find(whitespaceTrimmedQuery) != notFound) {
+ resultCollector.add(node);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
}
- if (!tagNameQuery.isEmpty() && startTagFound) {
- matchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
- matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
- continue;
- }
+ // XPath evaluation
+ for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
+ Document* document = *it;
+ ExceptionCode ec = 0;
+ RefPtr<XPathResult> result = document->evaluate(whitespaceTrimmedQuery, document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
+ if (ec || !result)
+ 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().
- matchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
- matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
- continue;
- }
+ unsigned long size = result->snapshotLength(ec);
+ for (unsigned long i = 0; !ec && i < size; ++i) {
+ Node* node = result->snapshotItem(i, ec);
+ if (ec)
+ break;
- bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*";
- 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.
- matchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
- matchJobs.append(new MatchPlainTextJob(document, escapedQuery));
- continue;
+ if (node->nodeType() == Node::ATTRIBUTE_NODE)
+ node = static_cast<Attr*>(node)->ownerElement();
+ resultCollector.add(node);
+ }
}
-
- 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())
- 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;
@@ -1245,26 +1134,26 @@
String nodeValue;
switch (node->nodeType()) {
- case Node::TEXT_NODE:
- 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();
- break;
- case Node::DOCUMENT_FRAGMENT_NODE:
- break;
- case Node::DOCUMENT_NODE:
- case Node::ELEMENT_NODE:
- default:
- nodeName = node->nodeName();
- localName = node->localName();
- break;
+ case Node::TEXT_NODE:
+ 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();
+ break;
+ case Node::DOCUMENT_FRAGMENT_NODE:
+ break;
+ case Node::DOCUMENT_NODE:
+ case Node::ELEMENT_NODE:
+ default:
+ nodeName = node->nodeName();
+ localName = node->localName();
+ break;
}
value->setNumber("nodeId", id);
@@ -1315,7 +1204,7 @@
unsigned numAttrs = attrMap->length();
for (unsigned i = 0; i < numAttrs; ++i) {
// Add attribute pair
- const Attribute *attribute = attrMap->attributeItem(i);
+ const Attribute* attribute = attrMap->attributeItem(i);
attributesValue->pushString(attribute->name().toString());
attributesValue->pushString(attribute->value());
}
diff --git a/Source/WebCore/inspector/InspectorDOMAgent.h b/Source/WebCore/inspector/InspectorDOMAgent.h
index c4620f7..0f80d48 100644
--- a/Source/WebCore/inspector/InspectorDOMAgent.h
+++ b/Source/WebCore/inspector/InspectorDOMAgent.h
@@ -60,7 +60,6 @@
class InspectorPageAgent;
class IntRect;
class HitTestResult;
-class MatchJob;
class HTMLElement;
struct HighlightData;
class InspectorState;