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& query, bool caseSensitive)
: m_query(query)
, m_caseSensitive(caseSensitive)
{
m_tagNameQuery = stripCharacters(query, '<', '>', m_startTagFound, m_endTagFound);
bool startQuoteFound, endQuoteFound;
m_attributeQuery = stripCharacters(query, '"', '"', 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 (checkContains(node->nodeValue(), m_query))
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::checkEquals(const String& a, const String& b)
{
if (m_caseSensitive)
return a == b;
return equalIgnoringASCIICase(a, b);
}
bool InspectorNodeFinder::checkContains(const String& a, const String& b)
{
if (m_caseSensitive)
return a.contains(b);
return a.containsIgnoringASCIICase(b);
}
bool InspectorNodeFinder::checkStartsWith(const String& a, const String& b)
{
if (m_caseSensitive)
return a.startsWith(b);
return a.startsWithIgnoringASCIICase(b);
}
bool InspectorNodeFinder::checkEndsWith(const String& a, const String& b)
{
if (m_caseSensitive)
return a.endsWith(b);
return a.endsWithIgnoringASCIICase(b);
}
bool InspectorNodeFinder::matchesAttribute(const Attribute& attribute)
{
if (checkContains(attribute.localName().string(), m_query))
return true;
auto value = attribute.value().string();
return m_exactAttributeMatch ? checkEquals(value, m_attributeQuery) : checkContains(value, m_attributeQuery);
}
bool InspectorNodeFinder::matchesElement(const Element& element)
{
String nodeName = element.nodeName();
if ((!m_startTagFound && !m_endTagFound && checkContains(nodeName, m_tagNameQuery))
|| (m_startTagFound && m_endTagFound && checkEquals(nodeName, m_tagNameQuery))
|| (m_startTagFound && !m_endTagFound && checkStartsWith(nodeName, m_tagNameQuery))
|| (!m_startTagFound && m_endTagFound && checkEndsWith(nodeName, 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_query, 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_query);
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));
}
}