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