• 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 "InspectorDOMAgent.h"
33 
34 #if ENABLE(INSPECTOR)
35 
36 #include "Attr.h"
37 #include "CSSComputedStyleDeclaration.h"
38 #include "CSSMutableStyleDeclaration.h"
39 #include "CSSPropertyNames.h"
40 #include "CSSPropertySourceData.h"
41 #include "CSSRule.h"
42 #include "CSSRuleList.h"
43 #include "CSSStyleRule.h"
44 #include "CSSStyleSelector.h"
45 #include "CSSStyleSheet.h"
46 #include "CharacterData.h"
47 #include "ContainerNode.h"
48 #include "Cookie.h"
49 #include "CookieJar.h"
50 #include "DOMNodeHighlighter.h"
51 #include "DOMWindow.h"
52 #include "Document.h"
53 #include "DocumentType.h"
54 #include "Event.h"
55 #include "EventContext.h"
56 #include "EventListener.h"
57 #include "EventNames.h"
58 #include "EventTarget.h"
59 #include "Frame.h"
60 #include "FrameTree.h"
61 #include "HitTestResult.h"
62 #include "HTMLElement.h"
63 #include "HTMLFrameOwnerElement.h"
64 #include "InjectedScriptManager.h"
65 #include "InspectorClient.h"
66 #include "InspectorFrontend.h"
67 #include "InspectorResourceAgent.h"
68 #include "InspectorState.h"
69 #include "InstrumentingAgents.h"
70 #include "MutationEvent.h"
71 #include "Node.h"
72 #include "NodeList.h"
73 #include "Page.h"
74 #include "Pasteboard.h"
75 #include "PlatformString.h"
76 #include "RenderStyle.h"
77 #include "RenderStyleConstants.h"
78 #include "ScriptEventListener.h"
79 #include "StyleSheetList.h"
80 #include "Text.h"
81 
82 #if ENABLE(XPATH)
83 #include "XPathResult.h"
84 #endif
85 
86 #include "markup.h"
87 
88 #include <wtf/text/CString.h>
89 #include <wtf/text/StringConcatenate.h>
90 #include <wtf/HashSet.h>
91 #include <wtf/ListHashSet.h>
92 #include <wtf/OwnPtr.h>
93 #include <wtf/Vector.h>
94 #include <wtf/text/AtomicString.h>
95 
96 namespace WebCore {
97 
98 namespace DOMAgentState {
99 static const char documentRequested[] = "documentRequested";
100 };
101 
102 class MatchJob {
103 public:
104     virtual void match(ListHashSet<Node*>& resultCollector) = 0;
~MatchJob()105     virtual ~MatchJob() { }
106 
107 protected:
MatchJob(Document * document,const String & query)108     MatchJob(Document* document, const String& query)
109         : m_document(document)
110         , m_query(query) { }
111 
addNodesToResults(PassRefPtr<NodeList> nodes,ListHashSet<Node * > & resultCollector)112     void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector)
113     {
114         for (unsigned i = 0; nodes && i < nodes->length(); ++i)
115             resultCollector.add(nodes->item(i));
116     }
117 
118     RefPtr<Document> m_document;
119     String m_query;
120 };
121 
122 class RevalidateStyleAttributeTask {
123 public:
124     RevalidateStyleAttributeTask(InspectorDOMAgent*);
125     void scheduleFor(Element*);
reset()126     void reset() { m_timer.stop(); }
127     void onTimer(Timer<RevalidateStyleAttributeTask>*);
128 
129 private:
130     InspectorDOMAgent* m_domAgent;
131     Timer<RevalidateStyleAttributeTask> m_timer;
132     HashSet<RefPtr<Element> > m_elements;
133 };
134 
135 namespace {
136 
137 class MatchExactIdJob : public WebCore::MatchJob {
138 public:
MatchExactIdJob(Document * document,const String & query)139     MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactIdJob()140     virtual ~MatchExactIdJob() { }
141 
142 protected:
match(ListHashSet<Node * > & resultCollector)143     virtual void match(ListHashSet<Node*>& resultCollector)
144     {
145         if (m_query.isEmpty())
146             return;
147 
148         Element* element = m_document->getElementById(m_query);
149         if (element)
150             resultCollector.add(element);
151     }
152 };
153 
154 class MatchExactClassNamesJob : public WebCore::MatchJob {
155 public:
MatchExactClassNamesJob(Document * document,const String & query)156     MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactClassNamesJob()157     virtual ~MatchExactClassNamesJob() { }
158 
match(ListHashSet<Node * > & resultCollector)159     virtual void match(ListHashSet<Node*>& resultCollector)
160     {
161         if (!m_query.isEmpty())
162             addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector);
163     }
164 };
165 
166 class MatchExactTagNamesJob : public WebCore::MatchJob {
167 public:
MatchExactTagNamesJob(Document * document,const String & query)168     MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactTagNamesJob()169     virtual ~MatchExactTagNamesJob() { }
170 
match(ListHashSet<Node * > & resultCollector)171     virtual void match(ListHashSet<Node*>& resultCollector)
172     {
173         if (!m_query.isEmpty())
174             addNodesToResults(m_document->getElementsByName(m_query), resultCollector);
175     }
176 };
177 
178 class MatchQuerySelectorAllJob : public WebCore::MatchJob {
179 public:
MatchQuerySelectorAllJob(Document * document,const String & query)180     MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchQuerySelectorAllJob()181     virtual ~MatchQuerySelectorAllJob() { }
182 
match(ListHashSet<Node * > & resultCollector)183     virtual void match(ListHashSet<Node*>& resultCollector)
184     {
185         if (m_query.isEmpty())
186             return;
187 
188         ExceptionCode ec = 0;
189         RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec);
190         if (!ec)
191             addNodesToResults(list, resultCollector);
192     }
193 };
194 
195 class MatchXPathJob : public WebCore::MatchJob {
196 public:
MatchXPathJob(Document * document,const String & query)197     MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchXPathJob()198     virtual ~MatchXPathJob() { }
199 
match(ListHashSet<Node * > & resultCollector)200     virtual void match(ListHashSet<Node*>& resultCollector)
201     {
202 #if ENABLE(XPATH)
203         if (m_query.isEmpty())
204             return;
205 
206         ExceptionCode ec = 0;
207         RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
208         if (ec || !result)
209             return;
210 
211         unsigned long size = result->snapshotLength(ec);
212         for (unsigned long i = 0; !ec && i < size; ++i) {
213             Node* node = result->snapshotItem(i, ec);
214             if (ec)
215                 break;
216 
217             if (node->nodeType() == Node::ATTRIBUTE_NODE)
218                 node = static_cast<Attr*>(node)->ownerElement();
219             resultCollector.add(node);
220         }
221 #else
222         UNUSED_PARAM(resultCollector);
223 #endif
224     }
225 };
226 
227 class MatchPlainTextJob : public MatchXPathJob {
228 public:
MatchPlainTextJob(Document * document,const String & query)229     MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query)
230     {
231         m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]";
232     }
~MatchPlainTextJob()233     virtual ~MatchPlainTextJob() { }
234 };
235 
236 }
237 
RevalidateStyleAttributeTask(InspectorDOMAgent * domAgent)238 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
239     : m_domAgent(domAgent)
240     , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
241 {
242 }
243 
scheduleFor(Element * element)244 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
245 {
246     m_elements.add(element);
247     if (!m_timer.isActive())
248         m_timer.startOneShot(0);
249 }
250 
onTimer(Timer<RevalidateStyleAttributeTask> *)251 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
252 {
253     // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
254     for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
255         m_domAgent->didModifyDOMAttr(it->get());
256 
257     m_elements.clear();
258 }
259 
InspectorDOMAgent(InstrumentingAgents * instrumentingAgents,Page * inspectedPage,InspectorClient * client,InspectorState * inspectorState,InjectedScriptManager * injectedScriptManager)260 InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, Page* inspectedPage, InspectorClient* client, InspectorState* inspectorState, InjectedScriptManager* injectedScriptManager)
261     : m_instrumentingAgents(instrumentingAgents)
262     , m_inspectedPage(inspectedPage)
263     , m_client(client)
264     , m_inspectorState(inspectorState)
265     , m_injectedScriptManager(injectedScriptManager)
266     , m_frontend(0)
267     , m_domListener(0)
268     , m_lastNodeId(1)
269     , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer)
270     , m_searchingForNode(false)
271 {
272 }
273 
~InspectorDOMAgent()274 InspectorDOMAgent::~InspectorDOMAgent()
275 {
276     reset();
277     ASSERT(!m_highlightedNode);
278     ASSERT(!m_searchingForNode);
279 }
280 
setFrontend(InspectorFrontend * frontend)281 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
282 {
283     ASSERT(!m_frontend);
284     m_frontend = frontend->dom();
285     m_instrumentingAgents->setInspectorDOMAgent(this);
286     m_document = m_inspectedPage->mainFrame()->document();
287 
288     if (m_nodeToFocus)
289         focusNode();
290 }
291 
clearFrontend()292 void InspectorDOMAgent::clearFrontend()
293 {
294     ASSERT(m_frontend);
295     setSearchingForNode(false);
296 
297     ErrorString error;
298     hideHighlight(&error);
299 
300     m_frontend = 0;
301     m_instrumentingAgents->setInspectorDOMAgent(0);
302     m_inspectorState->setBoolean(DOMAgentState::documentRequested, false);
303     reset();
304 }
305 
restore()306 void InspectorDOMAgent::restore()
307 {
308     // Reset document to avoid early return from setDocument.
309     m_document = 0;
310     setDocument(m_inspectedPage->mainFrame()->document());
311 }
312 
documents()313 Vector<Document*> InspectorDOMAgent::documents()
314 {
315     Vector<Document*> result;
316     for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->traverseNext()) {
317         Document* document = frame->document();
318         if (!document)
319             continue;
320         result.append(document);
321     }
322     return result;
323 }
324 
reset()325 void InspectorDOMAgent::reset()
326 {
327     ErrorString error;
328     cancelSearch(&error);
329     discardBindings();
330     if (m_revalidateStyleAttrTask)
331         m_revalidateStyleAttrTask->reset();
332     m_document = 0;
333 }
334 
setDOMListener(DOMListener * listener)335 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
336 {
337     m_domListener = listener;
338 }
339 
setDocument(Document * doc)340 void InspectorDOMAgent::setDocument(Document* doc)
341 {
342     if (doc == m_document.get())
343         return;
344 
345     reset();
346 
347     m_document = doc;
348 
349     if (!m_inspectorState->getBoolean(DOMAgentState::documentRequested))
350         return;
351 
352     // Immediately communicate 0 document or document that has finished loading.
353     if (!doc || !doc->parsing())
354         m_frontend->documentUpdated();
355 }
356 
releaseDanglingNodes()357 void InspectorDOMAgent::releaseDanglingNodes()
358 {
359     deleteAllValues(m_danglingNodeToIdMaps);
360     m_danglingNodeToIdMaps.clear();
361 }
362 
bind(Node * node,NodeToIdMap * nodesMap)363 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
364 {
365     int id = nodesMap->get(node);
366     if (id)
367         return id;
368     id = m_lastNodeId++;
369     nodesMap->set(node, id);
370     m_idToNode.set(id, node);
371     m_idToNodesMap.set(id, nodesMap);
372     return id;
373 }
374 
unbind(Node * node,NodeToIdMap * nodesMap)375 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
376 {
377     if (node->isFrameOwnerElement()) {
378         const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
379         if (m_domListener)
380             m_domListener->didRemoveDocument(frameOwner->contentDocument());
381     }
382 
383     int id = nodesMap->get(node);
384     if (!id)
385         return;
386     m_idToNode.remove(id);
387     nodesMap->remove(node);
388     bool childrenRequested = m_childrenRequested.contains(id);
389     if (childrenRequested) {
390         // Unbind subtree known to client recursively.
391         m_childrenRequested.remove(id);
392         Node* child = innerFirstChild(node);
393         while (child) {
394             unbind(child, nodesMap);
395             child = innerNextSibling(child);
396         }
397     }
398 }
399 
assertNode(ErrorString * errorString,int nodeId)400 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
401 {
402     Node* node = nodeForId(nodeId);
403     if (!node) {
404         *errorString = "Could not find node with given id";
405         return 0;
406     }
407     return node;
408 }
409 
assertElement(ErrorString * errorString,int nodeId)410 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
411 {
412     Node* node = assertNode(errorString, nodeId);
413     if (!node)
414         return 0;
415 
416     if (node->nodeType() != Node::ELEMENT_NODE) {
417         *errorString = "Node is not an Element";
418         return 0;
419     }
420     return toElement(node);
421 }
422 
423 
assertHTMLElement(ErrorString * errorString,int nodeId)424 HTMLElement* InspectorDOMAgent::assertHTMLElement(ErrorString* errorString, int nodeId)
425 {
426     Element* element = assertElement(errorString, nodeId);
427     if (!element)
428         return 0;
429 
430     if (!element->isHTMLElement()) {
431         *errorString = "Node is not an HTML Element";
432         return 0;
433     }
434     return toHTMLElement(element);
435 }
436 
getDocument(ErrorString *,RefPtr<InspectorObject> * root)437 void InspectorDOMAgent::getDocument(ErrorString*, RefPtr<InspectorObject>* root)
438 {
439     m_inspectorState->setBoolean(DOMAgentState::documentRequested, true);
440 
441     if (!m_document)
442         return;
443 
444     // Reset backend state.
445     RefPtr<Document> doc = m_document;
446     reset();
447     m_document = doc;
448 
449     *root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
450 }
451 
pushChildNodesToFrontend(int nodeId)452 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId)
453 {
454     Node* node = nodeForId(nodeId);
455     if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
456         return;
457     if (m_childrenRequested.contains(nodeId))
458         return;
459 
460     NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
461     RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, 1, nodeMap);
462     m_frontend->setChildNodes(nodeId, children.release());
463 }
464 
discardBindings()465 void InspectorDOMAgent::discardBindings()
466 {
467     m_documentNodeToIdMap.clear();
468     m_idToNode.clear();
469     releaseDanglingNodes();
470     m_childrenRequested.clear();
471 }
472 
nodeForId(int id)473 Node* InspectorDOMAgent::nodeForId(int id)
474 {
475     if (!id)
476         return 0;
477 
478     HashMap<int, Node*>::iterator it = m_idToNode.find(id);
479     if (it != m_idToNode.end())
480         return it->second;
481     return 0;
482 }
483 
getChildNodes(ErrorString *,int nodeId)484 void InspectorDOMAgent::getChildNodes(ErrorString*, int nodeId)
485 {
486     pushChildNodesToFrontend(nodeId);
487 }
488 
querySelector(ErrorString * errorString,int nodeId,const String & selectors,int * elementId)489 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
490 {
491     *elementId = 0;
492     Node* node = assertNode(errorString, nodeId);
493     if (!node)
494         return;
495 
496     ExceptionCode ec = 0;
497     RefPtr<Element> element = node->querySelector(selectors, ec);
498     if (ec) {
499         *errorString = "DOM Error while querying";
500         return;
501     }
502 
503     if (element)
504         *elementId = pushNodePathToFrontend(element.get());
505 }
506 
querySelectorAll(ErrorString * errorString,int nodeId,const String & selectors,RefPtr<InspectorArray> * result)507 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<InspectorArray>* result)
508 {
509     Node* node = assertNode(errorString, nodeId);
510     if (!node)
511         return;
512 
513     ExceptionCode ec = 0;
514     RefPtr<NodeList> nodes = node->querySelectorAll(selectors, ec);
515     if (ec) {
516         *errorString = "DOM Error while querying";
517         return;
518     }
519 
520     for (unsigned i = 0; i < nodes->length(); ++i)
521         (*result)->pushNumber(pushNodePathToFrontend(nodes->item(i)));
522 }
523 
pushNodePathToFrontend(Node * nodeToPush)524 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
525 {
526     ASSERT(nodeToPush);  // Invalid input
527 
528     if (!m_document)
529         return 0;
530     if (!m_documentNodeToIdMap.contains(m_document))
531         return 0;
532 
533     // Return id in case the node is known.
534     int result = m_documentNodeToIdMap.get(nodeToPush);
535     if (result)
536         return result;
537 
538     Node* node = nodeToPush;
539     Vector<Node*> path;
540     NodeToIdMap* danglingMap = 0;
541 
542     while (true) {
543         Node* parent = innerParentNode(node);
544         if (!parent) {
545             // Node being pushed is detached -> push subtree root.
546             danglingMap = new NodeToIdMap();
547             m_danglingNodeToIdMaps.append(danglingMap);
548             RefPtr<InspectorArray> children = InspectorArray::create();
549             children->pushObject(buildObjectForNode(node, 0, danglingMap));
550             m_frontend->setChildNodes(0, children);
551             break;
552         } else {
553             path.append(parent);
554             if (m_documentNodeToIdMap.get(parent))
555                 break;
556             else
557                 node = parent;
558         }
559     }
560 
561     NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
562     for (int i = path.size() - 1; i >= 0; --i) {
563         int nodeId = map->get(path.at(i));
564         ASSERT(nodeId);
565         pushChildNodesToFrontend(nodeId);
566     }
567     return map->get(nodeToPush);
568 }
569 
boundNodeId(Node * node)570 int InspectorDOMAgent::boundNodeId(Node* node)
571 {
572     return m_documentNodeToIdMap.get(node);
573 }
574 
setAttribute(ErrorString * errorString,int elementId,const String & name,const String & value)575 void InspectorDOMAgent::setAttribute(ErrorString* errorString, int elementId, const String& name, const String& value)
576 {
577     Element* element = assertElement(errorString, elementId);
578     if (element) {
579         ExceptionCode ec = 0;
580         element->setAttribute(name, value, ec);
581         if (ec)
582             *errorString = "Exception while setting attribute value";
583     }
584 }
585 
removeAttribute(ErrorString * errorString,int elementId,const String & name)586 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
587 {
588     Element* element = assertElement(errorString, elementId);
589     if (element) {
590         ExceptionCode ec = 0;
591         element->removeAttribute(name, ec);
592         if (ec)
593             *errorString = "Exception while removing attribute";
594     }
595 }
596 
removeNode(ErrorString * errorString,int nodeId)597 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
598 {
599     Node* node = assertNode(errorString, nodeId);
600     if (!node)
601         return;
602 
603     ContainerNode* parentNode = node->parentNode();
604     if (!parentNode) {
605         *errorString = "Can not remove detached node";
606         return;
607     }
608 
609     ExceptionCode ec = 0;
610     parentNode->removeChild(node, ec);
611     if (ec)
612         *errorString = "Could not remove node due to DOM exception";
613 }
614 
setNodeName(ErrorString *,int nodeId,const String & tagName,int * newId)615 void InspectorDOMAgent::setNodeName(ErrorString*, int nodeId, const String& tagName, int* newId)
616 {
617     *newId = 0;
618 
619     Node* oldNode = nodeForId(nodeId);
620     if (!oldNode || !oldNode->isElementNode())
621         return;
622 
623     ExceptionCode ec = 0;
624     RefPtr<Element> newElem = oldNode->document()->createElement(tagName, ec);
625     if (ec)
626         return;
627 
628     // Copy over the original node's attributes.
629     Element* oldElem = static_cast<Element*>(oldNode);
630     newElem->copyNonAttributeProperties(oldElem);
631     if (oldElem->attributes())
632         newElem->attributes()->setAttributes(*(oldElem->attributes(true)));
633 
634     // Copy over the original node's children.
635     Node* child;
636     while ((child = oldNode->firstChild()))
637         newElem->appendChild(child, ec);
638 
639     // Replace the old node with the new node
640     ContainerNode* parent = oldNode->parentNode();
641     parent->insertBefore(newElem, oldNode->nextSibling(), ec);
642     parent->removeChild(oldNode, ec);
643 
644     if (ec)
645         return;
646 
647     *newId = pushNodePathToFrontend(newElem.get());
648     if (m_childrenRequested.contains(nodeId))
649         pushChildNodesToFrontend(*newId);
650 }
651 
getOuterHTML(ErrorString * errorString,int nodeId,WTF::String * outerHTML)652 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
653 {
654     HTMLElement* element = assertHTMLElement(errorString, nodeId);
655     if (element)
656         *outerHTML = element->outerHTML();
657 }
658 
setOuterHTML(ErrorString * errorString,int nodeId,const String & outerHTML,int * newId)659 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML, int* newId)
660 {
661     HTMLElement* htmlElement = assertHTMLElement(errorString, nodeId);
662     if (!htmlElement)
663         return;
664 
665     bool requiresTotalUpdate = htmlElement->tagName() == "HTML" || htmlElement->tagName() == "BODY" || htmlElement->tagName() == "HEAD";
666 
667     bool childrenRequested = m_childrenRequested.contains(nodeId);
668     Node* previousSibling = htmlElement->previousSibling();
669     ContainerNode* parentNode = htmlElement->parentNode();
670 
671     ExceptionCode ec = 0;
672     htmlElement->setOuterHTML(outerHTML, ec);
673     if (ec)
674         return;
675 
676     if (requiresTotalUpdate) {
677         RefPtr<Document> document = m_document;
678         reset();
679         setDocument(document.get());
680         *newId = 0;
681         return;
682     }
683 
684     Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
685     if (!newNode) {
686         // The only child node has been deleted.
687         *newId = 0;
688         return;
689     }
690 
691     *newId = pushNodePathToFrontend(newNode);
692     if (childrenRequested)
693         pushChildNodesToFrontend(*newId);
694 }
695 
setNodeValue(ErrorString * errorString,int nodeId,const String & value)696 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
697 {
698     Node* node = assertNode(errorString, nodeId);
699     if (!node)
700         return;
701 
702     if (node->nodeType() != Node::TEXT_NODE) {
703         *errorString = "Can only set value of text nodes";
704         return;
705     }
706 
707     Text* textNode = static_cast<Text*>(node);
708     ExceptionCode ec = 0;
709     textNode->replaceWholeText(value, ec);
710     if (ec)
711         *errorString = "DOM Error while setting the node value";
712 }
713 
getEventListenersForNode(ErrorString *,int nodeId,RefPtr<InspectorArray> * listenersArray)714 void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPtr<InspectorArray>* listenersArray)
715 {
716     Node* node = nodeForId(nodeId);
717     EventTargetData* d;
718 
719     // Quick break if a null node or no listeners at all
720     if (!node || !(d = node->eventTargetData()))
721         return;
722 
723     // Get the list of event types this Node is concerned with
724     Vector<AtomicString> eventTypes;
725     const EventListenerMap& listenerMap = d->eventListenerMap;
726     EventListenerMap::const_iterator end = listenerMap.end();
727     for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
728         eventTypes.append(iter->first);
729 
730     // Quick break if no useful listeners
731     size_t eventTypesLength = eventTypes.size();
732     if (!eventTypesLength)
733         return;
734 
735     // The Node's Ancestors (not including self)
736     Vector<ContainerNode*> ancestors;
737     for (ContainerNode* ancestor = node->parentOrHostNode(); ancestor; ancestor = ancestor->parentOrHostNode())
738         ancestors.append(ancestor);
739 
740     // Nodes and their Listeners for the concerned event types (order is top to bottom)
741     Vector<EventListenerInfo> eventInformation;
742     for (size_t i = ancestors.size(); i; --i) {
743         ContainerNode* ancestor = ancestors[i - 1];
744         for (size_t j = 0; j < eventTypesLength; ++j) {
745             AtomicString& type = eventTypes[j];
746             if (ancestor->hasEventListeners(type))
747                 eventInformation.append(EventListenerInfo(ancestor, type, ancestor->getEventListeners(type)));
748         }
749     }
750 
751     // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
752     for (size_t i = 0; i < eventTypesLength; ++i) {
753         const AtomicString& type = eventTypes[i];
754         eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
755     }
756 
757     // Get Capturing Listeners (in this order)
758     size_t eventInformationLength = eventInformation.size();
759     for (size_t i = 0; i < eventInformationLength; ++i) {
760         const EventListenerInfo& info = eventInformation[i];
761         const EventListenerVector& vector = info.eventListenerVector;
762         for (size_t j = 0; j < vector.size(); ++j) {
763             const RegisteredEventListener& listener = vector[j];
764             if (listener.useCapture)
765                 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
766         }
767     }
768 
769     // Get Bubbling Listeners (reverse order)
770     for (size_t i = eventInformationLength; i; --i) {
771         const EventListenerInfo& info = eventInformation[i - 1];
772         const EventListenerVector& vector = info.eventListenerVector;
773         for (size_t j = 0; j < vector.size(); ++j) {
774             const RegisteredEventListener& listener = vector[j];
775             if (!listener.useCapture)
776                 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
777         }
778     }
779 }
780 
performSearch(ErrorString * error,const String & whitespaceTrimmedQuery,const bool * const runSynchronously)781 void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, const bool* const runSynchronously)
782 {
783     // FIXME: Few things are missing here:
784     // 1) Search works with node granularity - number of matches within node is not calculated.
785     // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
786     //    is sufficient.
787 
788     unsigned queryLength = whitespaceTrimmedQuery.length();
789     bool startTagFound = !whitespaceTrimmedQuery.find('<');
790     bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
791 
792     String tagNameQuery = whitespaceTrimmedQuery;
793     if (startTagFound || endTagFound)
794         tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength);
795     if (!Document::isValidName(tagNameQuery))
796         tagNameQuery = "";
797 
798     String attributeNameQuery = whitespaceTrimmedQuery;
799     if (!Document::isValidName(attributeNameQuery))
800         attributeNameQuery = "";
801 
802     String escapedQuery = whitespaceTrimmedQuery;
803     escapedQuery.replace("'", "\\'");
804     String escapedTagNameQuery = tagNameQuery;
805     escapedTagNameQuery.replace("'", "\\'");
806 
807     // Clear pending jobs.
808     cancelSearch(error);
809 
810     // Find all frames, iframes and object elements to search their documents.
811     Vector<Document*> docs = documents();
812     for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
813         Document* document = *it;
814 
815         if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
816             m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
817             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
818             continue;
819         }
820 
821         if (!tagNameQuery.isEmpty() && startTagFound) {
822             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
823             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
824             continue;
825         }
826 
827         if (!tagNameQuery.isEmpty() && endTagFound) {
828             // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
829             // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
830             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
831             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
832             continue;
833         }
834 
835         bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*";
836         if (matchesEveryNode) {
837             // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
838             // so limit the search functions list to plain text and attribute matching for these.
839             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
840             m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
841             continue;
842         }
843 
844         m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
845         m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
846         m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
847         m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
848         m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
849         m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
850         if (!tagNameQuery.isEmpty())
851             m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
852         m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
853         m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
854     }
855 
856     if (runSynchronously && *runSynchronously) {
857         // For tests.
858         ListHashSet<Node*> resultCollector;
859         for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it)
860             (*it)->match(resultCollector);
861         reportNodesAsSearchResults(resultCollector);
862         cancelSearch(error);
863         return;
864     }
865     m_matchJobsTimer.startOneShot(0);
866 }
867 
cancelSearch(ErrorString *)868 void InspectorDOMAgent::cancelSearch(ErrorString*)
869 {
870     if (m_matchJobsTimer.isActive())
871         m_matchJobsTimer.stop();
872     deleteAllValues(m_pendingMatchJobs);
873     m_pendingMatchJobs.clear();
874     m_searchResults.clear();
875 }
876 
handleMousePress()877 bool InspectorDOMAgent::handleMousePress()
878 {
879     if (!m_searchingForNode)
880         return false;
881 
882     if (m_highlightedNode) {
883         RefPtr<Node> node = m_highlightedNode;
884         setSearchingForNode(false);
885         inspect(node.get());
886     }
887     return true;
888 }
889 
inspect(Node * node)890 void InspectorDOMAgent::inspect(Node* node)
891 {
892     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
893         node = node->parentNode();
894     m_nodeToFocus = node;
895 
896     focusNode();
897 }
898 
focusNode()899 void InspectorDOMAgent::focusNode()
900 {
901     if (!m_frontend)
902         return;
903 
904     ASSERT(m_nodeToFocus);
905 
906     RefPtr<Node> node = m_nodeToFocus.get();
907     m_nodeToFocus = 0;
908 
909     Document* document = node->ownerDocument();
910     if (!document)
911         return;
912     Frame* frame = document->frame();
913     if (!frame)
914         return;
915 
916     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
917     if (injectedScript.hasNoValue())
918         return;
919 
920     injectedScript.inspectNode(node.get());
921 }
922 
mouseDidMoveOverElement(const HitTestResult & result,unsigned)923 void InspectorDOMAgent::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
924 {
925     if (!m_searchingForNode)
926         return;
927 
928     Node* node = result.innerNode();
929     while (node && node->nodeType() == Node::TEXT_NODE)
930         node = node->parentNode();
931     if (node) {
932         ErrorString error;
933         highlight(&error, node);
934     }
935 }
936 
setSearchingForNode(bool enabled)937 void InspectorDOMAgent::setSearchingForNode(bool enabled)
938 {
939     if (m_searchingForNode == enabled)
940         return;
941     m_searchingForNode = enabled;
942     if (!enabled) {
943         ErrorString error;
944         hideHighlight(&error);
945     }
946 }
947 
setSearchingForNode(ErrorString *,bool enabled)948 void InspectorDOMAgent::setSearchingForNode(ErrorString*, bool enabled)
949 {
950     setSearchingForNode(enabled);
951 }
952 
highlight(ErrorString *,Node * node)953 void InspectorDOMAgent::highlight(ErrorString*, Node* node)
954 {
955     ASSERT_ARG(node, node);
956     m_highlightedNode = node;
957     m_client->highlight(node);
958 }
959 
highlightDOMNode(ErrorString * error,int nodeId)960 void InspectorDOMAgent::highlightDOMNode(ErrorString* error, int nodeId)
961 {
962     if (Node* node = nodeForId(nodeId))
963         highlight(error, node);
964 }
965 
highlightFrame(ErrorString * error,const String & frameId)966 void InspectorDOMAgent::highlightFrame(ErrorString* error, const String& frameId)
967 {
968     Frame* frame = m_instrumentingAgents->inspectorResourceAgent()->frameForId(frameId);
969     if (frame && frame->ownerElement())
970         highlight(error, frame->ownerElement());
971 }
972 
hideHighlight(ErrorString *)973 void InspectorDOMAgent::hideHighlight(ErrorString*)
974 {
975     m_highlightedNode = 0;
976     m_client->hideHighlight();
977 }
978 
resolveNode(ErrorString * error,int nodeId,RefPtr<InspectorObject> * result)979 void InspectorDOMAgent::resolveNode(ErrorString* error, int nodeId, RefPtr<InspectorObject>* result)
980 {
981     Node* node = nodeForId(nodeId);
982     if (!node) {
983         *error = "No node with given id found.";
984         return;
985     }
986     *result = resolveNode(node);
987 }
988 
pushNodeToFrontend(ErrorString *,const String & objectId,int * nodeId)989 void InspectorDOMAgent::pushNodeToFrontend(ErrorString*, const String& objectId, int* nodeId)
990 {
991     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
992     Node* node = injectedScript.nodeForObjectId(objectId);
993     if (node)
994         *nodeId = pushNodePathToFrontend(node);
995     else
996         *nodeId = 0;
997 }
998 
documentURLString(Document * document) const999 String InspectorDOMAgent::documentURLString(Document* document) const
1000 {
1001     if (!document || document->url().isNull())
1002         return "";
1003     return document->url().string();
1004 }
1005 
buildObjectForNode(Node * node,int depth,NodeToIdMap * nodesMap)1006 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1007 {
1008     RefPtr<InspectorObject> value = InspectorObject::create();
1009 
1010     int id = bind(node, nodesMap);
1011     String nodeName;
1012     String localName;
1013     String nodeValue;
1014 
1015     switch (node->nodeType()) {
1016         case Node::TEXT_NODE:
1017         case Node::COMMENT_NODE:
1018         case Node::CDATA_SECTION_NODE:
1019             nodeValue = node->nodeValue();
1020             break;
1021         case Node::ATTRIBUTE_NODE:
1022             localName = node->localName();
1023             break;
1024         case Node::DOCUMENT_FRAGMENT_NODE:
1025             break;
1026         case Node::DOCUMENT_NODE:
1027         case Node::ELEMENT_NODE:
1028         default:
1029             nodeName = node->nodeName();
1030             localName = node->localName();
1031             break;
1032     }
1033 
1034     value->setNumber("id", id);
1035     value->setNumber("nodeType", node->nodeType());
1036     value->setString("nodeName", nodeName);
1037     value->setString("localName", localName);
1038     value->setString("nodeValue", nodeValue);
1039 
1040     if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
1041         int nodeCount = innerChildNodeCount(node);
1042         value->setNumber("childNodeCount", nodeCount);
1043         RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap);
1044         if (children->length() > 0)
1045             value->setArray("children", children.release());
1046 
1047         if (node->nodeType() == Node::ELEMENT_NODE) {
1048             Element* element = static_cast<Element*>(node);
1049             value->setArray("attributes", buildArrayForElementAttributes(element));
1050             if (node->isFrameOwnerElement()) {
1051                 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
1052                 value->setString("documentURL", documentURLString(frameOwner->contentDocument()));
1053             }
1054         } else if (node->nodeType() == Node::DOCUMENT_NODE) {
1055             Document* document = static_cast<Document*>(node);
1056             value->setString("documentURL", documentURLString(document));
1057         }
1058     } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
1059         DocumentType* docType = static_cast<DocumentType*>(node);
1060         value->setString("publicId", docType->publicId());
1061         value->setString("systemId", docType->systemId());
1062         value->setString("internalSubset", docType->internalSubset());
1063     } else if (node->nodeType() == Node::ATTRIBUTE_NODE) {
1064         Attr* attribute = static_cast<Attr*>(node);
1065         value->setString("name", attribute->name());
1066         value->setString("value", attribute->value());
1067     }
1068     return value.release();
1069 }
1070 
buildArrayForElementAttributes(Element * element)1071 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1072 {
1073     RefPtr<InspectorArray> attributesValue = InspectorArray::create();
1074     // Go through all attributes and serialize them.
1075     const NamedNodeMap* attrMap = element->attributes(true);
1076     if (!attrMap)
1077         return attributesValue.release();
1078     unsigned numAttrs = attrMap->length();
1079     for (unsigned i = 0; i < numAttrs; ++i) {
1080         // Add attribute pair
1081         const Attribute *attribute = attrMap->attributeItem(i);
1082         attributesValue->pushString(attribute->name().toString());
1083         attributesValue->pushString(attribute->value());
1084     }
1085     return attributesValue.release();
1086 }
1087 
buildArrayForContainerChildren(Node * container,int depth,NodeToIdMap * nodesMap)1088 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1089 {
1090     RefPtr<InspectorArray> children = InspectorArray::create();
1091     Node* child = innerFirstChild(container);
1092 
1093     if (depth == 0) {
1094         // Special-case the only text child - pretend that container's children have been requested.
1095         if (child && child->nodeType() == Node::TEXT_NODE && !innerNextSibling(child))
1096             return buildArrayForContainerChildren(container, 1, nodesMap);
1097         return children.release();
1098     }
1099 
1100     depth--;
1101     m_childrenRequested.add(bind(container, nodesMap));
1102 
1103     while (child) {
1104         children->pushObject(buildObjectForNode(child, depth, nodesMap));
1105         child = innerNextSibling(child);
1106     }
1107     return children.release();
1108 }
1109 
buildObjectForEventListener(const RegisteredEventListener & registeredEventListener,const AtomicString & eventType,Node * node)1110 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
1111 {
1112     RefPtr<EventListener> eventListener = registeredEventListener.listener;
1113     RefPtr<InspectorObject> value = InspectorObject::create();
1114     value->setString("type", eventType);
1115     value->setBoolean("useCapture", registeredEventListener.useCapture);
1116     value->setBoolean("isAttribute", eventListener->isAttribute());
1117     value->setNumber("nodeId", pushNodePathToFrontend(node));
1118     value->setString("listenerBody", eventListenerHandlerBody(node->document(), eventListener.get()));
1119     String sourceName;
1120     int lineNumber;
1121     if (eventListenerHandlerLocation(node->document(), eventListener.get(), sourceName, lineNumber)) {
1122         value->setString("sourceName", sourceName);
1123         value->setNumber("lineNumber", lineNumber);
1124     }
1125     return value.release();
1126 }
1127 
innerFirstChild(Node * node)1128 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1129 {
1130     if (node->isFrameOwnerElement()) {
1131         HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
1132         Document* doc = frameOwner->contentDocument();
1133         if (doc)
1134             return doc->firstChild();
1135     }
1136     node = node->firstChild();
1137     while (isWhitespace(node))
1138         node = node->nextSibling();
1139     return node;
1140 }
1141 
innerNextSibling(Node * node)1142 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1143 {
1144     do {
1145         node = node->nextSibling();
1146     } while (isWhitespace(node));
1147     return node;
1148 }
1149 
innerPreviousSibling(Node * node)1150 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1151 {
1152     do {
1153         node = node->previousSibling();
1154     } while (isWhitespace(node));
1155     return node;
1156 }
1157 
innerChildNodeCount(Node * node)1158 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1159 {
1160     unsigned count = 0;
1161     Node* child = innerFirstChild(node);
1162     while (child) {
1163         count++;
1164         child = innerNextSibling(child);
1165     }
1166     return count;
1167 }
1168 
innerParentNode(Node * node)1169 Node* InspectorDOMAgent::innerParentNode(Node* node)
1170 {
1171     ContainerNode* parent = node->parentNode();
1172     if (parent && parent->isDocumentNode())
1173         return static_cast<Document*>(parent)->ownerElement();
1174     return parent;
1175 }
1176 
isWhitespace(Node * node)1177 bool InspectorDOMAgent::isWhitespace(Node* node)
1178 {
1179     //TODO: pull ignoreWhitespace setting from the frontend and use here.
1180     return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1181 }
1182 
mainFrameDOMContentLoaded()1183 void InspectorDOMAgent::mainFrameDOMContentLoaded()
1184 {
1185     // Re-push document once it is loaded.
1186     discardBindings();
1187     if (m_inspectorState->getBoolean(DOMAgentState::documentRequested))
1188         m_frontend->documentUpdated();
1189 }
1190 
loadEventFired(Document * document)1191 void InspectorDOMAgent::loadEventFired(Document* document)
1192 {
1193     Element* frameOwner = document->ownerElement();
1194     if (!frameOwner)
1195         return;
1196 
1197     int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1198     if (!frameOwnerId)
1199         return;
1200 
1201     if (!m_childrenRequested.contains(frameOwnerId)) {
1202         // No children are mapped yet -> only notify on changes of hasChildren.
1203         m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(frameOwner));
1204     } else {
1205         // Re-add frame owner element together with its new children.
1206         int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1207         m_frontend->childNodeRemoved(parentId, frameOwnerId);
1208         RefPtr<InspectorObject> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1209         Node* previousSibling = innerPreviousSibling(frameOwner);
1210         int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1211         m_frontend->childNodeInserted(parentId, prevId, value.release());
1212         // Invalidate children requested flag for the element.
1213         m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId));
1214     }
1215 }
1216 
didInsertDOMNode(Node * node)1217 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1218 {
1219     if (isWhitespace(node))
1220         return;
1221 
1222     // We could be attaching existing subtree. Forget the bindings.
1223     unbind(node, &m_documentNodeToIdMap);
1224 
1225     ContainerNode* parent = node->parentNode();
1226     int parentId = m_documentNodeToIdMap.get(parent);
1227     // Return if parent is not mapped yet.
1228     if (!parentId)
1229         return;
1230 
1231     if (!m_childrenRequested.contains(parentId)) {
1232         // No children are mapped yet -> only notify on changes of hasChildren.
1233         m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1234     } else {
1235         // Children have been requested -> return value of a new child.
1236         Node* prevSibling = innerPreviousSibling(node);
1237         int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1238         RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1239         m_frontend->childNodeInserted(parentId, prevId, value.release());
1240     }
1241 }
1242 
didRemoveDOMNode(Node * node)1243 void InspectorDOMAgent::didRemoveDOMNode(Node* node)
1244 {
1245     if (isWhitespace(node))
1246         return;
1247 
1248     ContainerNode* parent = node->parentNode();
1249     int parentId = m_documentNodeToIdMap.get(parent);
1250     // If parent is not mapped yet -> ignore the event.
1251     if (!parentId)
1252         return;
1253 
1254     if (m_domListener)
1255         m_domListener->didRemoveDOMNode(node);
1256 
1257     if (!m_childrenRequested.contains(parentId)) {
1258         // No children are mapped yet -> only notify on changes of hasChildren.
1259         if (innerChildNodeCount(parent) == 1)
1260             m_frontend->childNodeCountUpdated(parentId, 0);
1261     } else
1262         m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1263     unbind(node, &m_documentNodeToIdMap);
1264 }
1265 
didModifyDOMAttr(Element * element)1266 void InspectorDOMAgent::didModifyDOMAttr(Element* element)
1267 {
1268     int id = m_documentNodeToIdMap.get(element);
1269     // If node is not mapped yet -> ignore the event.
1270     if (!id)
1271         return;
1272 
1273     if (m_domListener)
1274         m_domListener->didModifyDOMAttr(element);
1275 
1276     m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
1277 }
1278 
characterDataModified(CharacterData * characterData)1279 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1280 {
1281     int id = m_documentNodeToIdMap.get(characterData);
1282     if (!id)
1283         return;
1284     m_frontend->characterDataModified(id, characterData->data());
1285 }
1286 
didInvalidateStyleAttr(Node * node)1287 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1288 {
1289     int id = m_documentNodeToIdMap.get(node);
1290     // If node is not mapped yet -> ignore the event.
1291     if (!id)
1292         return;
1293 
1294     if (!m_revalidateStyleAttrTask)
1295         m_revalidateStyleAttrTask = new RevalidateStyleAttributeTask(this);
1296     m_revalidateStyleAttrTask->scheduleFor(static_cast<Element*>(node));
1297 }
1298 
nodeForPath(const String & path)1299 Node* InspectorDOMAgent::nodeForPath(const String& path)
1300 {
1301     // The path is of form "1,HTML,2,BODY,1,DIV"
1302     if (!m_document)
1303         return 0;
1304 
1305     Node* node = m_document.get();
1306     Vector<String> pathTokens;
1307     path.split(",", false, pathTokens);
1308     if (!pathTokens.size())
1309         return 0;
1310     for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1311         bool success = true;
1312         unsigned childNumber = pathTokens[i].toUInt(&success);
1313         if (!success)
1314             return 0;
1315         if (childNumber >= innerChildNodeCount(node))
1316             return 0;
1317 
1318         Node* child = innerFirstChild(node);
1319         String childName = pathTokens[i + 1];
1320         for (size_t j = 0; child && j < childNumber; ++j)
1321             child = innerNextSibling(child);
1322 
1323         if (!child || child->nodeName() != childName)
1324             return 0;
1325         node = child;
1326     }
1327     return node;
1328 }
1329 
toArray(const Vector<String> & data)1330 PassRefPtr<InspectorArray> InspectorDOMAgent::toArray(const Vector<String>& data)
1331 {
1332     RefPtr<InspectorArray> result = InspectorArray::create();
1333     for (unsigned i = 0; i < data.size(); ++i)
1334         result->pushString(data[i]);
1335     return result.release();
1336 }
1337 
onMatchJobsTimer(Timer<InspectorDOMAgent> *)1338 void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*)
1339 {
1340     if (!m_pendingMatchJobs.size()) {
1341         ErrorString error;
1342         cancelSearch(&error);
1343         return;
1344     }
1345 
1346     ListHashSet<Node*> resultCollector;
1347     MatchJob* job = m_pendingMatchJobs.takeFirst();
1348     job->match(resultCollector);
1349     delete job;
1350 
1351     reportNodesAsSearchResults(resultCollector);
1352 
1353     m_matchJobsTimer.startOneShot(0.025);
1354 }
1355 
reportNodesAsSearchResults(ListHashSet<Node * > & resultCollector)1356 void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector)
1357 {
1358     RefPtr<InspectorArray> nodeIds = InspectorArray::create();
1359     for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) {
1360         if (m_searchResults.contains(*it))
1361             continue;
1362         m_searchResults.add(*it);
1363         nodeIds->pushNumber(pushNodePathToFrontend(*it));
1364     }
1365     m_frontend->searchResults(nodeIds.release());
1366 }
1367 
copyNode(ErrorString *,int nodeId)1368 void InspectorDOMAgent::copyNode(ErrorString*, int nodeId)
1369 {
1370     Node* node = nodeForId(nodeId);
1371     if (!node)
1372         return;
1373     String markup = createMarkup(node);
1374     Pasteboard::generalPasteboard()->writePlainText(markup);
1375 }
1376 
pushNodeByPathToFrontend(ErrorString *,const String & path,int * nodeId)1377 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString*, const String& path, int* nodeId)
1378 {
1379     if (Node* node = nodeForPath(path))
1380         *nodeId = pushNodePathToFrontend(node);
1381 }
1382 
resolveNode(Node * node)1383 PassRefPtr<InspectorObject> InspectorDOMAgent::resolveNode(Node* node)
1384 {
1385     Document* document = node->ownerDocument();
1386     Frame* frame = document ? document->frame() : 0;
1387     if (!frame)
1388         return 0;
1389 
1390     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
1391     if (injectedScript.hasNoValue())
1392         return 0;
1393 
1394     return injectedScript.wrapNode(node);
1395 }
1396 
drawNodeHighlight(GraphicsContext & context) const1397 void InspectorDOMAgent::drawNodeHighlight(GraphicsContext& context) const
1398 {
1399     if (!m_highlightedNode)
1400         return;
1401 
1402     DOMNodeHighlighter::DrawNodeHighlight(context, m_highlightedNode.get());
1403 }
1404 
1405 } // namespace WebCore
1406 
1407 #endif // ENABLE(INSPECTOR)
1408