InspectorNodeFinder.cpp [plain text]
#include "config.h"
#include "InspectorNodeFinder.h"
#include "Attr.h"
#include "Document.h"
#include "Element.h"
#include "HTMLFrameOwnerElement.h"
#include "NodeList.h"
#include "NodeTraversal.h"
#include "XPathNSResolver.h"
#include "XPathResult.h"
namespace WebCore {
static String stripCharacters(const String& string, const char startCharacter, const char endCharacter, bool& startCharFound, bool& endCharFound)
{
startCharFound = string.startsWith(startCharacter);
endCharFound = string.endsWith(endCharacter);
unsigned start = startCharFound ? 1 : 0;
unsigned end = string.length() - (endCharFound ? 1 : 0);
return string.substring(start, end - start);
}
InspectorNodeFinder::InspectorNodeFinder(const String& whitespaceTrimmedQuery)
: m_whitespaceTrimmedQuery(whitespaceTrimmedQuery)
{
m_tagNameQuery = stripCharacters(whitespaceTrimmedQuery, '<', '>', m_startTagFound, m_endTagFound);
bool startQuoteFound, endQuoteFound;
m_attributeQuery = stripCharacters(whitespaceTrimmedQuery, '"', '"', startQuoteFound, endQuoteFound);
m_exactAttributeMatch = startQuoteFound && endQuoteFound;
}
void InspectorNodeFinder::performSearch(Node* parentNode)
{
if (!parentNode)
return;
searchUsingXPath(*parentNode);
searchUsingCSSSelectors(*parentNode);
searchUsingDOMTreeTraversal(*parentNode);
}
void InspectorNodeFinder::searchUsingDOMTreeTraversal(Node& parentNode)
{
for (auto* node = &parentNode; node; node = NodeTraversal::next(*node, &parentNode)) {
switch (node->nodeType()) {
case Node::TEXT_NODE:
case Node::COMMENT_NODE:
case Node::CDATA_SECTION_NODE:
if (node->nodeValue().containsIgnoringASCIICase(m_whitespaceTrimmedQuery))
m_results.add(node);
break;
case Node::ELEMENT_NODE:
if (matchesElement(downcast<Element>(*node)))
m_results.add(node);
if (is<HTMLFrameOwnerElement>(downcast<Element>(*node)))
performSearch(downcast<HTMLFrameOwnerElement>(*node).contentDocument());
break;
default:
break;
}
}
}
bool InspectorNodeFinder::matchesAttribute(const Attribute& attribute)
{
if (attribute.localName().string().containsIgnoringASCIICase(m_whitespaceTrimmedQuery))
return true;
return m_exactAttributeMatch ? attribute.value() == m_attributeQuery : attribute.value().string().containsIgnoringASCIICase(m_attributeQuery);
}
bool InspectorNodeFinder::matchesElement(const Element& element)
{
String nodeName = element.nodeName();
if ((!m_startTagFound && !m_endTagFound && nodeName.containsIgnoringASCIICase(m_tagNameQuery))
|| (m_startTagFound && m_endTagFound && equalIgnoringASCIICase(nodeName, m_tagNameQuery))
|| (m_startTagFound && !m_endTagFound && nodeName.startsWithIgnoringASCIICase(m_tagNameQuery))
|| (!m_startTagFound && m_endTagFound && nodeName.endsWithIgnoringASCIICase(m_tagNameQuery)))
return true;
if (!element.hasAttributes())
return false;
for (const Attribute& attribute : element.attributesIterator()) {
if (matchesAttribute(attribute))
return true;
}
return false;
}
void InspectorNodeFinder::searchUsingXPath(Node& parentNode)
{
auto evaluateResult = parentNode.document().evaluate(m_whitespaceTrimmedQuery, &parentNode, nullptr, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
if (evaluateResult.hasException())
return;
auto result = evaluateResult.releaseReturnValue();
auto snapshotLengthResult = result->snapshotLength();
if (snapshotLengthResult.hasException())
return;
unsigned size = snapshotLengthResult.releaseReturnValue();
for (unsigned i = 0; i < size; ++i) {
auto snapshotItemResult = result->snapshotItem(i);
if (snapshotItemResult.hasException())
return;
Node* node = snapshotItemResult.releaseReturnValue();
if (is<Attr>(*node))
node = downcast<Attr>(*node).ownerElement();
if (parentNode.contains(node))
m_results.add(node);
}
}
void InspectorNodeFinder::searchUsingCSSSelectors(Node& parentNode)
{
if (!is<ContainerNode>(parentNode))
return;
auto queryResult = downcast<ContainerNode>(parentNode).querySelectorAll(m_whitespaceTrimmedQuery);
if (queryResult.hasException())
return;
auto nodeList = queryResult.releaseReturnValue();
unsigned size = nodeList->length();
for (unsigned i = 0; i < size; ++i)
m_results.add(nodeList->item(i));
}
}