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