• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 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 "AtomicString.h"
37 #include "ContainerNode.h"
38 #include "Cookie.h"
39 #include "CookieJar.h"
40 #include "DOMWindow.h"
41 #include "Document.h"
42 #include "DocumentType.h"
43 #include "Event.h"
44 #include "EventListener.h"
45 #include "EventNames.h"
46 #include "EventTarget.h"
47 #include "HTMLFrameOwnerElement.h"
48 #include "InspectorFrontend.h"
49 #include "markup.h"
50 #include "MutationEvent.h"
51 #include "Node.h"
52 #include "NodeList.h"
53 #include "PlatformString.h"
54 #include "ScriptEventListener.h"
55 #include "ScriptObject.h"
56 #include "Text.h"
57 
58 #include <wtf/OwnPtr.h>
59 #include <wtf/Vector.h>
60 
61 namespace WebCore {
62 
InspectorDOMAgent(InspectorFrontend * frontend)63 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend)
64     : EventListener(InspectorDOMAgentType)
65     , m_frontend(frontend)
66     , m_lastNodeId(1)
67 {
68 }
69 
~InspectorDOMAgent()70 InspectorDOMAgent::~InspectorDOMAgent()
71 {
72     reset();
73 }
74 
reset()75 void InspectorDOMAgent::reset()
76 {
77     discardBindings();
78 
79     ListHashSet<RefPtr<Document> > copy = m_documents;
80     for (ListHashSet<RefPtr<Document> >::iterator it = copy.begin(); it != copy.end(); ++it)
81         stopListening((*it).get());
82 
83     ASSERT(!m_documents.size());
84 }
85 
setDocument(Document * doc)86 void InspectorDOMAgent::setDocument(Document* doc)
87 {
88     if (doc == mainFrameDocument())
89         return;
90 
91     reset();
92 
93     if (doc) {
94         startListening(doc);
95         if (doc->documentElement())
96             pushDocumentToFrontend();
97     } else
98         m_frontend->setDocument(ScriptObject());
99 }
100 
releaseDanglingNodes()101 void InspectorDOMAgent::releaseDanglingNodes()
102 {
103     deleteAllValues(m_danglingNodeToIdMaps);
104     m_danglingNodeToIdMaps.clear();
105 }
106 
startListening(Document * doc)107 void InspectorDOMAgent::startListening(Document* doc)
108 {
109     if (m_documents.contains(doc))
110         return;
111 
112     doc->addEventListener(eventNames().DOMContentLoadedEvent, this, false);
113     doc->addEventListener(eventNames().loadEvent, this, true);
114     m_documents.add(doc);
115 }
116 
stopListening(Document * doc)117 void InspectorDOMAgent::stopListening(Document* doc)
118 {
119     if (!m_documents.contains(doc))
120         return;
121 
122     doc->removeEventListener(eventNames().DOMContentLoadedEvent, this, false);
123     doc->removeEventListener(eventNames().loadEvent, this, true);
124     m_documents.remove(doc);
125 }
126 
handleEvent(ScriptExecutionContext *,Event * event)127 void InspectorDOMAgent::handleEvent(ScriptExecutionContext*, Event* event)
128 {
129     AtomicString type = event->type();
130     Node* node = event->target()->toNode();
131 
132     if (type == eventNames().DOMContentLoadedEvent) {
133         // Re-push document once it is loaded.
134         discardBindings();
135         pushDocumentToFrontend();
136     } else if (type == eventNames().loadEvent) {
137         long frameOwnerId = m_documentNodeToIdMap.get(node);
138         if (!frameOwnerId)
139             return;
140 
141         if (!m_childrenRequested.contains(frameOwnerId)) {
142             // No children are mapped yet -> only notify on changes of hasChildren.
143             m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(node));
144         } else {
145             // Re-add frame owner element together with its new children.
146             long parentId = m_documentNodeToIdMap.get(innerParentNode(node));
147             m_frontend->childNodeRemoved(parentId, frameOwnerId);
148             ScriptObject value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
149             Node* previousSibling = innerPreviousSibling(node);
150             long prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
151             m_frontend->childNodeInserted(parentId, prevId, value);
152             // Invalidate children requested flag for the element.
153             m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId));
154         }
155     }
156 }
157 
bind(Node * node,NodeToIdMap * nodesMap)158 long InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
159 {
160     long id = nodesMap->get(node);
161     if (id)
162         return id;
163     id = m_lastNodeId++;
164     nodesMap->set(node, id);
165     m_idToNode.set(id, node);
166     m_idToNodesMap.set(id, nodesMap);
167     return id;
168 }
169 
unbind(Node * node,NodeToIdMap * nodesMap)170 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
171 {
172     if (node->isFrameOwnerElement()) {
173         const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
174         stopListening(frameOwner->contentDocument());
175     }
176 
177     int id = nodesMap->get(node);
178     if (!id)
179         return;
180     m_idToNode.remove(id);
181     nodesMap->remove(node);
182     bool childrenRequested = m_childrenRequested.contains(id);
183     if (childrenRequested) {
184         // Unbind subtree known to client recursively.
185         m_childrenRequested.remove(id);
186         Node* child = innerFirstChild(node);
187         while (child) {
188             unbind(child, nodesMap);
189             child = innerNextSibling(child);
190         }
191     }
192 }
193 
pushDocumentToFrontend()194 bool InspectorDOMAgent::pushDocumentToFrontend()
195 {
196     Document* document = mainFrameDocument();
197     if (!document)
198         return false;
199     if (!m_documentNodeToIdMap.contains(document))
200         m_frontend->setDocument(buildObjectForNode(document, 2, &m_documentNodeToIdMap));
201     return true;
202 }
203 
pushChildNodesToFrontend(long nodeId)204 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId)
205 {
206     Node* node = nodeForId(nodeId);
207     if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
208         return;
209     if (m_childrenRequested.contains(nodeId))
210         return;
211 
212     NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
213     ScriptArray children = buildArrayForContainerChildren(node, 1, nodeMap);
214     m_childrenRequested.add(nodeId);
215     m_frontend->setChildNodes(nodeId, children);
216 }
217 
discardBindings()218 void InspectorDOMAgent::discardBindings()
219 {
220     m_documentNodeToIdMap.clear();
221     m_idToNode.clear();
222     releaseDanglingNodes();
223     m_childrenRequested.clear();
224 }
225 
nodeForId(long id)226 Node* InspectorDOMAgent::nodeForId(long id)
227 {
228     if (!id)
229         return 0;
230 
231     HashMap<long, Node*>::iterator it = m_idToNode.find(id);
232     if (it != m_idToNode.end())
233         return it->second;
234     return 0;
235 }
236 
nodeForPath(const String & path)237 Node* InspectorDOMAgent::nodeForPath(const String& path)
238 {
239     // The path is of form "1,HTML,2,BODY,1,DIV"
240     Node* node = mainFrameDocument();
241     if (!node)
242         return 0;
243 
244     Vector<String> pathTokens;
245     path.split(",", false, pathTokens);
246     for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
247         bool success = true;
248         unsigned childNumber = pathTokens[i].toUInt(&success);
249         if (!success)
250             return 0;
251         if (childNumber >= innerChildNodeCount(node))
252             return 0;
253 
254         Node* child = innerFirstChild(node);
255         String childName = pathTokens[i + 1];
256         for (size_t j = 0; child && j < childNumber; ++j)
257             child = innerNextSibling(child);
258 
259         if (!child || child->nodeName() != childName)
260             return 0;
261         node = child;
262     }
263     return node;
264 }
265 
getChildNodes(long callId,long nodeId)266 void InspectorDOMAgent::getChildNodes(long callId, long nodeId)
267 {
268     pushChildNodesToFrontend(nodeId);
269     m_frontend->didGetChildNodes(callId);
270 }
271 
pushNodePathToFrontend(Node * nodeToPush)272 long InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
273 {
274     ASSERT(nodeToPush);  // Invalid input
275 
276     // If we are sending information to the client that is currently being created. Send root node first.
277     if (!pushDocumentToFrontend())
278         return 0;
279 
280     // Return id in case the node is known.
281     long result = m_documentNodeToIdMap.get(nodeToPush);
282     if (result)
283         return result;
284 
285     Node* node = nodeToPush;
286     Vector<Node*> path;
287     NodeToIdMap* danglingMap = 0;
288     while (true) {
289         Node* parent = innerParentNode(node);
290         if (!parent) {
291             // Node being pushed is detached -> push subtree root.
292             danglingMap = new NodeToIdMap();
293             m_danglingNodeToIdMaps.append(danglingMap);
294             m_frontend->setDetachedRoot(buildObjectForNode(node, 0, danglingMap));
295             break;
296         } else {
297             path.append(parent);
298             if (m_documentNodeToIdMap.get(parent))
299                 break;
300             else
301                 node = parent;
302         }
303     }
304 
305     NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
306     for (int i = path.size() - 1; i >= 0; --i) {
307         long nodeId = map->get(path.at(i));
308         ASSERT(nodeId);
309         pushChildNodesToFrontend(nodeId);
310     }
311     return map->get(nodeToPush);
312 }
313 
setAttribute(long callId,long elementId,const String & name,const String & value)314 void InspectorDOMAgent::setAttribute(long callId, long elementId, const String& name, const String& value)
315 {
316     Node* node = nodeForId(elementId);
317     if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
318         Element* element = static_cast<Element*>(node);
319         ExceptionCode ec = 0;
320         element->setAttribute(name, value, ec);
321         m_frontend->didApplyDomChange(callId, ec == 0);
322     } else {
323         m_frontend->didApplyDomChange(callId, false);
324     }
325 }
326 
removeAttribute(long callId,long elementId,const String & name)327 void InspectorDOMAgent::removeAttribute(long callId, long elementId, const String& name)
328 {
329     Node* node = nodeForId(elementId);
330     if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
331         Element* element = static_cast<Element*>(node);
332         ExceptionCode ec = 0;
333         element->removeAttribute(name, ec);
334         m_frontend->didApplyDomChange(callId, ec == 0);
335     } else {
336         m_frontend->didApplyDomChange(callId, false);
337     }
338 }
339 
setTextNodeValue(long callId,long nodeId,const String & value)340 void InspectorDOMAgent::setTextNodeValue(long callId, long nodeId, const String& value)
341 {
342     Node* node = nodeForId(nodeId);
343     if (node && (node->nodeType() == Node::TEXT_NODE)) {
344         Text* text_node = static_cast<Text*>(node);
345         ExceptionCode ec = 0;
346         text_node->replaceWholeText(value, ec);
347         m_frontend->didApplyDomChange(callId, ec == 0);
348     } else {
349         m_frontend->didApplyDomChange(callId, false);
350     }
351 }
352 
getEventListenersForNode(long callId,long nodeId)353 void InspectorDOMAgent::getEventListenersForNode(long callId, long nodeId)
354 {
355     Node* node = nodeForId(nodeId);
356     ScriptArray listenersArray = m_frontend->newScriptArray();
357     unsigned counter = 0;
358     EventTargetData* d;
359 
360     // Quick break if a null node or no listeners at all
361     if (!node || !(d = node->eventTargetData())) {
362         m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
363         return;
364     }
365 
366     // Get the list of event types this Node is concerned with
367     Vector<AtomicString> eventTypes;
368     const EventListenerMap& listenerMap = d->eventListenerMap;
369     EventListenerMap::const_iterator end = listenerMap.end();
370     for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
371         eventTypes.append(iter->first);
372 
373     // Quick break if no useful listeners
374     size_t eventTypesLength = eventTypes.size();
375     if (eventTypesLength == 0) {
376         m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
377         return;
378     }
379 
380     // The Node's Event Ancestors (not including self)
381     Vector<RefPtr<ContainerNode> > ancestors;
382     node->eventAncestors(ancestors);
383 
384     // Nodes and their Listeners for the concerned event types (order is top to bottom)
385     Vector<EventListenerInfo> eventInformation;
386     for (size_t i = ancestors.size(); i; --i) {
387         ContainerNode* ancestor = ancestors[i - 1].get();
388         for (size_t j = 0; j < eventTypesLength; ++j) {
389             AtomicString& type = eventTypes[j];
390             if (ancestor->hasEventListeners(type))
391                 eventInformation.append(EventListenerInfo(static_cast<Node*>(ancestor), type, ancestor->getEventListeners(type)));
392         }
393     }
394 
395     // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
396     for (size_t i = 0; i < eventTypesLength; ++i) {
397         const AtomicString& type = eventTypes[i];
398         eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
399     }
400 
401     // Get Capturing Listeners (in this order)
402     size_t eventInformationLength = eventInformation.size();
403     for (size_t i = 0; i < eventInformationLength; ++i) {
404         const EventListenerInfo& info = eventInformation[i];
405         const EventListenerVector& vector = info.eventListenerVector;
406         for (size_t j = 0; j < vector.size(); ++j) {
407             const RegisteredEventListener& listener = vector[j];
408             if (listener.useCapture)
409                 listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node));
410         }
411     }
412 
413     // Get Bubbling Listeners (reverse order)
414     for (size_t i = eventInformationLength; i; --i) {
415         const EventListenerInfo& info = eventInformation[i - 1];
416         const EventListenerVector& vector = info.eventListenerVector;
417         for (size_t j = 0; j < vector.size(); ++j) {
418             const RegisteredEventListener& listener = vector[j];
419             if (!listener.useCapture)
420                 listenersArray.set(counter++, buildObjectForEventListener(listener, info.eventType, info.node));
421         }
422     }
423 
424     m_frontend->didGetEventListenersForNode(callId, nodeId, listenersArray);
425 }
426 
documentURLString(Document * document) const427 String InspectorDOMAgent::documentURLString(Document* document) const
428 {
429     if (!document || document->url().isNull())
430         return "";
431     return document->url().string();
432 }
433 
buildObjectForNode(Node * node,int depth,NodeToIdMap * nodesMap)434 ScriptObject InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
435 {
436     ScriptObject value = m_frontend->newScriptObject();
437 
438     long id = bind(node, nodesMap);
439     String nodeName;
440     String localName;
441     String nodeValue;
442 
443     switch (node->nodeType()) {
444         case Node::TEXT_NODE:
445         case Node::COMMENT_NODE:
446             nodeValue = node->nodeValue();
447             break;
448         case Node::ATTRIBUTE_NODE:
449             localName = node->localName();
450             break;
451         case Node::DOCUMENT_FRAGMENT_NODE:
452             break;
453         case Node::DOCUMENT_NODE:
454         case Node::ELEMENT_NODE:
455         default:
456             nodeName = node->nodeName();
457             localName = node->localName();
458             break;
459     }
460 
461     value.set("id", id);
462     value.set("nodeType", node->nodeType());
463     value.set("nodeName", nodeName);
464     value.set("localName", localName);
465     value.set("nodeValue", nodeValue);
466 
467     if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
468         int nodeCount = innerChildNodeCount(node);
469         value.set("childNodeCount", nodeCount);
470         ScriptArray children = buildArrayForContainerChildren(node, depth, nodesMap);
471         if (children.length() > 0)
472             value.set("children", children);
473 
474         if (node->nodeType() == Node::ELEMENT_NODE) {
475             Element* element = static_cast<Element*>(node);
476             value.set("attributes", buildArrayForElementAttributes(element));
477             if (node->isFrameOwnerElement()) {
478                 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
479                 value.set("documentURL", documentURLString(frameOwner->contentDocument()));
480             }
481         } else if (node->nodeType() == Node::DOCUMENT_NODE) {
482             Document* document = static_cast<Document*>(node);
483             value.set("documentURL", documentURLString(document));
484         }
485     } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
486         DocumentType* docType = static_cast<DocumentType*>(node);
487         value.set("publicId", docType->publicId());
488         value.set("systemId", docType->systemId());
489         value.set("internalSubset", docType->internalSubset());
490     }
491     return value;
492 }
493 
buildArrayForElementAttributes(Element * element)494 ScriptArray InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
495 {
496     ScriptArray attributesValue = m_frontend->newScriptArray();
497     // Go through all attributes and serialize them.
498     const NamedNodeMap* attrMap = element->attributes(true);
499     if (!attrMap)
500         return attributesValue;
501     unsigned numAttrs = attrMap->length();
502     int index = 0;
503     for (unsigned i = 0; i < numAttrs; ++i) {
504         // Add attribute pair
505         const Attribute *attribute = attrMap->attributeItem(i);
506         attributesValue.set(index++, attribute->name().toString());
507         attributesValue.set(index++, attribute->value());
508     }
509     return attributesValue;
510 }
511 
buildArrayForContainerChildren(Node * container,int depth,NodeToIdMap * nodesMap)512 ScriptArray InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
513 {
514     ScriptArray children = m_frontend->newScriptArray();
515     if (depth == 0) {
516         int index = 0;
517         // Special case the_only text child.
518         if (innerChildNodeCount(container) == 1) {
519             Node *child = innerFirstChild(container);
520             if (child->nodeType() == Node::TEXT_NODE)
521                 children.set(index++, buildObjectForNode(child, 0, nodesMap));
522         }
523         return children;
524     } else if (depth > 0) {
525         depth--;
526     }
527 
528     int index = 0;
529     for (Node *child = innerFirstChild(container); child; child = innerNextSibling(child))
530         children.set(index++, buildObjectForNode(child, depth, nodesMap));
531     return children;
532 }
533 
buildObjectForEventListener(const RegisteredEventListener & registeredEventListener,const AtomicString & eventType,Node * node)534 ScriptObject InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
535 {
536     RefPtr<EventListener> eventListener = registeredEventListener.listener;
537     ScriptObject value = m_frontend->newScriptObject();
538     value.set("type", eventType);
539     value.set("useCapture", registeredEventListener.useCapture);
540     value.set("isAttribute", eventListener->isAttribute());
541     value.set("nodeId", pushNodePathToFrontend(node));
542     value.set("listener", getEventListenerHandlerBody(node->document(), m_frontend->scriptState(), eventListener.get()));
543     return value;
544 }
545 
innerFirstChild(Node * node)546 Node* InspectorDOMAgent::innerFirstChild(Node* node)
547 {
548     if (node->isFrameOwnerElement()) {
549         HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
550         Document* doc = frameOwner->contentDocument();
551         if (doc) {
552             startListening(doc);
553             return doc->firstChild();
554         }
555     }
556     node = node->firstChild();
557     while (isWhitespace(node))
558         node = node->nextSibling();
559     return node;
560 }
561 
innerNextSibling(Node * node)562 Node* InspectorDOMAgent::innerNextSibling(Node* node)
563 {
564     do {
565         node = node->nextSibling();
566     } while (isWhitespace(node));
567     return node;
568 }
569 
innerPreviousSibling(Node * node)570 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
571 {
572     do {
573         node = node->previousSibling();
574     } while (isWhitespace(node));
575     return node;
576 }
577 
innerChildNodeCount(Node * node)578 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
579 {
580     unsigned count = 0;
581     Node* child = innerFirstChild(node);
582     while (child) {
583         count++;
584         child = innerNextSibling(child);
585     }
586     return count;
587 }
588 
innerParentNode(Node * node)589 Node* InspectorDOMAgent::innerParentNode(Node* node)
590 {
591     Node* parent = node->parentNode();
592     if (parent && parent->nodeType() == Node::DOCUMENT_NODE)
593         return static_cast<Document*>(parent)->ownerElement();
594     return parent;
595 }
596 
isWhitespace(Node * node)597 bool InspectorDOMAgent::isWhitespace(Node* node)
598 {
599     //TODO: pull ignoreWhitespace setting from the frontend and use here.
600     return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
601 }
602 
mainFrameDocument() const603 Document* InspectorDOMAgent::mainFrameDocument() const
604 {
605     ListHashSet<RefPtr<Document> >::const_iterator it = m_documents.begin();
606     if (it != m_documents.end())
607         return it->get();
608     return 0;
609 }
610 
operator ==(const EventListener & listener)611 bool InspectorDOMAgent::operator==(const EventListener& listener)
612 {
613     if (const InspectorDOMAgent* inspectorDOMAgentListener = InspectorDOMAgent::cast(&listener))
614         return mainFrameDocument() == inspectorDOMAgentListener->mainFrameDocument();
615     return false;
616 }
617 
didInsertDOMNode(Node * node)618 void InspectorDOMAgent::didInsertDOMNode(Node* node)
619 {
620     if (isWhitespace(node))
621         return;
622 
623     // We could be attaching existing subtree. Forget the bindings.
624     unbind(node, &m_documentNodeToIdMap);
625 
626     Node* parent = node->parentNode();
627     long parentId = m_documentNodeToIdMap.get(parent);
628     // Return if parent is not mapped yet.
629     if (!parentId)
630         return;
631 
632     if (!m_childrenRequested.contains(parentId)) {
633         // No children are mapped yet -> only notify on changes of hasChildren.
634         m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
635     } else {
636         // Children have been requested -> return value of a new child.
637         Node* prevSibling = innerPreviousSibling(node);
638         long prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
639         ScriptObject value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
640         m_frontend->childNodeInserted(parentId, prevId, value);
641     }
642 }
643 
didRemoveDOMNode(Node * node)644 void InspectorDOMAgent::didRemoveDOMNode(Node* node)
645 {
646     if (isWhitespace(node))
647         return;
648 
649     Node* parent = node->parentNode();
650     long parentId = m_documentNodeToIdMap.get(parent);
651     // If parent is not mapped yet -> ignore the event.
652     if (!parentId)
653         return;
654 
655     if (!m_childrenRequested.contains(parentId)) {
656         // No children are mapped yet -> only notify on changes of hasChildren.
657         if (innerChildNodeCount(parent) == 1)
658             m_frontend->childNodeCountUpdated(parentId, 0);
659     } else
660         m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
661     unbind(node, &m_documentNodeToIdMap);
662 }
663 
didModifyDOMAttr(Element * element)664 void InspectorDOMAgent::didModifyDOMAttr(Element* element)
665 {
666     long id = m_documentNodeToIdMap.get(element);
667     // If node is not mapped yet -> ignore the event.
668     if (!id)
669         return;
670 
671     m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
672 }
673 
674 } // namespace WebCore
675 
676 #endif // ENABLE(INSPECTOR)
677