Rubber-stamped by Brady Eidson.
[webbrowser.git] / WebCore / inspector / InspectorDOMAgent.cpp
blob1aaf3098e7b793d096772b2a6a4f83d005533411
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
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
31 #include "config.h"
32 #include "InspectorDOMAgent.h"
34 #if ENABLE(INSPECTOR)
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 "Event.h"
43 #include "EventListener.h"
44 #include "EventNames.h"
45 #include "EventTarget.h"
46 #include "HTMLFrameOwnerElement.h"
47 #include "InspectorFrontend.h"
48 #include "markup.h"
49 #include "MutationEvent.h"
50 #include "Node.h"
51 #include "NodeList.h"
52 #include "PlatformString.h"
53 #include "ScriptEventListener.h"
54 #include "ScriptObject.h"
55 #include "Text.h"
57 #include <wtf/OwnPtr.h>
58 #include <wtf/Vector.h>
60 namespace WebCore {
62 InspectorDOMAgent::InspectorDOMAgent(InspectorFrontend* frontend)
63 : EventListener(InspectorDOMAgentType)
64 , m_frontend(frontend)
65 , m_lastNodeId(1)
69 InspectorDOMAgent::~InspectorDOMAgent()
71 reset();
74 void InspectorDOMAgent::reset()
76 discardBindings();
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())
88 return;
90 reset();
92 if (doc) {
93 startListening(doc);
94 if (doc->documentElement())
95 pushDocumentToFrontend();
96 } else
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))
109 return;
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))
122 return;
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.
140 if (!id)
141 return;
143 Element* element = static_cast<Element*>(node);
144 m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
145 } else if (type == eventNames().DOMNodeInsertedEvent) {
146 if (isWhitespace(node))
147 return;
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.
155 if (!parentId)
156 return;
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));
161 } else {
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))
170 return;
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.
175 if (!parentId)
176 return;
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);
182 } else {
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.
188 discardBindings();
189 pushDocumentToFrontend();
190 } else if (type == eventNames().loadEvent) {
191 long frameOwnerId = m_documentNodeToIdMap.get(node);
192 if (!frameOwnerId)
193 return;
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));
198 } else {
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);
215 if (id)
216 return id;
217 id = m_lastNodeId++;
218 nodesMap->set(node, id);
219 m_idToNode.set(id, node);
220 m_idToNodesMap.set(id, nodesMap);
221 return id;
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);
232 if (!id)
233 return;
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);
241 while (child) {
242 unbind(child, nodesMap);
243 child = innerNextSibling(child);
248 bool InspectorDOMAgent::pushDocumentToFrontend()
250 Document* document = mainFrameDocument();
251 if (!document)
252 return false;
253 if (!m_documentNodeToIdMap.contains(document))
254 m_frontend->setDocument(buildObjectForNode(document, 2, &m_documentNodeToIdMap));
255 return true;
258 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId)
260 Node* node = nodeForId(nodeId);
261 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE))
262 return;
263 if (m_childrenRequested.contains(nodeId))
264 return;
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();
275 m_idToNode.clear();
276 releaseDanglingNodes();
277 m_childrenRequested.clear();
280 Node* InspectorDOMAgent::nodeForId(long id)
282 if (!id)
283 return 0;
285 HashMap<long, Node*>::iterator it = m_idToNode.find(id);
286 if (it != m_idToNode.end())
287 return it->second;
288 return 0;
291 Node* InspectorDOMAgent::nodeForPath(const String& path)
293 // The path is of form "1,HTML,2,BODY,1,DIV"
294 Node* node = mainFrameDocument();
295 if (!node)
296 return 0;
298 Vector<String> pathTokens;
299 path.split(",", false, pathTokens);
300 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
301 bool success = true;
302 unsigned childNumber = pathTokens[i].toUInt(&success);
303 if (!success)
304 return 0;
305 if (childNumber >= innerChildNodeCount(node))
306 return 0;
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)
314 return 0;
315 node = child;
317 return node;
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())
332 return 0;
334 // Return id in case the node is known.
335 long result = m_documentNodeToIdMap.get(nodeToPush);
336 if (result)
337 return result;
339 Node* node = nodeToPush;
340 Vector<Node*> path;
341 NodeToIdMap* danglingMap = 0;
342 while (true) {
343 Node* parent = innerParentNode(node);
344 if (!parent) {
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));
349 break;
350 } else {
351 path.append(parent);
352 if (m_documentNodeToIdMap.get(parent))
353 break;
354 else
355 node = 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));
362 ASSERT(nodeId);
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);
376 } else {
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);
389 } else {
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);
402 } else {
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;
412 EventTargetData* d;
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);
417 return;
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);
431 return;
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);
486 String nodeName;
487 String localName;
488 String nodeValue;
490 switch (node->nodeType()) {
491 case Node::TEXT_NODE:
492 case Node::COMMENT_NODE:
493 nodeValue = node->nodeValue();
494 break;
495 case Node::ATTRIBUTE_NODE:
496 localName = node->localName();
497 break;
498 case Node::DOCUMENT_FRAGMENT_NODE:
499 break;
500 case Node::DOCUMENT_NODE:
501 case Node::ELEMENT_NODE:
502 default:
503 nodeName = node->nodeName();
504 localName = node->localName();
505 break;
508 value.set("id", id);
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);
525 return value;
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);
533 if (!attrMap)
534 return attributesValue;
535 unsigned numAttrs = attrMap->length();
536 int index = 0;
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();
549 if (depth == 0) {
550 int index = 0;
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));
557 return children;
558 } else if (depth > 0) {
559 depth--;
562 int index = 0;
563 for (Node *child = innerFirstChild(container); child; child = innerNextSibling(child))
564 children.set(index++, buildObjectForNode(child, depth, nodesMap));
565 return children;
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()));
577 return value;
580 Node* InspectorDOMAgent::innerFirstChild(Node* node)
582 if (node->isFrameOwnerElement()) {
583 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
584 Document* doc = frameOwner->contentDocument();
585 if (doc) {
586 startListening(doc);
587 return doc->firstChild();
590 node = node->firstChild();
591 while (isWhitespace(node))
592 node = node->nextSibling();
593 return node;
596 Node* InspectorDOMAgent::innerNextSibling(Node* node)
598 do {
599 node = node->nextSibling();
600 } while (isWhitespace(node));
601 return node;
604 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
606 do {
607 node = node->previousSibling();
608 } while (isWhitespace(node));
609 return node;
612 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
614 unsigned count = 0;
615 Node* child = innerFirstChild(node);
616 while (child) {
617 count++;
618 child = innerNextSibling(child);
620 return count;
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();
628 return parent;
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())
641 return it->get();
642 return 0;
645 bool InspectorDOMAgent::operator==(const EventListener& listener)
647 if (const InspectorDOMAgent* inspectorDOMAgentListener = InspectorDOMAgent::cast(&listener))
648 return mainFrameDocument() == inspectorDOMAgentListener->mainFrameDocument();
649 return false;
652 } // namespace WebCore
654 #endif // ENABLE(INSPECTOR)