Home | History | Annotate | Download | only in inspector
      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 
     63 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend)
     64     : EventListener(InspectorDOMAgentType)
     65     , m_frontend(frontend)
     66     , m_lastNodeId(1)
     67 {
     68 }
     69 
     70 InspectorDOMAgent::~InspectorDOMAgent()
     71 {
     72     reset();
     73 }
     74 
     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 
     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 
    101 void InspectorDOMAgent::releaseDanglingNodes()
    102 {
    103     deleteAllValues(m_danglingNodeToIdMaps);
    104     m_danglingNodeToIdMaps.clear();
    105 }
    106 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    218 void InspectorDOMAgent::discardBindings()
    219 {
    220     m_documentNodeToIdMap.clear();
    221     m_idToNode.clear();
    222     releaseDanglingNodes();
    223     m_childrenRequested.clear();
    224 }
    225 
    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 
    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 
    266 void InspectorDOMAgent::getChildNodes(long callId, long nodeId)
    267 {
    268     pushChildNodesToFrontend(nodeId);
    269     m_frontend->didGetChildNodes(callId);
    270 }
    271 
    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 
    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 
    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 
    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 
    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 
    427 String InspectorDOMAgent::documentURLString(Document* document) const
    428 {
    429     if (!document || document->url().isNull())
    430         return "";
    431     return document->url().string();
    432 }
    433 
    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 
    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 
    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 
    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 
    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 
    562 Node* InspectorDOMAgent::innerNextSibling(Node* node)
    563 {
    564     do {
    565         node = node->nextSibling();
    566     } while (isWhitespace(node));
    567     return node;
    568 }
    569 
    570 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
    571 {
    572     do {
    573         node = node->previousSibling();
    574     } while (isWhitespace(node));
    575     return node;
    576 }
    577 
    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 
    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 
    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 
    603 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 
    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 
    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 
    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 
    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