• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  * Copyright (C) 2009 Joseph Pecoraro
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "core/inspector/InspectorDOMAgent.h"
33 
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/ScriptEventListener.h"
36 #include "core/InputTypeNames.h"
37 #include "core/dom/Attr.h"
38 #include "core/dom/CharacterData.h"
39 #include "core/dom/ContainerNode.h"
40 #include "core/dom/DOMException.h"
41 #include "core/dom/Document.h"
42 #include "core/dom/DocumentFragment.h"
43 #include "core/dom/DocumentType.h"
44 #include "core/dom/Element.h"
45 #include "core/dom/Node.h"
46 #include "core/dom/PseudoElement.h"
47 #include "core/dom/StaticNodeList.h"
48 #include "core/dom/Text.h"
49 #include "core/dom/shadow/ElementShadow.h"
50 #include "core/dom/shadow/ShadowRoot.h"
51 #include "core/editing/markup.h"
52 #include "core/events/EventListener.h"
53 #include "core/events/EventTarget.h"
54 #include "core/fileapi/File.h"
55 #include "core/fileapi/FileList.h"
56 #include "core/frame/LocalFrame.h"
57 #include "core/html/HTMLFrameOwnerElement.h"
58 #include "core/html/HTMLInputElement.h"
59 #include "core/html/HTMLLinkElement.h"
60 #include "core/html/HTMLTemplateElement.h"
61 #include "core/html/imports/HTMLImportChild.h"
62 #include "core/html/imports/HTMLImportLoader.h"
63 #include "core/inspector/DOMEditor.h"
64 #include "core/inspector/DOMPatchSupport.h"
65 #include "core/inspector/IdentifiersFactory.h"
66 #include "core/inspector/InspectorHistory.h"
67 #include "core/inspector/InspectorNodeIds.h"
68 #include "core/inspector/InspectorOverlay.h"
69 #include "core/inspector/InspectorPageAgent.h"
70 #include "core/inspector/InspectorState.h"
71 #include "core/inspector/InstrumentingAgents.h"
72 #include "core/loader/DocumentLoader.h"
73 #include "core/page/FrameTree.h"
74 #include "core/page/Page.h"
75 #include "core/rendering/HitTestResult.h"
76 #include "core/rendering/RenderView.h"
77 #include "core/xml/DocumentXPathEvaluator.h"
78 #include "core/xml/XPathResult.h"
79 #include "platform/PlatformGestureEvent.h"
80 #include "platform/PlatformMouseEvent.h"
81 #include "platform/PlatformTouchEvent.h"
82 #include "wtf/ListHashSet.h"
83 #include "wtf/text/CString.h"
84 #include "wtf/text/WTFString.h"
85 
86 namespace blink {
87 
88 using namespace HTMLNames;
89 
90 namespace DOMAgentState {
91 static const char domAgentEnabled[] = "domAgentEnabled";
92 };
93 
94 static const size_t maxTextSize = 10000;
95 static const UChar ellipsisUChar[] = { 0x2026, 0 };
96 
parseColor(const RefPtr<JSONObject> * colorObject)97 static Color parseColor(const RefPtr<JSONObject>* colorObject)
98 {
99     if (!colorObject || !(*colorObject))
100         return Color::transparent;
101 
102     int r;
103     int g;
104     int b;
105     bool success = (*colorObject)->getNumber("r", &r);
106     success |= (*colorObject)->getNumber("g", &g);
107     success |= (*colorObject)->getNumber("b", &b);
108     if (!success)
109         return Color::transparent;
110 
111     double a;
112     success = (*colorObject)->getNumber("a", &a);
113     if (!success)
114         return Color(r, g, b);
115 
116     // Clamp alpha to the [0..1] range.
117     if (a < 0)
118         a = 0;
119     else if (a > 1)
120         a = 1;
121 
122     return Color(r, g, b, static_cast<int>(a * 255));
123 }
124 
parseConfigColor(const String & fieldName,JSONObject * configObject)125 static Color parseConfigColor(const String& fieldName, JSONObject* configObject)
126 {
127     const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName);
128     return parseColor(&colorObject);
129 }
130 
parseQuad(const RefPtr<JSONArray> & quadArray,FloatQuad * quad)131 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad)
132 {
133     if (!quadArray)
134         return false;
135     const size_t coordinatesInQuad = 8;
136     double coordinates[coordinatesInQuad];
137     if (quadArray->length() != coordinatesInQuad)
138         return false;
139     for (size_t i = 0; i < coordinatesInQuad; ++i) {
140         if (!quadArray->get(i)->asNumber(coordinates + i))
141             return false;
142     }
143     quad->setP1(FloatPoint(coordinates[0], coordinates[1]));
144     quad->setP2(FloatPoint(coordinates[2], coordinates[3]));
145     quad->setP3(FloatPoint(coordinates[4], coordinates[5]));
146     quad->setP4(FloatPoint(coordinates[6], coordinates[7]));
147 
148     return true;
149 }
150 
hoveredNodeForPoint(LocalFrame * frame,const IntPoint & point,bool ignorePointerEventsNone)151 static Node* hoveredNodeForPoint(LocalFrame* frame, const IntPoint& point, bool ignorePointerEventsNone)
152 {
153     HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent;
154     if (ignorePointerEventsNone)
155         hitType |= HitTestRequest::IgnorePointerEventsNone;
156     HitTestRequest request(hitType);
157     HitTestResult result(frame->view()->windowToContents(point));
158     frame->contentRenderer()->hitTest(request, result);
159     Node* node = result.innerPossiblyPseudoNode();
160     while (node && node->nodeType() == Node::TEXT_NODE)
161         node = node->parentNode();
162     return node;
163 }
164 
hoveredNodeForEvent(LocalFrame * frame,const PlatformGestureEvent & event,bool ignorePointerEventsNone)165 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone)
166 {
167     return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
168 }
169 
hoveredNodeForEvent(LocalFrame * frame,const PlatformMouseEvent & event,bool ignorePointerEventsNone)170 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone)
171 {
172     return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
173 }
174 
hoveredNodeForEvent(LocalFrame * frame,const PlatformTouchEvent & event,bool ignorePointerEventsNone)175 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone)
176 {
177     const Vector<PlatformTouchPoint>& points = event.touchPoints();
178     if (!points.size())
179         return 0;
180     return hoveredNodeForPoint(frame, roundedIntPoint(points[0].pos()), ignorePointerEventsNone);
181 }
182 
183 class RevalidateStyleAttributeTask FINAL : public NoBaseWillBeGarbageCollectedFinalized<RevalidateStyleAttributeTask> {
184     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
185 public:
186     explicit RevalidateStyleAttributeTask(InspectorDOMAgent*);
187     void scheduleFor(Element*);
reset()188     void reset() { m_timer.stop(); }
189     void onTimer(Timer<RevalidateStyleAttributeTask>*);
190     void trace(Visitor*);
191 
192 private:
193     RawPtrWillBeMember<InspectorDOMAgent> m_domAgent;
194     Timer<RevalidateStyleAttributeTask> m_timer;
195     WillBeHeapHashSet<RefPtrWillBeMember<Element> > m_elements;
196 };
197 
RevalidateStyleAttributeTask(InspectorDOMAgent * domAgent)198 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
199     : m_domAgent(domAgent)
200     , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
201 {
202 }
203 
scheduleFor(Element * element)204 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
205 {
206     m_elements.add(element);
207     if (!m_timer.isActive())
208         m_timer.startOneShot(0, FROM_HERE);
209 }
210 
onTimer(Timer<RevalidateStyleAttributeTask> *)211 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
212 {
213     // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
214     WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
215     for (WillBePersistentHeapHashSet<RefPtrWillBeMember<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
216         elements.append(it->get());
217     m_domAgent->styleAttributeInvalidated(elements);
218 
219     m_elements.clear();
220 }
221 
trace(Visitor * visitor)222 void RevalidateStyleAttributeTask::trace(Visitor* visitor)
223 {
224     visitor->trace(m_domAgent);
225 #if ENABLE(OILPAN)
226     visitor->trace(m_elements);
227 #endif
228 }
229 
toErrorString(ExceptionState & exceptionState)230 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState)
231 {
232     if (exceptionState.hadException())
233         return DOMException::getErrorName(exceptionState.code()) + " " + exceptionState.message();
234     return "";
235 }
236 
InspectorDOMAgent(InspectorPageAgent * pageAgent,InjectedScriptManager * injectedScriptManager,InspectorOverlay * overlay)237 InspectorDOMAgent::InspectorDOMAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
238     : InspectorBaseAgent<InspectorDOMAgent>("DOM")
239     , m_pageAgent(pageAgent)
240     , m_injectedScriptManager(injectedScriptManager)
241     , m_overlay(overlay)
242     , m_frontend(0)
243     , m_domListener(nullptr)
244     , m_documentNodeToIdMap(adoptPtrWillBeNoop(new NodeToIdMap()))
245     , m_lastNodeId(1)
246     , m_searchingForNode(NotSearching)
247     , m_suppressAttributeModifiedEvent(false)
248     , m_listener(nullptr)
249 {
250 }
251 
~InspectorDOMAgent()252 InspectorDOMAgent::~InspectorDOMAgent()
253 {
254 #if !ENABLE(OILPAN)
255     reset();
256     ASSERT(m_searchingForNode == NotSearching);
257 #endif
258 }
259 
setFrontend(InspectorFrontend * frontend)260 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
261 {
262     ASSERT(!m_frontend);
263     m_history = adoptPtrWillBeNoop(new InspectorHistory());
264     m_domEditor = adoptPtrWillBeNoop(new DOMEditor(m_history.get()));
265 
266     m_frontend = frontend->dom();
267     m_instrumentingAgents->setInspectorDOMAgent(this);
268     m_document = m_pageAgent->mainFrame()->document();
269 }
270 
clearFrontend()271 void InspectorDOMAgent::clearFrontend()
272 {
273     ASSERT(m_frontend);
274 
275     m_history.clear();
276     m_domEditor.clear();
277 
278     ErrorString error;
279     setSearchingForNode(&error, NotSearching, 0);
280     hideHighlight(&error);
281 
282     m_frontend = 0;
283     m_instrumentingAgents->setInspectorDOMAgent(0);
284     disable(0);
285     reset();
286 }
287 
restore()288 void InspectorDOMAgent::restore()
289 {
290     if (!enabled())
291         return;
292     innerEnable();
293     notifyDocumentUpdated();
294 }
295 
documents()296 WillBeHeapVector<RawPtrWillBeMember<Document> > InspectorDOMAgent::documents()
297 {
298     WillBeHeapVector<RawPtrWillBeMember<Document> > result;
299     for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
300         if (!frame->isLocalFrame())
301             continue;
302         Document* document = toLocalFrame(frame)->document();
303         if (!document)
304             continue;
305         result.append(document);
306     }
307     return result;
308 }
309 
reset()310 void InspectorDOMAgent::reset()
311 {
312     discardFrontendBindings();
313     m_document = nullptr;
314 }
315 
setDOMListener(DOMListener * listener)316 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
317 {
318     m_domListener = listener;
319 }
320 
setDocument(Document * doc)321 void InspectorDOMAgent::setDocument(Document* doc)
322 {
323     if (doc == m_document.get())
324         return;
325 
326     reset();
327 
328     m_document = doc;
329 
330     if (!enabled())
331         return;
332 
333     // Immediately communicate 0 document or document that has finished loading.
334     if (!doc || !doc->parsing())
335         m_frontend->documentUpdated();
336 }
337 
releaseDanglingNodes()338 void InspectorDOMAgent::releaseDanglingNodes()
339 {
340     m_danglingNodeToIdMaps.clear();
341 }
342 
bind(Node * node,NodeToIdMap * nodesMap)343 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
344 {
345     int id = nodesMap->get(node);
346     if (id)
347         return id;
348     id = m_lastNodeId++;
349     nodesMap->set(node, id);
350     m_idToNode.set(id, node);
351     m_idToNodesMap.set(id, nodesMap);
352     return id;
353 }
354 
unbind(Node * node,NodeToIdMap * nodesMap)355 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
356 {
357     int id = nodesMap->get(node);
358     if (!id)
359         return;
360 
361     m_idToNode.remove(id);
362     m_idToNodesMap.remove(id);
363 
364     if (node->isFrameOwnerElement()) {
365         Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument();
366         if (m_domListener)
367             m_domListener->didRemoveDocument(contentDocument);
368         if (contentDocument)
369             unbind(contentDocument, nodesMap);
370     }
371 
372     for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot())
373         unbind(root, nodesMap);
374 
375     if (node->isElementNode()) {
376         Element* element = toElement(node);
377         if (element->pseudoElement(BEFORE))
378             unbind(element->pseudoElement(BEFORE), nodesMap);
379         if (element->pseudoElement(AFTER))
380             unbind(element->pseudoElement(AFTER), nodesMap);
381 
382         if (isHTMLLinkElement(*element)) {
383             HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
384             if (linkElement.isImport() && linkElement.import())
385                 unbind(linkElement.import(), nodesMap);
386         }
387     }
388 
389     nodesMap->remove(node);
390     if (m_domListener)
391         m_domListener->didRemoveDOMNode(node);
392 
393     bool childrenRequested = m_childrenRequested.contains(id);
394     if (childrenRequested) {
395         // Unbind subtree known to client recursively.
396         m_childrenRequested.remove(id);
397         Node* child = innerFirstChild(node);
398         while (child) {
399             unbind(child, nodesMap);
400             child = innerNextSibling(child);
401         }
402     }
403     if (nodesMap == m_documentNodeToIdMap.get())
404         m_cachedChildCount.remove(id);
405 }
406 
assertNode(ErrorString * errorString,int nodeId)407 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
408 {
409     Node* node = nodeForId(nodeId);
410     if (!node) {
411         *errorString = "Could not find node with given id";
412         return 0;
413     }
414     return node;
415 }
416 
assertDocument(ErrorString * errorString,int nodeId)417 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId)
418 {
419     Node* node = assertNode(errorString, nodeId);
420     if (!node)
421         return 0;
422 
423     if (!(node->isDocumentNode())) {
424         *errorString = "Document is not available";
425         return 0;
426     }
427     return toDocument(node);
428 }
429 
assertElement(ErrorString * errorString,int nodeId)430 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
431 {
432     Node* node = assertNode(errorString, nodeId);
433     if (!node)
434         return 0;
435 
436     if (!node->isElementNode()) {
437         *errorString = "Node is not an Element";
438         return 0;
439     }
440     return toElement(node);
441 }
442 
userAgentShadowRoot(Node * node)443 static ShadowRoot* userAgentShadowRoot(Node* node)
444 {
445     if (!node || !node->isInShadowTree())
446         return 0;
447 
448     Node* candidate = node;
449     while (candidate && !candidate->isShadowRoot())
450         candidate = candidate->parentOrShadowHostNode();
451     ASSERT(candidate);
452     ShadowRoot* shadowRoot = toShadowRoot(candidate);
453 
454     return shadowRoot->type() == ShadowRoot::UserAgentShadowRoot ? shadowRoot : 0;
455 }
456 
assertEditableNode(ErrorString * errorString,int nodeId)457 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId)
458 {
459     Node* node = assertNode(errorString, nodeId);
460     if (!node)
461         return 0;
462 
463     if (node->isInShadowTree()) {
464         if (node->isShadowRoot()) {
465             *errorString = "Cannot edit shadow roots";
466             return 0;
467         }
468         if (userAgentShadowRoot(node)) {
469             *errorString = "Cannot edit nodes from user-agent shadow trees";
470             return 0;
471         }
472     }
473 
474     if (node->isPseudoElement()) {
475         *errorString = "Cannot edit pseudo elements";
476         return 0;
477     }
478 
479     return node;
480 }
481 
assertEditableChildNode(ErrorString * errorString,Element * parentElement,int nodeId)482 Node* InspectorDOMAgent::assertEditableChildNode(ErrorString* errorString, Element* parentElement, int nodeId)
483 {
484     Node* node = assertEditableNode(errorString, nodeId);
485     if (!node)
486         return 0;
487     if (node->parentNode() != parentElement) {
488         *errorString = "Anchor node must be child of the target element";
489         return 0;
490     }
491     return node;
492 }
493 
assertEditableElement(ErrorString * errorString,int nodeId)494 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId)
495 {
496     Element* element = assertElement(errorString, nodeId);
497     if (!element)
498         return 0;
499 
500     if (element->isInShadowTree() && userAgentShadowRoot(element)) {
501         *errorString = "Cannot edit elements from user-agent shadow trees";
502         return 0;
503     }
504 
505     if (element->isPseudoElement()) {
506         *errorString = "Cannot edit pseudo elements";
507         return 0;
508     }
509 
510     return element;
511 }
512 
innerEnable()513 void InspectorDOMAgent::innerEnable()
514 {
515     m_state->setBoolean(DOMAgentState::domAgentEnabled, true);
516     if (m_listener)
517         m_listener->domAgentWasEnabled();
518 }
519 
enable(ErrorString *)520 void InspectorDOMAgent::enable(ErrorString*)
521 {
522     if (enabled())
523         return;
524     innerEnable();
525     notifyDocumentUpdated();
526 }
527 
notifyDocumentUpdated()528 void InspectorDOMAgent::notifyDocumentUpdated()
529 {
530     m_document = nullptr;
531     setDocument(m_pageAgent->mainFrame()->document());
532 }
533 
enabled() const534 bool InspectorDOMAgent::enabled() const
535 {
536     return m_state->getBoolean(DOMAgentState::domAgentEnabled);
537 }
538 
disable(ErrorString * errorString)539 void InspectorDOMAgent::disable(ErrorString* errorString)
540 {
541     if (!enabled()) {
542         if (errorString)
543             *errorString = "DOM agent hasn't been enabled";
544         return;
545     }
546     m_state->setBoolean(DOMAgentState::domAgentEnabled, false);
547     reset();
548     if (m_listener)
549         m_listener->domAgentWasDisabled();
550 }
551 
getDocument(ErrorString * errorString,RefPtr<TypeBuilder::DOM::Node> & root)552 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root)
553 {
554     // Backward compatibility. Mark agent as enabled when it requests document.
555     if (!enabled())
556         innerEnable();
557 
558     if (!m_document) {
559         *errorString = "Document is not available";
560         return;
561     }
562 
563     discardFrontendBindings();
564 
565     root = buildObjectForNode(m_document.get(), 2, m_documentNodeToIdMap.get());
566 }
567 
pushChildNodesToFrontend(int nodeId,int depth)568 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
569 {
570     Node* node = nodeForId(nodeId);
571     if (!node || (!node->isElementNode() && !node->isDocumentNode() && !node->isDocumentFragment()))
572         return;
573 
574     NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
575 
576     if (m_childrenRequested.contains(nodeId)) {
577         if (depth <= 1)
578             return;
579 
580         depth--;
581 
582         for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
583             int childNodeId = nodeMap->get(node);
584             ASSERT(childNodeId);
585             pushChildNodesToFrontend(childNodeId, depth);
586         }
587 
588         return;
589     }
590 
591     RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap);
592     m_frontend->setChildNodes(nodeId, children.release());
593 }
594 
discardFrontendBindings()595 void InspectorDOMAgent::discardFrontendBindings()
596 {
597     if (m_history)
598         m_history->reset();
599     m_searchResults.clear();
600     m_documentNodeToIdMap->clear();
601     m_idToNode.clear();
602     m_idToNodesMap.clear();
603     releaseDanglingNodes();
604     m_childrenRequested.clear();
605     m_cachedChildCount.clear();
606     if (m_revalidateStyleAttrTask)
607         m_revalidateStyleAttrTask->reset();
608 }
609 
nodeForId(int id)610 Node* InspectorDOMAgent::nodeForId(int id)
611 {
612     if (!id)
613         return 0;
614 
615     WillBeHeapHashMap<int, RawPtrWillBeMember<Node> >::iterator it = m_idToNode.find(id);
616     if (it != m_idToNode.end())
617         return it->value;
618     return 0;
619 }
620 
requestChildNodes(ErrorString * errorString,int nodeId,const int * depth)621 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth)
622 {
623     int sanitizedDepth;
624 
625     if (!depth)
626         sanitizedDepth = 1;
627     else if (*depth == -1)
628         sanitizedDepth = INT_MAX;
629     else if (*depth > 0)
630         sanitizedDepth = *depth;
631     else {
632         *errorString = "Please provide a positive integer as a depth or -1 for entire subtree";
633         return;
634     }
635 
636     pushChildNodesToFrontend(nodeId, sanitizedDepth);
637 }
638 
querySelector(ErrorString * errorString,int nodeId,const String & selectors,int * elementId)639 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
640 {
641     *elementId = 0;
642     Node* node = assertNode(errorString, nodeId);
643     if (!node || !node->isContainerNode())
644         return;
645 
646     TrackExceptionState exceptionState;
647     RefPtrWillBeRawPtr<Element> element = toContainerNode(node)->querySelector(AtomicString(selectors), exceptionState);
648     if (exceptionState.hadException()) {
649         *errorString = "DOM Error while querying";
650         return;
651     }
652 
653     if (element)
654         *elementId = pushNodePathToFrontend(element.get());
655 }
656 
querySelectorAll(ErrorString * errorString,int nodeId,const String & selectors,RefPtr<TypeBuilder::Array<int>> & result)657 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result)
658 {
659     Node* node = assertNode(errorString, nodeId);
660     if (!node || !node->isContainerNode())
661         return;
662 
663     TrackExceptionState exceptionState;
664     RefPtrWillBeRawPtr<StaticElementList> elements = toContainerNode(node)->querySelectorAll(AtomicString(selectors), exceptionState);
665     if (exceptionState.hadException()) {
666         *errorString = "DOM Error while querying";
667         return;
668     }
669 
670     result = TypeBuilder::Array<int>::create();
671 
672     for (unsigned i = 0; i < elements->length(); ++i)
673         result->addItem(pushNodePathToFrontend(elements->item(i)));
674 }
675 
pushNodePathToFrontend(Node * nodeToPush)676 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
677 {
678     ASSERT(nodeToPush);  // Invalid input
679 
680     if (!m_document)
681         return 0;
682     if (!m_documentNodeToIdMap->contains(m_document))
683         return 0;
684 
685     // Return id in case the node is known.
686     int result = m_documentNodeToIdMap->get(nodeToPush);
687     if (result)
688         return result;
689 
690     Node* node = nodeToPush;
691     WillBeHeapVector<RawPtrWillBeMember<Node> > path;
692     NodeToIdMap* danglingMap = 0;
693 
694     while (true) {
695         Node* parent = innerParentNode(node);
696         if (!parent) {
697             // Node being pushed is detached -> push subtree root.
698             OwnPtrWillBeRawPtr<NodeToIdMap> newMap = adoptPtrWillBeNoop(new NodeToIdMap);
699             danglingMap = newMap.get();
700             m_danglingNodeToIdMaps.append(newMap.release());
701             RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
702             children->addItem(buildObjectForNode(node, 0, danglingMap));
703             m_frontend->setChildNodes(0, children);
704             break;
705         } else {
706             path.append(parent);
707             if (m_documentNodeToIdMap->get(parent))
708                 break;
709             node = parent;
710         }
711     }
712 
713     NodeToIdMap* map = danglingMap ? danglingMap : m_documentNodeToIdMap.get();
714     for (int i = path.size() - 1; i >= 0; --i) {
715         int nodeId = map->get(path.at(i).get());
716         ASSERT(nodeId);
717         pushChildNodesToFrontend(nodeId);
718     }
719     return map->get(nodeToPush);
720 }
721 
boundNodeId(Node * node)722 int InspectorDOMAgent::boundNodeId(Node* node)
723 {
724     return m_documentNodeToIdMap->get(node);
725 }
726 
setAttributeValue(ErrorString * errorString,int elementId,const String & name,const String & value)727 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value)
728 {
729     Element* element = assertEditableElement(errorString, elementId);
730     if (!element)
731         return;
732 
733     m_domEditor->setAttribute(element, name, value, errorString);
734 }
735 
setAttributesAsText(ErrorString * errorString,int elementId,const String & text,const String * const name)736 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
737 {
738     Element* element = assertEditableElement(errorString, elementId);
739     if (!element)
740         return;
741 
742     String markup = "<span " + text + "></span>";
743     RefPtrWillBeRawPtr<DocumentFragment> fragment = element->document().createDocumentFragment();
744 
745     bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement();
746     // Not all elements can represent the context (i.e. IFRAME), hence using document.body.
747     if (shouldIgnoreCase && element->document().body())
748         fragment->parseHTML(markup, element->document().body(), AllowScriptingContent);
749     else
750         fragment->parseXML(markup, 0, AllowScriptingContent);
751 
752     Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0;
753     if (!parsedElement) {
754         *errorString = "Could not parse value as attributes";
755         return;
756     }
757 
758     String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String();
759 
760     AttributeCollection attributes = parsedElement->attributes();
761     if (attributes.isEmpty() && name) {
762         m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
763         return;
764     }
765 
766     bool foundOriginalAttribute = false;
767     AttributeCollection::iterator end = attributes.end();
768     for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
769         // Add attribute pair
770         String attributeName = it->name().toString();
771         if (shouldIgnoreCase)
772             attributeName = attributeName.lower();
773         foundOriginalAttribute |= name && attributeName == caseAdjustedName;
774         if (!m_domEditor->setAttribute(element, attributeName, it->value(), errorString))
775             return;
776     }
777 
778     if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
779         m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
780 }
781 
removeAttribute(ErrorString * errorString,int elementId,const String & name)782 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
783 {
784     Element* element = assertEditableElement(errorString, elementId);
785     if (!element)
786         return;
787 
788     m_domEditor->removeAttribute(element, name, errorString);
789 }
790 
removeNode(ErrorString * errorString,int nodeId)791 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
792 {
793     Node* node = assertEditableNode(errorString, nodeId);
794     if (!node)
795         return;
796 
797     ContainerNode* parentNode = node->parentNode();
798     if (!parentNode) {
799         *errorString = "Cannot remove detached node";
800         return;
801     }
802 
803     m_domEditor->removeChild(parentNode, node, errorString);
804 }
805 
setNodeName(ErrorString * errorString,int nodeId,const String & tagName,int * newId)806 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
807 {
808     *newId = 0;
809 
810     Node* oldNode = nodeForId(nodeId);
811     if (!oldNode || !oldNode->isElementNode())
812         return;
813 
814     TrackExceptionState exceptionState;
815     RefPtrWillBeRawPtr<Element> newElem = oldNode->document().createElement(AtomicString(tagName), exceptionState);
816     if (exceptionState.hadException())
817         return;
818 
819     // Copy over the original node's attributes.
820     newElem->cloneAttributesFromElement(*toElement(oldNode));
821 
822     // Copy over the original node's children.
823     for (Node* child = oldNode->firstChild(); child; child = oldNode->firstChild()) {
824         if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
825             return;
826     }
827 
828     // Replace the old node with the new node
829     ContainerNode* parent = oldNode->parentNode();
830     if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
831         return;
832     if (!m_domEditor->removeChild(parent, oldNode, errorString))
833         return;
834 
835     *newId = pushNodePathToFrontend(newElem.get());
836     if (m_childrenRequested.contains(nodeId))
837         pushChildNodesToFrontend(*newId);
838 }
839 
getOuterHTML(ErrorString * errorString,int nodeId,WTF::String * outerHTML)840 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
841 {
842     Node* node = assertNode(errorString, nodeId);
843     if (!node)
844         return;
845 
846     *outerHTML = createMarkup(node);
847 }
848 
setOuterHTML(ErrorString * errorString,int nodeId,const String & outerHTML)849 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
850 {
851     if (!nodeId) {
852         ASSERT(m_document);
853         DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get());
854         domPatchSupport.patchDocument(outerHTML);
855         return;
856     }
857 
858     Node* node = assertEditableNode(errorString, nodeId);
859     if (!node)
860         return;
861 
862     Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument();
863     if (!document || (!document->isHTMLDocument() && !document->isXMLDocument())) {
864         *errorString = "Not an HTML/XML document";
865         return;
866     }
867 
868     Node* newNode = 0;
869     if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString))
870         return;
871 
872     if (!newNode) {
873         // The only child node has been deleted.
874         return;
875     }
876 
877     int newId = pushNodePathToFrontend(newNode);
878 
879     bool childrenRequested = m_childrenRequested.contains(nodeId);
880     if (childrenRequested)
881         pushChildNodesToFrontend(newId);
882 }
883 
setNodeValue(ErrorString * errorString,int nodeId,const String & value)884 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
885 {
886     Node* node = assertEditableNode(errorString, nodeId);
887     if (!node)
888         return;
889 
890     if (node->nodeType() != Node::TEXT_NODE) {
891         *errorString = "Can only set value of text nodes";
892         return;
893     }
894 
895     m_domEditor->replaceWholeText(toText(node), value, errorString);
896 }
897 
getEventListenersForNode(ErrorString * errorString,int nodeId,const String * objectGroup,RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener>> & listenersArray)898 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
899 {
900     listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
901     Node* node = assertNode(errorString, nodeId);
902     if (!node)
903         return;
904     Vector<EventListenerInfo> eventInformation;
905     getEventListeners(node, eventInformation, true);
906 
907     // Get Capturing Listeners (in this order)
908     size_t eventInformationLength = eventInformation.size();
909     for (size_t i = 0; i < eventInformationLength; ++i) {
910         const EventListenerInfo& info = eventInformation[i];
911         const EventListenerVector& vector = info.eventListenerVector;
912         for (size_t j = 0; j < vector.size(); ++j) {
913             const RegisteredEventListener& listener = vector[j];
914             if (listener.useCapture) {
915                 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
916                 if (listenerObject)
917                     listenersArray->addItem(listenerObject);
918             }
919         }
920     }
921 
922     // Get Bubbling Listeners (reverse order)
923     for (size_t i = eventInformationLength; i; --i) {
924         const EventListenerInfo& info = eventInformation[i - 1];
925         const EventListenerVector& vector = info.eventListenerVector;
926         for (size_t j = 0; j < vector.size(); ++j) {
927             const RegisteredEventListener& listener = vector[j];
928             if (!listener.useCapture) {
929                 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
930                 if (listenerObject)
931                     listenersArray->addItem(listenerObject);
932             }
933         }
934     }
935 }
936 
getEventListeners(EventTarget * target,Vector<EventListenerInfo> & eventInformation,bool includeAncestors)937 void InspectorDOMAgent::getEventListeners(EventTarget* target, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
938 {
939     // The Node's Ancestors including self.
940     Vector<EventTarget*> ancestors;
941     ancestors.append(target);
942     if (includeAncestors) {
943         Node* node = target->toNode();
944         for (ContainerNode* ancestor = node ? node->parentOrShadowHostNode() : 0; ancestor; ancestor = ancestor->parentOrShadowHostNode())
945             ancestors.append(ancestor);
946     }
947 
948     // Nodes and their Listeners for the concerned event types (order is top to bottom)
949     for (size_t i = ancestors.size(); i; --i) {
950         EventTarget* ancestor = ancestors[i - 1];
951         Vector<AtomicString> eventTypes = ancestor->eventTypes();
952         for (size_t j = 0; j < eventTypes.size(); ++j) {
953             AtomicString& type = eventTypes[j];
954             const EventListenerVector& listeners = ancestor->getEventListeners(type);
955             EventListenerVector filteredListeners;
956             filteredListeners.reserveCapacity(listeners.size());
957             for (size_t k = 0; k < listeners.size(); ++k) {
958                 if (listeners[k].listener->type() == EventListener::JSEventListenerType)
959                     filteredListeners.append(listeners[k]);
960             }
961             if (!filteredListeners.isEmpty())
962                 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners));
963         }
964     }
965 }
966 
nextNodeWithShadowDOMInMind(const Node & current,const Node * stayWithin,bool includeUserAgentShadowDOM)967 static Node* nextNodeWithShadowDOMInMind(const Node& current, const Node* stayWithin, bool includeUserAgentShadowDOM)
968 {
969     // At first traverse the subtree.
970     if (current.isElementNode()) {
971         const Element& element = toElement(current);
972         ElementShadow* elementShadow = element.shadow();
973         if (elementShadow) {
974             ShadowRoot* shadowRoot = elementShadow->youngestShadowRoot();
975             if (shadowRoot) {
976                 if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot || includeUserAgentShadowDOM)
977                     return shadowRoot;
978             }
979         }
980     }
981     if (current.hasChildren())
982         return current.firstChild();
983 
984     // Then traverse siblings of the node itself and its ancestors.
985     const Node* node = &current;
986     do {
987         if (node == stayWithin)
988             return 0;
989         if (node->isShadowRoot()) {
990             const ShadowRoot* shadowRoot = toShadowRoot(node);
991             if (shadowRoot->olderShadowRoot())
992                 return shadowRoot->olderShadowRoot();
993             Node* host = shadowRoot->host();
994             if (host && host->hasChildren())
995                 return host->firstChild();
996         }
997         if (node->nextSibling())
998             return node->nextSibling();
999         node = node->isShadowRoot() ? toShadowRoot(node)->host() : node->parentNode();
1000     } while (node);
1001 
1002     return 0;
1003 }
1004 
performSearch(ErrorString *,const String & whitespaceTrimmedQuery,const bool * optionalIncludeUserAgentShadowDOM,String * searchId,int * resultCount)1005 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, const bool* optionalIncludeUserAgentShadowDOM, String* searchId, int* resultCount)
1006 {
1007     // FIXME: Few things are missing here:
1008     // 1) Search works with node granularity - number of matches within node is not calculated.
1009     // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
1010     //    is sufficient.
1011 
1012     bool includeUserAgentShadowDOM = optionalIncludeUserAgentShadowDOM ? *optionalIncludeUserAgentShadowDOM : false;
1013 
1014     unsigned queryLength = whitespaceTrimmedQuery.length();
1015     bool startTagFound = !whitespaceTrimmedQuery.find('<');
1016     bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
1017     bool startQuoteFound = !whitespaceTrimmedQuery.find('"');
1018     bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength;
1019     bool exactAttributeMatch = startQuoteFound && endQuoteFound;
1020 
1021     String tagNameQuery = whitespaceTrimmedQuery;
1022     String attributeQuery = whitespaceTrimmedQuery;
1023     if (startTagFound)
1024         tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
1025     if (endTagFound)
1026         tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
1027     if (startQuoteFound)
1028         attributeQuery = attributeQuery.right(attributeQuery.length() - 1);
1029     if (endQuoteFound)
1030         attributeQuery = attributeQuery.left(attributeQuery.length() - 1);
1031 
1032     WillBeHeapVector<RawPtrWillBeMember<Document> > docs = documents();
1033     WillBeHeapListHashSet<RawPtrWillBeMember<Node> > resultCollector;
1034 
1035     for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1036         Document* document = *it;
1037         Node* documentElement = document->documentElement();
1038         Node* node = documentElement;
1039         if (!node)
1040             continue;
1041 
1042         // Manual plain text search.
1043         for (node = nextNodeWithShadowDOMInMind(*node, documentElement, includeUserAgentShadowDOM); node; node = nextNodeWithShadowDOMInMind(*node, documentElement, includeUserAgentShadowDOM)) {
1044             switch (node->nodeType()) {
1045             case Node::TEXT_NODE:
1046             case Node::COMMENT_NODE:
1047             case Node::CDATA_SECTION_NODE: {
1048                 String text = node->nodeValue();
1049                 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound)
1050                     resultCollector.add(node);
1051                 break;
1052             }
1053             case Node::ELEMENT_NODE: {
1054                 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound))
1055                     || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery))
1056                     || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false))
1057                     || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) {
1058                     resultCollector.add(node);
1059                     break;
1060                 }
1061                 // Go through all attributes and serialize them.
1062                 const Element* element = toElement(node);
1063                 AttributeCollection attributes = element->attributes();
1064                 AttributeCollection::iterator end = attributes.end();
1065                 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
1066                     // Add attribute pair
1067                     if (it->localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) {
1068                         resultCollector.add(node);
1069                         break;
1070                     }
1071                     size_t foundPosition = it->value().find(attributeQuery, 0, false);
1072                     if (foundPosition != kNotFound) {
1073                         if (!exactAttributeMatch || (!foundPosition && it->value().length() == attributeQuery.length())) {
1074                             resultCollector.add(node);
1075                             break;
1076                         }
1077                     }
1078                 }
1079                 break;
1080             }
1081             default:
1082                 break;
1083             }
1084         }
1085 
1086         // XPath evaluation
1087         for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1088             Document* document = *it;
1089             ASSERT(document);
1090             TrackExceptionState exceptionState;
1091             RefPtrWillBeRawPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(*document, whitespaceTrimmedQuery, document, nullptr, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState);
1092             if (exceptionState.hadException() || !result)
1093                 continue;
1094 
1095             unsigned long size = result->snapshotLength(exceptionState);
1096             for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) {
1097                 Node* node = result->snapshotItem(i, exceptionState);
1098                 if (exceptionState.hadException())
1099                     break;
1100 
1101                 if (node->nodeType() == Node::ATTRIBUTE_NODE)
1102                     node = toAttr(node)->ownerElement();
1103                 resultCollector.add(node);
1104             }
1105         }
1106 
1107         // Selector evaluation
1108         for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1109             Document* document = *it;
1110             TrackExceptionState exceptionState;
1111             RefPtrWillBeRawPtr<StaticElementList> elementList = document->querySelectorAll(AtomicString(whitespaceTrimmedQuery), exceptionState);
1112             if (exceptionState.hadException() || !elementList)
1113                 continue;
1114 
1115             unsigned size = elementList->length();
1116             for (unsigned i = 0; i < size; ++i)
1117                 resultCollector.add(elementList->item(i));
1118         }
1119     }
1120 
1121     *searchId = IdentifiersFactory::createIdentifier();
1122     WillBeHeapVector<RefPtrWillBeMember<Node> >* resultsIt = &m_searchResults.add(*searchId, WillBeHeapVector<RefPtrWillBeMember<Node> >()).storedValue->value;
1123 
1124     for (WillBeHeapListHashSet<RawPtrWillBeMember<Node> >::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
1125         resultsIt->append(*it);
1126 
1127     *resultCount = resultsIt->size();
1128 }
1129 
getSearchResults(ErrorString * errorString,const String & searchId,int fromIndex,int toIndex,RefPtr<TypeBuilder::Array<int>> & nodeIds)1130 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds)
1131 {
1132     SearchResults::iterator it = m_searchResults.find(searchId);
1133     if (it == m_searchResults.end()) {
1134         *errorString = "No search session with given id found";
1135         return;
1136     }
1137 
1138     int size = it->value.size();
1139     if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1140         *errorString = "Invalid search result range";
1141         return;
1142     }
1143 
1144     nodeIds = TypeBuilder::Array<int>::create();
1145     for (int i = fromIndex; i < toIndex; ++i)
1146         nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1147 }
1148 
discardSearchResults(ErrorString *,const String & searchId)1149 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
1150 {
1151     m_searchResults.remove(searchId);
1152 }
1153 
handleMousePress()1154 bool InspectorDOMAgent::handleMousePress()
1155 {
1156     if (m_searchingForNode == NotSearching)
1157         return false;
1158 
1159     if (Node* node = m_overlay->highlightedNode()) {
1160         inspect(node);
1161         return true;
1162     }
1163     return false;
1164 }
1165 
handleGestureEvent(LocalFrame * frame,const PlatformGestureEvent & event)1166 bool InspectorDOMAgent::handleGestureEvent(LocalFrame* frame, const PlatformGestureEvent& event)
1167 {
1168     if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap)
1169         return false;
1170     Node* node = hoveredNodeForEvent(frame, event, false);
1171     if (node && m_inspectModeHighlightConfig) {
1172         m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1173         inspect(node);
1174         return true;
1175     }
1176     return false;
1177 }
1178 
handleTouchEvent(LocalFrame * frame,const PlatformTouchEvent & event)1179 bool InspectorDOMAgent::handleTouchEvent(LocalFrame* frame, const PlatformTouchEvent& event)
1180 {
1181     if (m_searchingForNode == NotSearching)
1182         return false;
1183     Node* node = hoveredNodeForEvent(frame, event, false);
1184     if (node && m_inspectModeHighlightConfig) {
1185         m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1186         inspect(node);
1187         return true;
1188     }
1189     return false;
1190 }
1191 
inspect(Node * inspectedNode)1192 void InspectorDOMAgent::inspect(Node* inspectedNode)
1193 {
1194     if (!m_frontend || !inspectedNode)
1195         return;
1196 
1197     Node* node = inspectedNode;
1198     while (node && !node->isElementNode() && !node->isDocumentNode() && !node->isDocumentFragment())
1199         node = node->parentOrShadowHostNode();
1200 
1201     if (!node)
1202         return;
1203 
1204     int nodeId = pushNodePathToFrontend(node);
1205     if (nodeId)
1206         m_frontend->inspectNodeRequested(nodeId);
1207 }
1208 
handleMouseMove(LocalFrame * frame,const PlatformMouseEvent & event)1209 bool InspectorDOMAgent::handleMouseMove(LocalFrame* frame, const PlatformMouseEvent& event)
1210 {
1211     if (m_searchingForNode == NotSearching)
1212         return false;
1213 
1214     if (!frame->view() || !frame->contentRenderer())
1215         return true;
1216     Node* node = hoveredNodeForEvent(frame, event, event.shiftKey());
1217 
1218     // Do not highlight within UA shadow root unless requested.
1219     if (m_searchingForNode != SearchingForUAShadow) {
1220         ShadowRoot* uaShadowRoot = userAgentShadowRoot(node);
1221         if (uaShadowRoot)
1222             node = uaShadowRoot->host();
1223     }
1224 
1225     // Shadow roots don't have boxes - use host element instead.
1226     if (node && node->isShadowRoot())
1227         node = node->parentOrShadowHostNode();
1228 
1229     if (!node)
1230         return true;
1231 
1232     Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0;
1233     if (eventTarget == node)
1234         eventTarget = 0;
1235 
1236     if (node && m_inspectModeHighlightConfig)
1237         m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig, event.ctrlKey() || event.metaKey());
1238     return true;
1239 }
1240 
setSearchingForNode(ErrorString * errorString,SearchMode searchMode,JSONObject * highlightInspectorObject)1241 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject)
1242 {
1243     if (m_searchingForNode == searchMode)
1244         return;
1245 
1246     m_searchingForNode = searchMode;
1247     m_overlay->setInspectModeEnabled(searchMode != NotSearching);
1248     if (searchMode != NotSearching) {
1249         m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1250         if (!m_inspectModeHighlightConfig)
1251             return;
1252     } else
1253         hideHighlight(errorString);
1254 }
1255 
highlightConfigFromInspectorObject(ErrorString * errorString,JSONObject * highlightInspectorObject)1256 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject)
1257 {
1258     if (!highlightInspectorObject) {
1259         *errorString = "Internal error: highlight configuration parameter is missing";
1260         return nullptr;
1261     }
1262 
1263     OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1264     bool showInfo = false; // Default: false (do not show a tooltip).
1265     highlightInspectorObject->getBoolean("showInfo", &showInfo);
1266     highlightConfig->showInfo = showInfo;
1267     bool showRulers = false; // Default: false (do not show rulers).
1268     highlightInspectorObject->getBoolean("showRulers", &showRulers);
1269     highlightConfig->showRulers = showRulers;
1270     bool showExtensionLines = false; // Default: false (do not show extension lines).
1271     highlightInspectorObject->getBoolean("showExtensionLines", &showExtensionLines);
1272     highlightConfig->showExtensionLines = showExtensionLines;
1273     highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1274     highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1275     highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1276     highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1277     highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1278     highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject);
1279     highlightConfig->shape = parseConfigColor("shapeColor", highlightInspectorObject);
1280     highlightConfig->shapeMargin = parseConfigColor("shapeMarginColor", highlightInspectorObject);
1281 
1282     return highlightConfig.release();
1283 }
1284 
setInspectModeEnabled(ErrorString * errorString,bool enabled,const bool * inspectUAShadowDOM,const RefPtr<JSONObject> * highlightConfig)1285 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectUAShadowDOM, const RefPtr<JSONObject>* highlightConfig)
1286 {
1287     if (enabled && !pushDocumentUponHandlelessOperation(errorString))
1288         return;
1289     SearchMode searchMode = enabled ? (asBool(inspectUAShadowDOM) ? SearchingForUAShadow : SearchingForNormal) : NotSearching;
1290     setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0);
1291 }
1292 
highlightRect(ErrorString *,int x,int y,int width,int height,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1293 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1294 {
1295     OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height)));
1296     innerHighlightQuad(quad.release(), color, outlineColor);
1297 }
1298 
highlightQuad(ErrorString * errorString,const RefPtr<JSONArray> & quadArray,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1299 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1300 {
1301     OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad());
1302     if (!parseQuad(quadArray, quad.get())) {
1303         *errorString = "Invalid Quad format";
1304         return;
1305     }
1306     innerHighlightQuad(quad.release(), color, outlineColor);
1307 }
1308 
innerHighlightQuad(PassOwnPtr<FloatQuad> quad,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1309 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1310 {
1311     OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1312     highlightConfig->content = parseColor(color);
1313     highlightConfig->contentOutline = parseColor(outlineColor);
1314     m_overlay->highlightQuad(quad, *highlightConfig);
1315 }
1316 
highlightNode(ErrorString * errorString,const RefPtr<JSONObject> & highlightInspectorObject,const int * nodeId,const String * objectId)1317 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId)
1318 {
1319     Node* node = 0;
1320     if (nodeId) {
1321         node = assertNode(errorString, *nodeId);
1322     } else if (objectId) {
1323         InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId);
1324         node = injectedScript.nodeForObjectId(*objectId);
1325         if (!node)
1326             *errorString = "Node for given objectId not found";
1327     } else
1328         *errorString = "Either nodeId or objectId must be specified";
1329 
1330     if (!node)
1331         return;
1332 
1333     OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get());
1334     if (!highlightConfig)
1335         return;
1336 
1337     m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig, false);
1338 }
1339 
highlightFrame(ErrorString *,const String & frameId,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1340 void InspectorDOMAgent::highlightFrame(
1341     ErrorString*,
1342     const String& frameId,
1343     const RefPtr<JSONObject>* color,
1344     const RefPtr<JSONObject>* outlineColor)
1345 {
1346     LocalFrame* frame = m_pageAgent->frameForId(frameId);
1347     // FIXME: Inspector doesn't currently work cross process.
1348     if (frame && frame->deprecatedLocalOwner()) {
1349         OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1350         highlightConfig->showInfo = true; // Always show tooltips for frames.
1351         highlightConfig->content = parseColor(color);
1352         highlightConfig->contentOutline = parseColor(outlineColor);
1353         m_overlay->highlightNode(frame->deprecatedLocalOwner(), 0 /* eventTarget */, *highlightConfig, false);
1354     }
1355 }
1356 
hideHighlight(ErrorString *)1357 void InspectorDOMAgent::hideHighlight(ErrorString*)
1358 {
1359     m_overlay->hideHighlight();
1360 }
1361 
copyTo(ErrorString * errorString,int nodeId,int targetElementId,const int * const anchorNodeId,int * newNodeId)1362 void InspectorDOMAgent::copyTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1363 {
1364     Node* node = assertEditableNode(errorString, nodeId);
1365     if (!node)
1366         return;
1367 
1368     Element* targetElement = assertEditableElement(errorString, targetElementId);
1369     if (!targetElement)
1370         return;
1371 
1372     Node* anchorNode = 0;
1373     if (anchorNodeId && *anchorNodeId) {
1374         anchorNode = assertEditableChildNode(errorString, targetElement, *anchorNodeId);
1375         if (!anchorNode)
1376             return;
1377     }
1378 
1379     // The clone is deep by default.
1380     RefPtrWillBeRawPtr<Node> clonedNode = node->cloneNode(true);
1381     if (!clonedNode) {
1382         *errorString = "Failed to clone node";
1383         return;
1384     }
1385     if (!m_domEditor->insertBefore(targetElement, clonedNode, anchorNode, errorString))
1386         return;
1387 
1388     *newNodeId = pushNodePathToFrontend(clonedNode.get());
1389 }
1390 
moveTo(ErrorString * errorString,int nodeId,int targetElementId,const int * const anchorNodeId,int * newNodeId)1391 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1392 {
1393     Node* node = assertEditableNode(errorString, nodeId);
1394     if (!node)
1395         return;
1396 
1397     Element* targetElement = assertEditableElement(errorString, targetElementId);
1398     if (!targetElement)
1399         return;
1400 
1401     Node* current = targetElement;
1402     while (current) {
1403         if (current == node) {
1404             *errorString = "Unable to move node into self or descendant";
1405             return;
1406         }
1407         current = current->parentNode();
1408     }
1409 
1410     Node* anchorNode = 0;
1411     if (anchorNodeId && *anchorNodeId) {
1412         anchorNode = assertEditableChildNode(errorString, targetElement, *anchorNodeId);
1413         if (!anchorNode)
1414             return;
1415     }
1416 
1417     if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString))
1418         return;
1419 
1420     *newNodeId = pushNodePathToFrontend(node);
1421 }
1422 
undo(ErrorString * errorString)1423 void InspectorDOMAgent::undo(ErrorString* errorString)
1424 {
1425     TrackExceptionState exceptionState;
1426     m_history->undo(exceptionState);
1427     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1428 }
1429 
redo(ErrorString * errorString)1430 void InspectorDOMAgent::redo(ErrorString* errorString)
1431 {
1432     TrackExceptionState exceptionState;
1433     m_history->redo(exceptionState);
1434     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1435 }
1436 
markUndoableState(ErrorString *)1437 void InspectorDOMAgent::markUndoableState(ErrorString*)
1438 {
1439     m_history->markUndoableState();
1440 }
1441 
focus(ErrorString * errorString,int nodeId)1442 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId)
1443 {
1444     Element* element = assertElement(errorString, nodeId);
1445     if (!element)
1446         return;
1447 
1448     element->document().updateLayoutIgnorePendingStylesheets();
1449     if (!element->isFocusable()) {
1450         *errorString = "Element is not focusable";
1451         return;
1452     }
1453     element->focus();
1454 }
1455 
setFileInputFiles(ErrorString * errorString,int nodeId,const RefPtr<JSONArray> & files)1456 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files)
1457 {
1458     Node* node = assertNode(errorString, nodeId);
1459     if (!node)
1460         return;
1461     if (!isHTMLInputElement(*node) || toHTMLInputElement(*node).type() != InputTypeNames::file) {
1462         *errorString = "Node is not a file input element";
1463         return;
1464     }
1465 
1466     RefPtrWillBeRawPtr<FileList> fileList = FileList::create();
1467     for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) {
1468         String path;
1469         if (!(*iter)->asString(&path)) {
1470             *errorString = "Files must be strings";
1471             return;
1472         }
1473         fileList->append(File::create(path));
1474     }
1475     toHTMLInputElement(node)->setFiles(fileList);
1476 }
1477 
getBoxModel(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::DOM::BoxModel> & model)1478 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model)
1479 {
1480     Node* node = assertNode(errorString, nodeId);
1481     if (!node)
1482         return;
1483 
1484     bool result = m_overlay->getBoxModel(node, model);
1485     if (!result)
1486         *errorString = "Could not compute box model.";
1487 
1488 }
1489 
getNodeForLocation(ErrorString * errorString,int x,int y,int * nodeId)1490 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId)
1491 {
1492     if (!pushDocumentUponHandlelessOperation(errorString))
1493         return;
1494     HitTestRequest request(HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent);
1495     HitTestResult result(IntPoint(x, y));
1496     m_document->frame()->contentRenderer()->hitTest(request, result);
1497     Node* node = result.innerPossiblyPseudoNode();
1498     while (node && node->nodeType() == Node::TEXT_NODE)
1499         node = node->parentNode();
1500     if (!node) {
1501         *errorString = "No node found at given location";
1502         return;
1503     }
1504     *nodeId = pushNodePathToFrontend(node);
1505 }
1506 
resolveNode(ErrorString * errorString,int nodeId,const String * const objectGroup,RefPtr<TypeBuilder::Runtime::RemoteObject> & result)1507 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
1508 {
1509     String objectGroupName = objectGroup ? *objectGroup : "";
1510     Node* node = nodeForId(nodeId);
1511     if (!node) {
1512         *errorString = "No node with given id found";
1513         return;
1514     }
1515     RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1516     if (!object) {
1517         *errorString = "Node with given id does not belong to the document";
1518         return;
1519     }
1520     result = object;
1521 }
1522 
getAttributes(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::Array<String>> & result)1523 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result)
1524 {
1525     Element* element = assertElement(errorString, nodeId);
1526     if (!element)
1527         return;
1528 
1529     result = buildArrayForElementAttributes(element);
1530 }
1531 
requestNode(ErrorString *,const String & objectId,int * nodeId)1532 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId)
1533 {
1534     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
1535     Node* node = injectedScript.nodeForObjectId(objectId);
1536     if (node)
1537         *nodeId = pushNodePathToFrontend(node);
1538     else
1539         *nodeId = 0;
1540 }
1541 
1542 // static
documentURLString(Document * document)1543 String InspectorDOMAgent::documentURLString(Document* document)
1544 {
1545     if (!document || document->url().isNull())
1546         return "";
1547     return document->url().string();
1548 }
1549 
documentBaseURLString(Document * document)1550 static String documentBaseURLString(Document* document)
1551 {
1552     return document->completeURL("").string();
1553 }
1554 
shadowRootType(ShadowRoot * shadowRoot)1555 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot)
1556 {
1557     switch (shadowRoot->type()) {
1558     case ShadowRoot::UserAgentShadowRoot:
1559         return TypeBuilder::DOM::ShadowRootType::User_agent;
1560     case ShadowRoot::AuthorShadowRoot:
1561         return TypeBuilder::DOM::ShadowRootType::Author;
1562     }
1563     ASSERT_NOT_REACHED();
1564     return TypeBuilder::DOM::ShadowRootType::User_agent;
1565 }
1566 
buildObjectForNode(Node * node,int depth,NodeToIdMap * nodesMap)1567 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1568 {
1569     int id = bind(node, nodesMap);
1570     String nodeName;
1571     String localName;
1572     String nodeValue;
1573 
1574     switch (node->nodeType()) {
1575     case Node::TEXT_NODE:
1576     case Node::COMMENT_NODE:
1577     case Node::CDATA_SECTION_NODE:
1578         nodeValue = node->nodeValue();
1579         if (nodeValue.length() > maxTextSize)
1580             nodeValue = nodeValue.left(maxTextSize) + ellipsisUChar;
1581         break;
1582     case Node::ATTRIBUTE_NODE:
1583         localName = node->localName();
1584         break;
1585     case Node::DOCUMENT_FRAGMENT_NODE:
1586     case Node::DOCUMENT_NODE:
1587     case Node::ELEMENT_NODE:
1588     default:
1589         nodeName = node->nodeName();
1590         localName = node->localName();
1591         break;
1592     }
1593 
1594     RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create()
1595         .setNodeId(id)
1596         .setNodeType(static_cast<int>(node->nodeType()))
1597         .setNodeName(nodeName)
1598         .setLocalName(localName)
1599         .setNodeValue(nodeValue);
1600 
1601     bool forcePushChildren = false;
1602     if (node->isElementNode()) {
1603         Element* element = toElement(node);
1604         value->setAttributes(buildArrayForElementAttributes(element));
1605 
1606         if (node->isFrameOwnerElement()) {
1607             HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node);
1608             LocalFrame* frame = (frameOwner->contentFrame() && frameOwner->contentFrame()->isLocalFrame()) ? toLocalFrame(frameOwner->contentFrame()) : 0;
1609             if (frame)
1610                 value->setFrameId(m_pageAgent->frameId(frame));
1611             if (Document* doc = frameOwner->contentDocument())
1612                 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
1613         }
1614 
1615         ElementShadow* shadow = element->shadow();
1616         if (shadow) {
1617             RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1618             for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
1619                 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1620             value->setShadowRoots(shadowRoots);
1621             forcePushChildren = true;
1622         }
1623 
1624         if (isHTMLLinkElement(*element)) {
1625             HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
1626             if (linkElement.isImport() && linkElement.import() && innerParentNode(linkElement.import()) == linkElement)
1627                 value->setImportedDocument(buildObjectForNode(linkElement.import(), 0, nodesMap));
1628             forcePushChildren = true;
1629         }
1630 
1631         if (isHTMLTemplateElement(*element)) {
1632             value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(*element).content(), 0, nodesMap));
1633             forcePushChildren = true;
1634         }
1635 
1636         switch (element->pseudoId()) {
1637         case BEFORE:
1638             value->setPseudoType(TypeBuilder::DOM::PseudoType::Before);
1639             break;
1640         case AFTER:
1641             value->setPseudoType(TypeBuilder::DOM::PseudoType::After);
1642             break;
1643         default: {
1644             RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap);
1645             if (pseudoElements) {
1646                 value->setPseudoElements(pseudoElements.release());
1647                 forcePushChildren = true;
1648             }
1649             break;
1650         }
1651         }
1652     } else if (node->isDocumentNode()) {
1653         Document* document = toDocument(node);
1654         value->setDocumentURL(documentURLString(document));
1655         value->setBaseURL(documentBaseURLString(document));
1656         value->setXmlVersion(document->xmlVersion());
1657     } else if (node->isDocumentTypeNode()) {
1658         DocumentType* docType = toDocumentType(node);
1659         value->setPublicId(docType->publicId());
1660         value->setSystemId(docType->systemId());
1661     } else if (node->isAttributeNode()) {
1662         Attr* attribute = toAttr(node);
1663         value->setName(attribute->name());
1664         value->setValue(attribute->value());
1665     } else if (node->isShadowRoot()) {
1666         value->setShadowRootType(shadowRootType(toShadowRoot(node)));
1667     }
1668 
1669     if (node->isContainerNode()) {
1670         int nodeCount = innerChildNodeCount(node);
1671         value->setChildNodeCount(nodeCount);
1672         if (nodesMap == m_documentNodeToIdMap)
1673             m_cachedChildCount.set(id, nodeCount);
1674         if (forcePushChildren && !depth)
1675             depth = 1;
1676         RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap);
1677         if (children->length() > 0 || depth) // Push children along with shadow in any case.
1678             value->setChildren(children.release());
1679     }
1680 
1681     return value.release();
1682 }
1683 
buildArrayForElementAttributes(Element * element)1684 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1685 {
1686     RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create();
1687     // Go through all attributes and serialize them.
1688     AttributeCollection attributes = element->attributes();
1689     AttributeCollection::iterator end = attributes.end();
1690     for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
1691         // Add attribute pair
1692         attributesValue->addItem(it->name().toString());
1693         attributesValue->addItem(it->value());
1694     }
1695     return attributesValue.release();
1696 }
1697 
buildArrayForContainerChildren(Node * container,int depth,NodeToIdMap * nodesMap)1698 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1699 {
1700     RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1701     if (depth == 0) {
1702         // Special-case the only text child - pretend that container's children have been requested.
1703         Node* firstChild = container->firstChild();
1704         if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1705             children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1706             m_childrenRequested.add(bind(container, nodesMap));
1707         }
1708         return children.release();
1709     }
1710 
1711     Node* child = innerFirstChild(container);
1712     depth--;
1713     m_childrenRequested.add(bind(container, nodesMap));
1714 
1715     while (child) {
1716         children->addItem(buildObjectForNode(child, depth, nodesMap));
1717         child = innerNextSibling(child);
1718     }
1719     return children.release();
1720 }
1721 
buildObjectForEventListener(const RegisteredEventListener & registeredEventListener,const AtomicString & eventType,Node * node,const String * objectGroupId)1722 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
1723 {
1724     RefPtr<EventListener> eventListener = registeredEventListener.listener;
1725     String sourceName;
1726     String scriptId;
1727     int lineNumber;
1728     int columnNumber;
1729     if (!eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber, columnNumber))
1730         return nullptr;
1731 
1732     Document& document = node->document();
1733     RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
1734         .setScriptId(scriptId)
1735         .setLineNumber(lineNumber);
1736     location->setColumnNumber(columnNumber);
1737     RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
1738         .setType(eventType)
1739         .setUseCapture(registeredEventListener.useCapture)
1740         .setIsAttribute(eventListener->isAttribute())
1741         .setNodeId(pushNodePathToFrontend(node))
1742         .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get()))
1743         .setLocation(location);
1744     if (objectGroupId) {
1745         ScriptValue functionValue = eventListenerHandler(&document, eventListener.get());
1746         if (!functionValue.isEmpty()) {
1747             LocalFrame* frame = document.frame();
1748             if (frame) {
1749                 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
1750                 if (scriptState) {
1751                     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1752                     if (!injectedScript.isEmpty()) {
1753                         RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId);
1754                         value->setHandler(valueJson);
1755                     }
1756                 }
1757             }
1758         }
1759     }
1760     if (!sourceName.isEmpty())
1761         value->setSourceName(sourceName);
1762     return value.release();
1763 }
1764 
buildArrayForPseudoElements(Element * element,NodeToIdMap * nodesMap)1765 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap)
1766 {
1767     if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER))
1768         return nullptr;
1769 
1770     RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1771     if (element->pseudoElement(BEFORE))
1772         pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap));
1773     if (element->pseudoElement(AFTER))
1774         pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap));
1775     return pseudoElements.release();
1776 }
1777 
innerFirstChild(Node * node)1778 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1779 {
1780     node = node->firstChild();
1781     while (isWhitespace(node))
1782         node = node->nextSibling();
1783     return node;
1784 }
1785 
innerNextSibling(Node * node)1786 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1787 {
1788     do {
1789         node = node->nextSibling();
1790     } while (isWhitespace(node));
1791     return node;
1792 }
1793 
innerPreviousSibling(Node * node)1794 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1795 {
1796     do {
1797         node = node->previousSibling();
1798     } while (isWhitespace(node));
1799     return node;
1800 }
1801 
innerChildNodeCount(Node * node)1802 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1803 {
1804     unsigned count = 0;
1805     Node* child = innerFirstChild(node);
1806     while (child) {
1807         count++;
1808         child = innerNextSibling(child);
1809     }
1810     return count;
1811 }
1812 
innerParentNode(Node * node)1813 Node* InspectorDOMAgent::innerParentNode(Node* node)
1814 {
1815     if (node->isDocumentNode()) {
1816         Document* document = toDocument(node);
1817         if (HTMLImportLoader* loader = document->importLoader())
1818             return loader->firstImport()->link();
1819         return document->ownerElement();
1820     }
1821     return node->parentOrShadowHostNode();
1822 }
1823 
isWhitespace(Node * node)1824 bool InspectorDOMAgent::isWhitespace(Node* node)
1825 {
1826     //TODO: pull ignoreWhitespace setting from the frontend and use here.
1827     return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1828 }
1829 
domContentLoadedEventFired(LocalFrame * frame)1830 void InspectorDOMAgent::domContentLoadedEventFired(LocalFrame* frame)
1831 {
1832     if (!frame->isMainFrame())
1833         return;
1834 
1835     // Re-push document once it is loaded.
1836     discardFrontendBindings();
1837     if (enabled())
1838         m_frontend->documentUpdated();
1839 }
1840 
invalidateFrameOwnerElement(LocalFrame * frame)1841 void InspectorDOMAgent::invalidateFrameOwnerElement(LocalFrame* frame)
1842 {
1843     HTMLFrameOwnerElement* frameOwner = frame->document()->ownerElement();
1844     if (!frameOwner)
1845         return;
1846 
1847     int frameOwnerId = m_documentNodeToIdMap->get(frameOwner);
1848     if (!frameOwnerId)
1849         return;
1850 
1851     // Re-add frame owner element together with its new children.
1852     int parentId = m_documentNodeToIdMap->get(innerParentNode(frameOwner));
1853     m_frontend->childNodeRemoved(parentId, frameOwnerId);
1854     unbind(frameOwner, m_documentNodeToIdMap.get());
1855 
1856     RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, m_documentNodeToIdMap.get());
1857     Node* previousSibling = innerPreviousSibling(frameOwner);
1858     int prevId = previousSibling ? m_documentNodeToIdMap->get(previousSibling) : 0;
1859     m_frontend->childNodeInserted(parentId, prevId, value.release());
1860 }
1861 
didCommitLoad(LocalFrame * frame,DocumentLoader * loader)1862 void InspectorDOMAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
1863 {
1864     // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame()
1865     // then all we need to check here is loader->frame()->isMainFrame()
1866     // and we don't need "frame" at all.
1867     if (!frame->page()->mainFrame()->isLocalFrame())
1868         return;
1869     LocalFrame* mainFrame = frame->page()->deprecatedLocalMainFrame();
1870     if (loader->frame() != mainFrame) {
1871         invalidateFrameOwnerElement(loader->frame());
1872         return;
1873     }
1874 
1875     setDocument(mainFrame->document());
1876 }
1877 
didInsertDOMNode(Node * node)1878 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1879 {
1880     if (isWhitespace(node))
1881         return;
1882 
1883     // We could be attaching existing subtree. Forget the bindings.
1884     unbind(node, m_documentNodeToIdMap.get());
1885 
1886     ContainerNode* parent = node->parentNode();
1887     if (!parent)
1888         return;
1889     int parentId = m_documentNodeToIdMap->get(parent);
1890     // Return if parent is not mapped yet.
1891     if (!parentId)
1892         return;
1893 
1894     if (!m_childrenRequested.contains(parentId)) {
1895         // No children are mapped yet -> only notify on changes of child count.
1896         int count = m_cachedChildCount.get(parentId) + 1;
1897         m_cachedChildCount.set(parentId, count);
1898         m_frontend->childNodeCountUpdated(parentId, count);
1899     } else {
1900         // Children have been requested -> return value of a new child.
1901         Node* prevSibling = innerPreviousSibling(node);
1902         int prevId = prevSibling ? m_documentNodeToIdMap->get(prevSibling) : 0;
1903         RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, m_documentNodeToIdMap.get());
1904         m_frontend->childNodeInserted(parentId, prevId, value.release());
1905     }
1906 }
1907 
willRemoveDOMNode(Node * node)1908 void InspectorDOMAgent::willRemoveDOMNode(Node* node)
1909 {
1910     if (isWhitespace(node))
1911         return;
1912 
1913     ContainerNode* parent = node->parentNode();
1914 
1915     // If parent is not mapped yet -> ignore the event.
1916     if (!m_documentNodeToIdMap->contains(parent))
1917         return;
1918 
1919     int parentId = m_documentNodeToIdMap->get(parent);
1920 
1921     if (!m_childrenRequested.contains(parentId)) {
1922         // No children are mapped yet -> only notify on changes of child count.
1923         int count = m_cachedChildCount.get(parentId) - 1;
1924         m_cachedChildCount.set(parentId, count);
1925         m_frontend->childNodeCountUpdated(parentId, count);
1926     } else {
1927         m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap->get(node));
1928     }
1929     unbind(node, m_documentNodeToIdMap.get());
1930 }
1931 
willModifyDOMAttr(Element *,const AtomicString & oldValue,const AtomicString & newValue)1932 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue)
1933 {
1934     m_suppressAttributeModifiedEvent = (oldValue == newValue);
1935 }
1936 
didModifyDOMAttr(Element * element,const AtomicString & name,const AtomicString & value)1937 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value)
1938 {
1939     bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
1940     m_suppressAttributeModifiedEvent = false;
1941     if (shouldSuppressEvent)
1942         return;
1943 
1944     int id = boundNodeId(element);
1945     // If node is not mapped yet -> ignore the event.
1946     if (!id)
1947         return;
1948 
1949     if (m_domListener)
1950         m_domListener->didModifyDOMAttr(element);
1951 
1952     m_frontend->attributeModified(id, name, value);
1953 }
1954 
didRemoveDOMAttr(Element * element,const AtomicString & name)1955 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name)
1956 {
1957     int id = boundNodeId(element);
1958     // If node is not mapped yet -> ignore the event.
1959     if (!id)
1960         return;
1961 
1962     if (m_domListener)
1963         m_domListener->didModifyDOMAttr(element);
1964 
1965     m_frontend->attributeRemoved(id, name);
1966 }
1967 
styleAttributeInvalidated(const WillBeHeapVector<RawPtrWillBeMember<Element>> & elements)1968 void InspectorDOMAgent::styleAttributeInvalidated(const WillBeHeapVector<RawPtrWillBeMember<Element> >& elements)
1969 {
1970     RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create();
1971     for (unsigned i = 0, size = elements.size(); i < size; ++i) {
1972         Element* element = elements.at(i);
1973         int id = boundNodeId(element);
1974         // If node is not mapped yet -> ignore the event.
1975         if (!id)
1976             continue;
1977 
1978         if (m_domListener)
1979             m_domListener->didModifyDOMAttr(element);
1980         nodeIds->addItem(id);
1981     }
1982     m_frontend->inlineStyleInvalidated(nodeIds.release());
1983 }
1984 
characterDataModified(CharacterData * characterData)1985 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1986 {
1987     int id = m_documentNodeToIdMap->get(characterData);
1988     if (!id) {
1989         // Push text node if it is being created.
1990         didInsertDOMNode(characterData);
1991         return;
1992     }
1993     m_frontend->characterDataModified(id, characterData->data());
1994 }
1995 
didInvalidateStyleAttr(Node * node)1996 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1997 {
1998     int id = m_documentNodeToIdMap->get(node);
1999     // If node is not mapped yet -> ignore the event.
2000     if (!id)
2001         return;
2002 
2003     if (!m_revalidateStyleAttrTask)
2004         m_revalidateStyleAttrTask = adoptPtrWillBeNoop(new RevalidateStyleAttributeTask(this));
2005     m_revalidateStyleAttrTask->scheduleFor(toElement(node));
2006 }
2007 
didPushShadowRoot(Element * host,ShadowRoot * root)2008 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root)
2009 {
2010     if (!host->ownerDocument())
2011         return;
2012 
2013     int hostId = m_documentNodeToIdMap->get(host);
2014     if (!hostId)
2015         return;
2016 
2017     pushChildNodesToFrontend(hostId, 1);
2018     m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, m_documentNodeToIdMap.get()));
2019 }
2020 
willPopShadowRoot(Element * host,ShadowRoot * root)2021 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root)
2022 {
2023     if (!host->ownerDocument())
2024         return;
2025 
2026     int hostId = m_documentNodeToIdMap->get(host);
2027     int rootId = m_documentNodeToIdMap->get(root);
2028     if (hostId && rootId)
2029         m_frontend->shadowRootPopped(hostId, rootId);
2030 }
2031 
frameDocumentUpdated(LocalFrame * frame)2032 void InspectorDOMAgent::frameDocumentUpdated(LocalFrame* frame)
2033 {
2034     Document* document = frame->document();
2035     if (!document)
2036         return;
2037 
2038     Page* page = frame->page();
2039     ASSERT(page);
2040     if (frame != page->mainFrame())
2041         return;
2042 
2043     // Only update the main frame document, nested frame document updates are not required
2044     // (will be handled by invalidateFrameOwnerElement()).
2045     setDocument(document);
2046 }
2047 
pseudoElementCreated(PseudoElement * pseudoElement)2048 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement)
2049 {
2050     Element* parent = pseudoElement->parentOrShadowHostElement();
2051     if (!parent)
2052         return;
2053     int parentId = m_documentNodeToIdMap->get(parent);
2054     if (!parentId)
2055         return;
2056 
2057     pushChildNodesToFrontend(parentId, 1);
2058     m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, m_documentNodeToIdMap.get()));
2059 }
2060 
pseudoElementDestroyed(PseudoElement * pseudoElement)2061 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
2062 {
2063     int pseudoElementId = m_documentNodeToIdMap->get(pseudoElement);
2064     if (!pseudoElementId)
2065         return;
2066 
2067     // If a PseudoElement is bound, its parent element must be bound, too.
2068     Element* parent = pseudoElement->parentOrShadowHostElement();
2069     ASSERT(parent);
2070     int parentId = m_documentNodeToIdMap->get(parent);
2071     ASSERT(parentId);
2072 
2073     unbind(pseudoElement, m_documentNodeToIdMap.get());
2074     m_frontend->pseudoElementRemoved(parentId, pseudoElementId);
2075 }
2076 
shadowRootForNode(Node * node,const String & type)2077 static ShadowRoot* shadowRootForNode(Node* node, const String& type)
2078 {
2079     if (!node->isElementNode())
2080         return 0;
2081     if (type == "a")
2082         return toElement(node)->shadowRoot();
2083     if (type == "u")
2084         return toElement(node)->userAgentShadowRoot();
2085     return 0;
2086 }
2087 
nodeForPath(const String & path)2088 Node* InspectorDOMAgent::nodeForPath(const String& path)
2089 {
2090     // The path is of form "1,HTML,2,BODY,1,DIV" (<index> and <nodeName> interleaved).
2091     // <index> may also be "a" (author shadow root) or "u" (user-agent shadow root),
2092     // in which case <nodeName> MUST be "#document-fragment".
2093     if (!m_document)
2094         return 0;
2095 
2096     Node* node = m_document.get();
2097     Vector<String> pathTokens;
2098     path.split(',', pathTokens);
2099     if (!pathTokens.size())
2100         return 0;
2101     for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
2102         bool success = true;
2103         String& indexValue = pathTokens[i];
2104         unsigned childNumber = indexValue.toUInt(&success);
2105         Node* child;
2106         if (!success) {
2107             child = shadowRootForNode(node, indexValue);
2108         } else {
2109             if (childNumber >= innerChildNodeCount(node))
2110                 return 0;
2111 
2112             child = innerFirstChild(node);
2113         }
2114         String childName = pathTokens[i + 1];
2115         for (size_t j = 0; child && j < childNumber; ++j)
2116             child = innerNextSibling(child);
2117 
2118         if (!child || child->nodeName() != childName)
2119             return 0;
2120         node = child;
2121     }
2122     return node;
2123 }
2124 
pushNodeByPathToFrontend(ErrorString * errorString,const String & path,int * nodeId)2125 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId)
2126 {
2127     if (Node* node = nodeForPath(path))
2128         *nodeId = pushNodePathToFrontend(node);
2129     else
2130         *errorString = "No node with given path found";
2131 }
2132 
pushNodesByBackendIdsToFrontend(ErrorString * errorString,const RefPtr<JSONArray> & backendNodeIds,RefPtr<TypeBuilder::Array<int>> & result)2133 void InspectorDOMAgent::pushNodesByBackendIdsToFrontend(ErrorString* errorString, const RefPtr<JSONArray>& backendNodeIds, RefPtr<TypeBuilder::Array<int> >& result)
2134 {
2135     result = TypeBuilder::Array<int>::create();
2136     for (JSONArray::const_iterator it = backendNodeIds->begin(); it != backendNodeIds->end(); ++it) {
2137         int backendNodeId;
2138 
2139         if (!(*it)->asNumber(&backendNodeId)) {
2140             *errorString = "Invalid argument type";
2141             return;
2142         }
2143 
2144         Node* node = InspectorNodeIds::nodeForId(backendNodeId);
2145         if (node && node->document().page() == m_pageAgent->page())
2146             result->addItem(pushNodePathToFrontend(node));
2147         else
2148             result->addItem(0);
2149     }
2150 }
2151 
getRelayoutBoundary(ErrorString * errorString,int nodeId,int * relayoutBoundaryNodeId)2152 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId)
2153 {
2154     Node* node = assertNode(errorString, nodeId);
2155     if (!node)
2156         return;
2157     RenderObject* renderer = node->renderer();
2158     if (!renderer) {
2159         *errorString = "No renderer for node, perhaps orphan or hidden node";
2160         return;
2161     }
2162     while (renderer && !renderer->isDocumentElement() && !renderer->isRelayoutBoundaryForInspector())
2163         renderer = renderer->container();
2164     Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument();
2165     *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode);
2166 }
2167 
resolveNode(Node * node,const String & objectGroup)2168 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2169 {
2170     Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument();
2171     LocalFrame* frame = document ? document->frame() : 0;
2172     if (!frame)
2173         return nullptr;
2174 
2175     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(ScriptState::forMainWorld(frame));
2176     if (injectedScript.isEmpty())
2177         return nullptr;
2178 
2179     return injectedScript.wrapNode(node, objectGroup);
2180 }
2181 
pushDocumentUponHandlelessOperation(ErrorString * errorString)2182 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString)
2183 {
2184     if (!m_documentNodeToIdMap->contains(m_document)) {
2185         RefPtr<TypeBuilder::DOM::Node> root;
2186         getDocument(errorString, root);
2187         return errorString->isEmpty();
2188     }
2189     return true;
2190 }
2191 
trace(Visitor * visitor)2192 void InspectorDOMAgent::trace(Visitor* visitor)
2193 {
2194     visitor->trace(m_domListener);
2195     visitor->trace(m_pageAgent);
2196     visitor->trace(m_injectedScriptManager);
2197 #if ENABLE(OILPAN)
2198     visitor->trace(m_documentNodeToIdMap);
2199     visitor->trace(m_danglingNodeToIdMaps);
2200     visitor->trace(m_idToNode);
2201     visitor->trace(m_idToNodesMap);
2202     visitor->trace(m_document);
2203     visitor->trace(m_revalidateStyleAttrTask);
2204     visitor->trace(m_searchResults);
2205 #endif
2206     visitor->trace(m_history);
2207     visitor->trace(m_domEditor);
2208     visitor->trace(m_listener);
2209     InspectorBaseAgent::trace(visitor);
2210 }
2211 
2212 } // namespace blink
2213 
2214