2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Copyright (C) 2009 Joseph Pecoraro
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
32 #include "InspectorDOMAgent.h"
36 #include "AtomicString.h"
37 #include "ContainerNode.h"
39 #include "CookieJar.h"
40 #include "DOMWindow.h"
43 #include "EventListener.h"
44 #include "EventNames.h"
45 #include "EventTarget.h"
46 #include "HTMLFrameOwnerElement.h"
47 #include "InspectorFrontend.h"
49 #include "MutationEvent.h"
52 #include "PlatformString.h"
53 #include "ScriptEventListener.h"
54 #include "ScriptObject.h"
57 #include <wtf/OwnPtr.h>
58 #include <wtf/Vector.h>
62 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend
* frontend
)
63 : EventListener(InspectorDOMAgentType
)
64 , m_frontend(frontend
)
69 InspectorDOMAgent::~InspectorDOMAgent()
74 void InspectorDOMAgent::reset()
78 ListHashSet
<RefPtr
<Document
> > copy
= m_documents
;
79 for (ListHashSet
<RefPtr
<Document
> >::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
80 stopListening((*it
).get());
82 ASSERT(!m_documents
.size());
85 void InspectorDOMAgent::setDocument(Document
* doc
)
87 if (doc
== mainFrameDocument())
94 if (doc
->documentElement())
95 pushDocumentToFrontend();
97 m_frontend
->setDocument(ScriptObject());
100 void InspectorDOMAgent::releaseDanglingNodes()
102 deleteAllValues(m_danglingNodeToIdMaps
);
103 m_danglingNodeToIdMaps
.clear();
106 void InspectorDOMAgent::startListening(Document
* doc
)
108 if (m_documents
.contains(doc
))
111 doc
->addEventListener(eventNames().DOMContentLoadedEvent
, this, false);
112 doc
->addEventListener(eventNames().DOMNodeInsertedEvent
, this, false);
113 doc
->addEventListener(eventNames().DOMNodeRemovedEvent
, this, false);
114 doc
->addEventListener(eventNames().DOMAttrModifiedEvent
, this, false);
115 doc
->addEventListener(eventNames().loadEvent
, this, true);
116 m_documents
.add(doc
);
119 void InspectorDOMAgent::stopListening(Document
* doc
)
121 if (!m_documents
.contains(doc
))
124 doc
->removeEventListener(eventNames().DOMContentLoadedEvent
, this, false);
125 doc
->removeEventListener(eventNames().DOMNodeInsertedEvent
, this, false);
126 doc
->removeEventListener(eventNames().DOMNodeRemovedEvent
, this, false);
127 doc
->removeEventListener(eventNames().DOMAttrModifiedEvent
, this, false);
128 doc
->removeEventListener(eventNames().loadEvent
, this, true);
129 m_documents
.remove(doc
);
132 void InspectorDOMAgent::handleEvent(ScriptExecutionContext
*, Event
* event
)
134 AtomicString type
= event
->type();
135 Node
* node
= event
->target()->toNode();
137 if (type
== eventNames().DOMAttrModifiedEvent
) {
138 long id
= m_documentNodeToIdMap
.get(node
);
139 // If node is not mapped yet -> ignore the event.
143 Element
* element
= static_cast<Element
*>(node
);
144 m_frontend
->attributesUpdated(id
, buildArrayForElementAttributes(element
));
145 } else if (type
== eventNames().DOMNodeInsertedEvent
) {
146 if (isWhitespace(node
))
149 // We could be attaching existing subtree. Forget the bindings.
150 unbind(node
, &m_documentNodeToIdMap
);
152 Node
* parent
= static_cast<MutationEvent
*>(event
)->relatedNode();
153 long parentId
= m_documentNodeToIdMap
.get(parent
);
154 // Return if parent is not mapped yet.
158 if (!m_childrenRequested
.contains(parentId
)) {
159 // No children are mapped yet -> only notify on changes of hasChildren.
160 m_frontend
->childNodeCountUpdated(parentId
, innerChildNodeCount(parent
));
162 // Children have been requested -> return value of a new child.
163 Node
* prevSibling
= innerPreviousSibling(node
);
164 long prevId
= prevSibling
? m_documentNodeToIdMap
.get(prevSibling
) : 0;
165 ScriptObject value
= buildObjectForNode(node
, 0, &m_documentNodeToIdMap
);
166 m_frontend
->childNodeInserted(parentId
, prevId
, value
);
168 } else if (type
== eventNames().DOMNodeRemovedEvent
) {
169 if (isWhitespace(node
))
172 Node
* parent
= static_cast<MutationEvent
*>(event
)->relatedNode();
173 long parentId
= m_documentNodeToIdMap
.get(parent
);
174 // If parent is not mapped yet -> ignore the event.
178 if (!m_childrenRequested
.contains(parentId
)) {
179 // No children are mapped yet -> only notify on changes of hasChildren.
180 if (innerChildNodeCount(parent
) == 1)
181 m_frontend
->childNodeCountUpdated(parentId
, 0);
183 m_frontend
->childNodeRemoved(parentId
, m_documentNodeToIdMap
.get(node
));
185 unbind(node
, &m_documentNodeToIdMap
);
186 } else if (type
== eventNames().DOMContentLoadedEvent
) {
187 // Re-push document once it is loaded.
189 pushDocumentToFrontend();
190 } else if (type
== eventNames().loadEvent
) {
191 long frameOwnerId
= m_documentNodeToIdMap
.get(node
);
195 if (!m_childrenRequested
.contains(frameOwnerId
)) {
196 // No children are mapped yet -> only notify on changes of hasChildren.
197 m_frontend
->childNodeCountUpdated(frameOwnerId
, innerChildNodeCount(node
));
199 // Re-add frame owner element together with its new children.
200 long parentId
= m_documentNodeToIdMap
.get(innerParentNode(node
));
201 m_frontend
->childNodeRemoved(parentId
, frameOwnerId
);
202 ScriptObject value
= buildObjectForNode(node
, 0, &m_documentNodeToIdMap
);
203 Node
* previousSibling
= innerPreviousSibling(node
);
204 long prevId
= previousSibling
? m_documentNodeToIdMap
.get(previousSibling
) : 0;
205 m_frontend
->childNodeInserted(parentId
, prevId
, value
);
206 // Invalidate children requested flag for the element.
207 m_childrenRequested
.remove(m_childrenRequested
.find(frameOwnerId
));
212 long InspectorDOMAgent::bind(Node
* node
, NodeToIdMap
* nodesMap
)
214 long id
= nodesMap
->get(node
);
218 nodesMap
->set(node
, id
);
219 m_idToNode
.set(id
, node
);
220 m_idToNodesMap
.set(id
, nodesMap
);
224 void InspectorDOMAgent::unbind(Node
* node
, NodeToIdMap
* nodesMap
)
226 if (node
->isFrameOwnerElement()) {
227 const HTMLFrameOwnerElement
* frameOwner
= static_cast<const HTMLFrameOwnerElement
*>(node
);
228 stopListening(frameOwner
->contentDocument());
231 int id
= nodesMap
->get(node
);
234 m_idToNode
.remove(id
);
235 nodesMap
->remove(node
);
236 bool childrenRequested
= m_childrenRequested
.contains(id
);
237 if (childrenRequested
) {
238 // Unbind subtree known to client recursively.
239 m_childrenRequested
.remove(id
);
240 Node
* child
= innerFirstChild(node
);
242 unbind(child
, nodesMap
);
243 child
= innerNextSibling(child
);
248 bool InspectorDOMAgent::pushDocumentToFrontend()
250 Document
* document
= mainFrameDocument();
253 if (!m_documentNodeToIdMap
.contains(document
))
254 m_frontend
->setDocument(buildObjectForNode(document
, 2, &m_documentNodeToIdMap
));
258 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId
)
260 Node
* node
= nodeForId(nodeId
);
261 if (!node
|| (node
->nodeType() != Node::ELEMENT_NODE
&& node
->nodeType() != Node::DOCUMENT_NODE
))
263 if (m_childrenRequested
.contains(nodeId
))
266 NodeToIdMap
* nodeMap
= m_idToNodesMap
.get(nodeId
);
267 ScriptArray children
= buildArrayForContainerChildren(node
, 1, nodeMap
);
268 m_childrenRequested
.add(nodeId
);
269 m_frontend
->setChildNodes(nodeId
, children
);
272 void InspectorDOMAgent::discardBindings()
274 m_documentNodeToIdMap
.clear();
276 releaseDanglingNodes();
277 m_childrenRequested
.clear();
280 Node
* InspectorDOMAgent::nodeForId(long id
)
285 HashMap
<long, Node
*>::iterator it
= m_idToNode
.find(id
);
286 if (it
!= m_idToNode
.end())
291 Node
* InspectorDOMAgent::nodeForPath(const String
& path
)
293 // The path is of form "1,HTML,2,BODY,1,DIV"
294 Node
* node
= mainFrameDocument();
298 Vector
<String
> pathTokens
;
299 path
.split(",", false, pathTokens
);
300 for (size_t i
= 0; i
< pathTokens
.size() - 1; i
+= 2) {
302 unsigned childNumber
= pathTokens
[i
].toUInt(&success
);
305 if (childNumber
>= innerChildNodeCount(node
))
308 Node
* child
= innerFirstChild(node
);
309 String childName
= pathTokens
[i
+ 1];
310 for (size_t j
= 0; child
&& j
< childNumber
; ++j
)
311 child
= innerNextSibling(child
);
313 if (!child
|| child
->nodeName() != childName
)
320 void InspectorDOMAgent::getChildNodes(long callId
, long nodeId
)
322 pushChildNodesToFrontend(nodeId
);
323 m_frontend
->didGetChildNodes(callId
);
326 long InspectorDOMAgent::pushNodePathToFrontend(Node
* nodeToPush
)
328 ASSERT(nodeToPush
); // Invalid input
330 // If we are sending information to the client that is currently being created. Send root node first.
331 if (!pushDocumentToFrontend())
334 // Return id in case the node is known.
335 long result
= m_documentNodeToIdMap
.get(nodeToPush
);
339 Node
* node
= nodeToPush
;
341 NodeToIdMap
* danglingMap
= 0;
343 Node
* parent
= innerParentNode(node
);
345 // Node being pushed is detached -> push subtree root.
346 danglingMap
= new NodeToIdMap();
347 m_danglingNodeToIdMaps
.append(danglingMap
);
348 m_frontend
->setDetachedRoot(buildObjectForNode(node
, 0, danglingMap
));
352 if (m_documentNodeToIdMap
.get(parent
))
359 NodeToIdMap
* map
= danglingMap
? danglingMap
: &m_documentNodeToIdMap
;
360 for (int i
= path
.size() - 1; i
>= 0; --i
) {
361 long nodeId
= map
->get(path
.at(i
));
363 pushChildNodesToFrontend(nodeId
);
365 return map
->get(nodeToPush
);
368 void InspectorDOMAgent::setAttribute(long callId
, long elementId
, const String
& name
, const String
& value
)
370 Node
* node
= nodeForId(elementId
);
371 if (node
&& (node
->nodeType() == Node::ELEMENT_NODE
)) {
372 Element
* element
= static_cast<Element
*>(node
);
373 ExceptionCode ec
= 0;
374 element
->setAttribute(name
, value
, ec
);
375 m_frontend
->didApplyDomChange(callId
, ec
== 0);
377 m_frontend
->didApplyDomChange(callId
, false);
381 void InspectorDOMAgent::removeAttribute(long callId
, long elementId
, const String
& name
)
383 Node
* node
= nodeForId(elementId
);
384 if (node
&& (node
->nodeType() == Node::ELEMENT_NODE
)) {
385 Element
* element
= static_cast<Element
*>(node
);
386 ExceptionCode ec
= 0;
387 element
->removeAttribute(name
, ec
);
388 m_frontend
->didApplyDomChange(callId
, ec
== 0);
390 m_frontend
->didApplyDomChange(callId
, false);
394 void InspectorDOMAgent::setTextNodeValue(long callId
, long nodeId
, const String
& value
)
396 Node
* node
= nodeForId(nodeId
);
397 if (node
&& (node
->nodeType() == Node::TEXT_NODE
)) {
398 Text
* text_node
= static_cast<Text
*>(node
);
399 ExceptionCode ec
= 0;
400 text_node
->replaceWholeText(value
, ec
);
401 m_frontend
->didApplyDomChange(callId
, ec
== 0);
403 m_frontend
->didApplyDomChange(callId
, false);
407 void InspectorDOMAgent::getEventListenersForNode(long callId
, long nodeId
)
409 Node
* node
= nodeForId(nodeId
);
410 ScriptArray listenersArray
= m_frontend
->newScriptArray();
411 unsigned counter
= 0;
414 // Quick break if a null node or no listeners at all
415 if (!node
|| !(d
= node
->eventTargetData())) {
416 m_frontend
->didGetEventListenersForNode(callId
, nodeId
, listenersArray
);
420 // Get the list of event types this Node is concerned with
421 Vector
<AtomicString
> eventTypes
;
422 const EventListenerMap
& listenerMap
= d
->eventListenerMap
;
423 EventListenerMap::const_iterator end
= listenerMap
.end();
424 for (EventListenerMap::const_iterator iter
= listenerMap
.begin(); iter
!= end
; ++iter
)
425 eventTypes
.append(iter
->first
);
427 // Quick break if no useful listeners
428 size_t eventTypesLength
= eventTypes
.size();
429 if (eventTypesLength
== 0) {
430 m_frontend
->didGetEventListenersForNode(callId
, nodeId
, listenersArray
);
434 // The Node's Event Ancestors (not including self)
435 Vector
<RefPtr
<ContainerNode
> > ancestors
;
436 node
->eventAncestors(ancestors
);
438 // Nodes and their Listeners for the concerned event types (order is top to bottom)
439 Vector
<EventListenerInfo
> eventInformation
;
440 for (size_t i
= ancestors
.size(); i
; --i
) {
441 ContainerNode
* ancestor
= ancestors
[i
- 1].get();
442 for (size_t j
= 0; j
< eventTypesLength
; ++j
) {
443 AtomicString
& type
= eventTypes
[j
];
444 if (ancestor
->hasEventListeners(type
))
445 eventInformation
.append(EventListenerInfo(static_cast<Node
*>(ancestor
), type
, ancestor
->getEventListeners(type
)));
449 // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
450 for (size_t i
= 0; i
< eventTypesLength
; ++i
) {
451 const AtomicString
& type
= eventTypes
[i
];
452 eventInformation
.append(EventListenerInfo(node
, type
, node
->getEventListeners(type
)));
455 // Get Capturing Listeners (in this order)
456 size_t eventInformationLength
= eventInformation
.size();
457 for (size_t i
= 0; i
< eventInformationLength
; ++i
) {
458 const EventListenerInfo
& info
= eventInformation
[i
];
459 const EventListenerVector
& vector
= info
.eventListenerVector
;
460 for (size_t j
= 0; j
< vector
.size(); ++j
) {
461 const RegisteredEventListener
& listener
= vector
[j
];
462 if (listener
.useCapture
)
463 listenersArray
.set(counter
++, buildObjectForEventListener(listener
, info
.eventType
, info
.node
));
467 // Get Bubbling Listeners (reverse order)
468 for (size_t i
= eventInformationLength
; i
; --i
) {
469 const EventListenerInfo
& info
= eventInformation
[i
- 1];
470 const EventListenerVector
& vector
= info
.eventListenerVector
;
471 for (size_t j
= 0; j
< vector
.size(); ++j
) {
472 const RegisteredEventListener
& listener
= vector
[j
];
473 if (!listener
.useCapture
)
474 listenersArray
.set(counter
++, buildObjectForEventListener(listener
, info
.eventType
, info
.node
));
478 m_frontend
->didGetEventListenersForNode(callId
, nodeId
, listenersArray
);
481 ScriptObject
InspectorDOMAgent::buildObjectForNode(Node
* node
, int depth
, NodeToIdMap
* nodesMap
)
483 ScriptObject value
= m_frontend
->newScriptObject();
485 long id
= bind(node
, nodesMap
);
490 switch (node
->nodeType()) {
491 case Node::TEXT_NODE
:
492 case Node::COMMENT_NODE
:
493 nodeValue
= node
->nodeValue();
495 case Node::ATTRIBUTE_NODE
:
496 localName
= node
->localName();
498 case Node::DOCUMENT_FRAGMENT_NODE
:
500 case Node::DOCUMENT_NODE
:
501 case Node::ELEMENT_NODE
:
503 nodeName
= node
->nodeName();
504 localName
= node
->localName();
509 value
.set("nodeType", node
->nodeType());
510 value
.set("nodeName", nodeName
);
511 value
.set("localName", localName
);
512 value
.set("nodeValue", nodeValue
);
514 if (node
->nodeType() == Node::ELEMENT_NODE
) {
515 Element
* element
= static_cast<Element
*>(node
);
516 value
.set("attributes", buildArrayForElementAttributes(element
));
518 if (node
->nodeType() == Node::ELEMENT_NODE
|| node
->nodeType() == Node::DOCUMENT_NODE
) {
519 int nodeCount
= innerChildNodeCount(node
);
520 value
.set("childNodeCount", nodeCount
);
521 ScriptArray children
= buildArrayForContainerChildren(node
, depth
, nodesMap
);
522 if (children
.length() > 0)
523 value
.set("children", children
);
528 ScriptArray
InspectorDOMAgent::buildArrayForElementAttributes(Element
* element
)
530 ScriptArray attributesValue
= m_frontend
->newScriptArray();
531 // Go through all attributes and serialize them.
532 const NamedNodeMap
* attrMap
= element
->attributes(true);
534 return attributesValue
;
535 unsigned numAttrs
= attrMap
->length();
537 for (unsigned i
= 0; i
< numAttrs
; ++i
) {
538 // Add attribute pair
539 const Attribute
*attribute
= attrMap
->attributeItem(i
);
540 attributesValue
.set(index
++, attribute
->name().toString());
541 attributesValue
.set(index
++, attribute
->value());
543 return attributesValue
;
546 ScriptArray
InspectorDOMAgent::buildArrayForContainerChildren(Node
* container
, int depth
, NodeToIdMap
* nodesMap
)
548 ScriptArray children
= m_frontend
->newScriptArray();
551 // Special case the_only text child.
552 if (innerChildNodeCount(container
) == 1) {
553 Node
*child
= innerFirstChild(container
);
554 if (child
->nodeType() == Node::TEXT_NODE
)
555 children
.set(index
++, buildObjectForNode(child
, 0, nodesMap
));
558 } else if (depth
> 0) {
563 for (Node
*child
= innerFirstChild(container
); child
; child
= innerNextSibling(child
))
564 children
.set(index
++, buildObjectForNode(child
, depth
, nodesMap
));
568 ScriptObject
InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener
& registeredEventListener
, const AtomicString
& eventType
, Node
* node
)
570 RefPtr
<EventListener
> eventListener
= registeredEventListener
.listener
;
571 ScriptObject value
= m_frontend
->newScriptObject();
572 value
.set("type", eventType
);
573 value
.set("useCapture", registeredEventListener
.useCapture
);
574 value
.set("isAttribute", eventListener
->isAttribute());
575 value
.set("nodeId", pushNodePathToFrontend(node
));
576 value
.set("listener", getEventListenerHandlerBody(node
->document(), m_frontend
->scriptState(), eventListener
.get()));
580 Node
* InspectorDOMAgent::innerFirstChild(Node
* node
)
582 if (node
->isFrameOwnerElement()) {
583 HTMLFrameOwnerElement
* frameOwner
= static_cast<HTMLFrameOwnerElement
*>(node
);
584 Document
* doc
= frameOwner
->contentDocument();
587 return doc
->firstChild();
590 node
= node
->firstChild();
591 while (isWhitespace(node
))
592 node
= node
->nextSibling();
596 Node
* InspectorDOMAgent::innerNextSibling(Node
* node
)
599 node
= node
->nextSibling();
600 } while (isWhitespace(node
));
604 Node
* InspectorDOMAgent::innerPreviousSibling(Node
* node
)
607 node
= node
->previousSibling();
608 } while (isWhitespace(node
));
612 unsigned InspectorDOMAgent::innerChildNodeCount(Node
* node
)
615 Node
* child
= innerFirstChild(node
);
618 child
= innerNextSibling(child
);
623 Node
* InspectorDOMAgent::innerParentNode(Node
* node
)
625 Node
* parent
= node
->parentNode();
626 if (parent
&& parent
->nodeType() == Node::DOCUMENT_NODE
)
627 return static_cast<Document
*>(parent
)->ownerElement();
631 bool InspectorDOMAgent::isWhitespace(Node
* node
)
633 //TODO: pull ignoreWhitespace setting from the frontend and use here.
634 return node
&& node
->nodeType() == Node::TEXT_NODE
&& node
->nodeValue().stripWhiteSpace().length() == 0;
637 Document
* InspectorDOMAgent::mainFrameDocument() const
639 ListHashSet
<RefPtr
<Document
> >::const_iterator it
= m_documents
.begin();
640 if (it
!= m_documents
.end())
645 bool InspectorDOMAgent::operator==(const EventListener
& listener
)
647 if (const InspectorDOMAgent
* inspectorDOMAgentListener
= InspectorDOMAgent::cast(&listener
))
648 return mainFrameDocument() == inspectorDOMAgentListener
->mainFrameDocument();
652 } // namespace WebCore
654 #endif // ENABLE(INSPECTOR)