Have is<>(T*) function do a null check on the pointer argument
[WebKit-https.git] / Source / WebCore / inspector / InspectorNodeFinder.cpp
1 /*
2  * Copyright (C) 2013 Adobe Systems Inc. All rights reserved.
3  * Copyright (C) 2009 Apple Inc. All rights reserved.
4  * Copyright (C) 2011 Google Inc. All rights reserved.
5  * Copyright (C) 2009 Joseph Pecoraro
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "InspectorNodeFinder.h"
34
35 #include "Attr.h"
36 #include "Attribute.h"
37 #include "Document.h"
38 #include "Element.h"
39 #include "HTMLFrameOwnerElement.h"
40 #include "NodeList.h"
41 #include "NodeTraversal.h"
42 #include "XPathResult.h"
43
44 namespace WebCore {
45
46 static String stripCharacters(String string, const char startCharacter, const char endCharacter, bool& startCharFound, bool& endCharFound)
47 {
48     startCharFound = string.startsWith(startCharacter);
49     endCharFound = string.endsWith(endCharacter);
50
51     unsigned start = startCharFound ? 1 : 0;
52     unsigned end = string.length() - (endCharFound ? 1 : 0);
53     return string.substring(start, end - start);
54 }
55
56 InspectorNodeFinder::InspectorNodeFinder(String whitespaceTrimmedQuery)
57     : m_whitespaceTrimmedQuery(whitespaceTrimmedQuery)
58 {
59     m_tagNameQuery = stripCharacters(whitespaceTrimmedQuery, '<', '>', m_startTagFound, m_endTagFound);
60
61     bool startQuoteFound, endQuoteFound;
62     m_attributeQuery = stripCharacters(whitespaceTrimmedQuery, '"', '"', startQuoteFound, endQuoteFound);
63     m_exactAttributeMatch = startQuoteFound && endQuoteFound;
64 }
65
66 void InspectorNodeFinder::performSearch(Node* parentNode)
67 {
68     searchUsingXPath(parentNode);
69     searchUsingCSSSelectors(parentNode);
70
71     // Keep the DOM tree traversal last. This way iframe content will come after their parents.
72     searchUsingDOMTreeTraversal(parentNode);
73 }
74
75 void InspectorNodeFinder::searchUsingDOMTreeTraversal(Node* parentNode)
76 {
77     // Manual plain text search.
78     for (auto* node = parentNode; node; node = NodeTraversal::next(node, parentNode)) {
79         switch (node->nodeType()) {
80         case Node::TEXT_NODE:
81         case Node::COMMENT_NODE:
82         case Node::CDATA_SECTION_NODE: {
83             if (node->nodeValue().findIgnoringCase(m_whitespaceTrimmedQuery) != notFound)
84                 m_results.add(node);
85             break;
86         }
87         case Node::ELEMENT_NODE: {
88             if (matchesElement(downcast<Element>(*node)))
89                 m_results.add(node);
90
91             // Search inside frame elements.
92             if (is<HTMLFrameOwnerElement>(*node)) {
93                 HTMLFrameOwnerElement& frameOwner = downcast<HTMLFrameOwnerElement>(*node);
94                 if (Document* document = frameOwner.contentDocument())
95                     performSearch(document);
96             }
97
98             break;
99         }
100         default:
101             break;
102         }
103     }
104 }
105
106 bool InspectorNodeFinder::matchesAttribute(const Attribute& attribute)
107 {
108     if (attribute.localName().string().findIgnoringCase(m_whitespaceTrimmedQuery) != notFound)
109         return true;
110     return m_exactAttributeMatch ? attribute.value() == m_attributeQuery : attribute.value().string().findIgnoringCase(m_attributeQuery) != notFound;
111 }
112
113 bool InspectorNodeFinder::matchesElement(const Element& element)
114 {
115     String nodeName = element.nodeName();
116     if ((!m_startTagFound && !m_endTagFound && (nodeName.findIgnoringCase(m_tagNameQuery) != notFound))
117         || (m_startTagFound && m_endTagFound && equalIgnoringCase(nodeName, m_tagNameQuery))
118         || (m_startTagFound && !m_endTagFound && nodeName.startsWith(m_tagNameQuery, false))
119         || (!m_startTagFound && m_endTagFound && nodeName.endsWith(m_tagNameQuery, false)))
120         return true;
121
122     if (!element.hasAttributes())
123         return false;
124
125     for (const Attribute& attribute : element.attributesIterator()) {
126         if (matchesAttribute(attribute))
127             return true;
128     }
129
130     return false;
131 }
132
133 void InspectorNodeFinder::searchUsingXPath(Node* parentNode)
134 {
135     ExceptionCode ec = 0;
136     RefPtr<XPathResult> result = parentNode->document().evaluate(m_whitespaceTrimmedQuery, parentNode, nullptr, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr, ec);
137     if (ec || !result)
138         return;
139
140     unsigned long size = result->snapshotLength(ec);
141     if (ec)
142         return;
143
144     for (unsigned long i = 0; i < size; ++i) {
145         Node* node = result->snapshotItem(i, ec);
146         if (ec)
147             return;
148
149         if (is<Attr>(*node))
150             node = downcast<Attr>(*node).ownerElement();
151
152         // XPath can get out of the context node that we pass as the starting point to evaluate, so we need to filter for just the nodes we care about.
153         if (node == parentNode || node->isDescendantOf(parentNode))
154             m_results.add(node);
155     }
156 }
157
158 void InspectorNodeFinder::searchUsingCSSSelectors(Node* parentNode)
159 {
160     ASSERT(parentNode);
161     if (!is<ContainerNode>(*parentNode))
162         return;
163
164     ExceptionCode ec = 0;
165     RefPtr<NodeList> nodeList = downcast<ContainerNode>(*parentNode).querySelectorAll(m_whitespaceTrimmedQuery, ec);
166     if (ec || !nodeList)
167         return;
168
169     unsigned size = nodeList->length();
170     for (unsigned i = 0; i < size; ++i)
171         m_results.add(nodeList->item(i));
172 }
173
174 } // namespace WebCore