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"
37 #include "CSSComputedStyleDeclaration.h"
38 #include "CSSMutableStyleDeclaration.h"
39 #include "CSSPropertyNames.h"
40 #include "CSSPropertySourceData.h"
42 #include "CSSRuleList.h"
43 #include "CSSStyleRule.h"
44 #include "CSSStyleSelector.h"
45 #include "CSSStyleSheet.h"
46 #include "CharacterData.h"
47 #include "ContainerNode.h"
49 #include "CookieJar.h"
50 #include "DOMWindow.h"
52 #include "DocumentType.h"
54 #include "EventContext.h"
55 #include "EventListener.h"
56 #include "EventNames.h"
57 #include "EventTarget.h"
59 #include "FrameTree.h"
60 #include "HTMLElement.h"
61 #include "HTMLFrameOwnerElement.h"
62 #include "InjectedScriptHost.h"
63 #include "InspectorFrontend.h"
64 #include "InstrumentingAgents.h"
65 #include "MutationEvent.h"
68 #include "Pasteboard.h"
69 #include "PlatformString.h"
70 #include "RenderStyle.h"
71 #include "RenderStyleConstants.h"
72 #include "ScriptEventListener.h"
73 #include "StyleSheetList.h"
77 #include "XPathResult.h"
82 #include <wtf/text/CString.h>
83 #include <wtf/text/StringConcatenate.h>
84 #include <wtf/HashSet.h>
85 #include <wtf/ListHashSet.h>
86 #include <wtf/OwnPtr.h>
87 #include <wtf/Vector.h>
88 #include <wtf/text/AtomicString.h>
94 virtual void match(ListHashSet<Node*>& resultCollector) = 0;
95 virtual ~MatchJob() { }
98 MatchJob(Document* document, const String& query)
99 : m_document(document)
102 void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector)
104 for (unsigned i = 0; nodes && i < nodes->length(); ++i)
105 resultCollector.add(nodes->item(i));
108 RefPtr<Document> m_document;
112 class RevalidateStyleAttributeTask {
114 RevalidateStyleAttributeTask(InspectorDOMAgent*);
115 void scheduleFor(Element*);
116 void reset() { m_timer.stop(); }
117 void onTimer(Timer<RevalidateStyleAttributeTask>*);
120 InspectorDOMAgent* m_domAgent;
121 Timer<RevalidateStyleAttributeTask> m_timer;
122 HashSet<RefPtr<Element> > m_elements;
127 class MatchExactIdJob : public WebCore::MatchJob {
129 MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
130 virtual ~MatchExactIdJob() { }
133 virtual void match(ListHashSet<Node*>& resultCollector)
135 if (m_query.isEmpty())
138 Element* element = m_document->getElementById(m_query);
140 resultCollector.add(element);
144 class MatchExactClassNamesJob : public WebCore::MatchJob {
146 MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
147 virtual ~MatchExactClassNamesJob() { }
149 virtual void match(ListHashSet<Node*>& resultCollector)
151 if (!m_query.isEmpty())
152 addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector);
156 class MatchExactTagNamesJob : public WebCore::MatchJob {
158 MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
159 virtual ~MatchExactTagNamesJob() { }
161 virtual void match(ListHashSet<Node*>& resultCollector)
163 if (!m_query.isEmpty())
164 addNodesToResults(m_document->getElementsByName(m_query), resultCollector);
168 class MatchQuerySelectorAllJob : public WebCore::MatchJob {
170 MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
171 virtual ~MatchQuerySelectorAllJob() { }
173 virtual void match(ListHashSet<Node*>& resultCollector)
175 if (m_query.isEmpty())
178 ExceptionCode ec = 0;
179 RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec);
181 addNodesToResults(list, resultCollector);
185 class MatchXPathJob : public WebCore::MatchJob {
187 MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
188 virtual ~MatchXPathJob() { }
190 virtual void match(ListHashSet<Node*>& resultCollector)
193 if (m_query.isEmpty())
196 ExceptionCode ec = 0;
197 RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
201 unsigned long size = result->snapshotLength(ec);
202 for (unsigned long i = 0; !ec && i < size; ++i) {
203 Node* node = result->snapshotItem(i, ec);
205 resultCollector.add(node);
208 UNUSED_PARAM(resultCollector);
213 class MatchPlainTextJob : public MatchXPathJob {
215 MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query)
217 m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]";
219 virtual ~MatchPlainTextJob() { }
224 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
225 : m_domAgent(domAgent)
226 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
230 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
232 m_elements.add(element);
233 if (!m_timer.isActive())
234 m_timer.startOneShot(0);
237 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
239 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
240 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
241 m_domAgent->didModifyDOMAttr(it->get());
246 InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, InjectedScriptHost* injectedScriptHost)
247 : m_instrumentingAgents(instrumentingAgents)
248 , m_injectedScriptHost(injectedScriptHost)
252 , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer)
256 InspectorDOMAgent::~InspectorDOMAgent()
261 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
264 m_frontend = frontend;
265 m_instrumentingAgents->setInspectorDOMAgent(this);
268 void InspectorDOMAgent::clearFrontend()
272 m_instrumentingAgents->setInspectorDOMAgent(0);
276 Vector<Document*> InspectorDOMAgent::documents()
278 Vector<Document*> result;
279 for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->traverseNext()) {
280 Document* document = frame->document();
283 result.append(document);
288 void InspectorDOMAgent::reset()
291 searchCanceled(&error);
293 if (m_revalidateStyleAttrTask)
294 m_revalidateStyleAttrTask->reset();
297 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
299 m_domListener = listener;
302 void InspectorDOMAgent::setDocument(Document* doc)
304 if (doc == m_document.get())
312 if (doc->documentElement())
313 pushDocumentToFrontend();
315 m_frontend->setDocument(InspectorValue::null());
318 void InspectorDOMAgent::releaseDanglingNodes()
320 deleteAllValues(m_danglingNodeToIdMaps);
321 m_danglingNodeToIdMaps.clear();
324 long InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
326 long id = nodesMap->get(node);
330 nodesMap->set(node, id);
331 m_idToNode.set(id, node);
332 m_idToNodesMap.set(id, nodesMap);
336 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
338 if (node->isFrameOwnerElement()) {
339 const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
341 m_domListener->didRemoveDocument(frameOwner->contentDocument());
344 long id = nodesMap->get(node);
347 m_idToNode.remove(id);
348 nodesMap->remove(node);
349 bool childrenRequested = m_childrenRequested.contains(id);
350 if (childrenRequested) {
351 // Unbind subtree known to client recursively.
352 m_childrenRequested.remove(id);
353 Node* child = innerFirstChild(node);
355 unbind(child, nodesMap);
356 child = innerNextSibling(child);
361 bool InspectorDOMAgent::pushDocumentToFrontend()
365 if (!m_documentNodeToIdMap.contains(m_document))
366 m_frontend->setDocument(buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap));
370 void InspectorDOMAgent::pushChildNodesToFrontend(long nodeId)
372 Node* node = nodeForId(nodeId);
373 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
375 if (m_childrenRequested.contains(nodeId))
378 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
379 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, 1, nodeMap);
380 m_childrenRequested.add(nodeId);
381 m_frontend->setChildNodes(nodeId, children.release());
384 void InspectorDOMAgent::inspect(Node* node)
389 long id = pushNodePathToFrontend(node);
391 m_frontend->inspectElementRequested(id);
394 long InspectorDOMAgent::inspectedNode(unsigned long num)
396 if (num < m_inspectedNodes.size())
397 return m_inspectedNodes[num];
401 void InspectorDOMAgent::discardBindings()
403 m_documentNodeToIdMap.clear();
405 releaseDanglingNodes();
406 m_childrenRequested.clear();
407 m_inspectedNodes.clear();
410 Node* InspectorDOMAgent::nodeForId(long id)
415 HashMap<long, Node*>::iterator it = m_idToNode.find(id);
416 if (it != m_idToNode.end())
421 void InspectorDOMAgent::getChildNodes(ErrorString*, long nodeId)
423 pushChildNodesToFrontend(nodeId);
426 long InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
428 ASSERT(nodeToPush); // Invalid input
430 // If we are sending information to the client that is currently being created. Send root node first.
431 if (!pushDocumentToFrontend())
434 // Return id in case the node is known.
435 long result = m_documentNodeToIdMap.get(nodeToPush);
439 Node* node = nodeToPush;
441 NodeToIdMap* danglingMap = 0;
443 Node* parent = innerParentNode(node);
445 // Node being pushed is detached -> push subtree root.
446 danglingMap = new NodeToIdMap();
447 m_danglingNodeToIdMaps.append(danglingMap);
448 m_frontend->setDetachedRoot(buildObjectForNode(node, 0, danglingMap));
452 if (m_documentNodeToIdMap.get(parent))
459 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
460 for (int i = path.size() - 1; i >= 0; --i) {
461 long nodeId = map->get(path.at(i));
463 pushChildNodesToFrontend(nodeId);
465 return map->get(nodeToPush);
468 void InspectorDOMAgent::setAttribute(ErrorString*, long elementId, const String& name, const String& value, bool* success)
470 Node* node = nodeForId(elementId);
471 if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
472 Element* element = static_cast<Element*>(node);
473 ExceptionCode ec = 0;
474 element->setAttribute(name, value, ec);
479 void InspectorDOMAgent::removeAttribute(ErrorString* error, long elementId, const String& name, bool* success)
481 Node* node = nodeForId(elementId);
482 if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
483 Element* element = static_cast<Element*>(node);
484 ExceptionCode ec = 0;
485 element->removeAttribute(name, ec);
490 void InspectorDOMAgent::removeNode(ErrorString*, long nodeId, long* outNodeId)
492 Node* node = nodeForId(nodeId);
496 ContainerNode* parentNode = node->parentNode();
500 ExceptionCode ec = 0;
501 parentNode->removeChild(node, ec);
508 void InspectorDOMAgent::changeTagName(ErrorString*, long nodeId, const String& tagName, long* newId)
510 Node* oldNode = nodeForId(nodeId);
511 if (!oldNode || !oldNode->isElementNode())
514 ExceptionCode ec = 0;
515 RefPtr<Element> newElem = oldNode->document()->createElement(tagName, ec);
519 // Copy over the original node's attributes.
520 Element* oldElem = static_cast<Element*>(oldNode);
521 newElem->copyNonAttributeProperties(oldElem);
522 if (oldElem->attributes())
523 newElem->attributes()->setAttributes(*(oldElem->attributes(true)));
525 // Copy over the original node's children.
527 while ((child = oldNode->firstChild()))
528 newElem->appendChild(child, ec);
530 // Replace the old node with the new node
531 ContainerNode* parent = oldNode->parentNode();
532 parent->insertBefore(newElem, oldNode->nextSibling(), ec);
533 parent->removeChild(oldNode, ec);
538 *newId = pushNodePathToFrontend(newElem.get());
539 if (m_childrenRequested.contains(nodeId))
540 pushChildNodesToFrontend(*newId);
543 void InspectorDOMAgent::getOuterHTML(ErrorString*, long nodeId, WTF::String* outerHTML)
545 Node* node = nodeForId(nodeId);
546 if (!node || !node->isHTMLElement())
549 *outerHTML = toHTMLElement(node)->outerHTML();
552 void InspectorDOMAgent::setOuterHTML(ErrorString*, long nodeId, const String& outerHTML, long* newId)
554 Node* node = nodeForId(nodeId);
555 if (!node || !node->isHTMLElement())
558 bool requiresTotalUpdate = node->nodeName() == "HTML" || node->nodeName() == "BODY" || node->nodeName() == "HEAD";
560 bool childrenRequested = m_childrenRequested.contains(nodeId);
561 Node* previousSibling = node->previousSibling();
562 ContainerNode* parentNode = node->parentNode();
564 HTMLElement* htmlElement = toHTMLElement(node);
565 ExceptionCode ec = 0;
566 htmlElement->setOuterHTML(outerHTML, ec);
570 if (requiresTotalUpdate) {
571 RefPtr<Document> document = m_document;
573 setDocument(document.get());
578 Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
580 // The only child node has been deleted.
585 *newId = pushNodePathToFrontend(newNode);
586 if (childrenRequested)
587 pushChildNodesToFrontend(*newId);
590 void InspectorDOMAgent::setTextNodeValue(ErrorString*, long nodeId, const String& value, bool* success)
592 Node* node = nodeForId(nodeId);
593 if (node && (node->nodeType() == Node::TEXT_NODE)) {
594 Text* text_node = static_cast<Text*>(node);
595 ExceptionCode ec = 0;
596 text_node->replaceWholeText(value, ec);
601 void InspectorDOMAgent::getEventListenersForNode(ErrorString*, long nodeId, long* outNodeId, RefPtr<InspectorArray>* listenersArray)
603 Node* node = nodeForId(nodeId);
607 // Quick break if a null node or no listeners at all
608 if (!node || !(d = node->eventTargetData()))
611 // Get the list of event types this Node is concerned with
612 Vector<AtomicString> eventTypes;
613 const EventListenerMap& listenerMap = d->eventListenerMap;
614 EventListenerMap::const_iterator end = listenerMap.end();
615 for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
616 eventTypes.append(iter->first);
618 // Quick break if no useful listeners
619 size_t eventTypesLength = eventTypes.size();
620 if (!eventTypesLength)
623 // The Node's Event Ancestors (not including self)
624 Vector<EventContext> ancestors;
625 node->getEventAncestors(ancestors, node);
627 // Nodes and their Listeners for the concerned event types (order is top to bottom)
628 Vector<EventListenerInfo> eventInformation;
629 for (size_t i = ancestors.size(); i; --i) {
630 Node* ancestor = ancestors[i - 1].node();
631 for (size_t j = 0; j < eventTypesLength; ++j) {
632 AtomicString& type = eventTypes[j];
633 if (ancestor->hasEventListeners(type))
634 eventInformation.append(EventListenerInfo(static_cast<Node*>(ancestor), type, ancestor->getEventListeners(type)));
638 // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
639 for (size_t i = 0; i < eventTypesLength; ++i) {
640 const AtomicString& type = eventTypes[i];
641 eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
644 // Get Capturing Listeners (in this order)
645 size_t eventInformationLength = eventInformation.size();
646 for (size_t i = 0; i < eventInformationLength; ++i) {
647 const EventListenerInfo& info = eventInformation[i];
648 const EventListenerVector& vector = info.eventListenerVector;
649 for (size_t j = 0; j < vector.size(); ++j) {
650 const RegisteredEventListener& listener = vector[j];
651 if (listener.useCapture)
652 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
656 // Get Bubbling Listeners (reverse order)
657 for (size_t i = eventInformationLength; i; --i) {
658 const EventListenerInfo& info = eventInformation[i - 1];
659 const EventListenerVector& vector = info.eventListenerVector;
660 for (size_t j = 0; j < vector.size(); ++j) {
661 const RegisteredEventListener& listener = vector[j];
662 if (!listener.useCapture)
663 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
668 void InspectorDOMAgent::addInspectedNode(ErrorString*, long nodeId)
670 m_inspectedNodes.prepend(nodeId);
671 while (m_inspectedNodes.size() > 5)
672 m_inspectedNodes.removeLast();
675 void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, bool runSynchronously)
677 // FIXME: Few things are missing here:
678 // 1) Search works with node granularity - number of matches within node is not calculated.
679 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
682 unsigned queryLength = whitespaceTrimmedQuery.length();
683 bool startTagFound = !whitespaceTrimmedQuery.find('<');
684 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
686 String tagNameQuery = whitespaceTrimmedQuery;
687 if (startTagFound || endTagFound)
688 tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength);
689 if (!Document::isValidName(tagNameQuery))
692 String attributeNameQuery = whitespaceTrimmedQuery;
693 if (!Document::isValidName(attributeNameQuery))
694 attributeNameQuery = "";
696 String escapedQuery = whitespaceTrimmedQuery;
697 escapedQuery.replace("'", "\\'");
698 String escapedTagNameQuery = tagNameQuery;
699 escapedTagNameQuery.replace("'", "\\'");
701 // Clear pending jobs.
702 searchCanceled(error);
704 // Find all frames, iframes and object elements to search their documents.
705 Vector<Document*> docs = documents();
706 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
707 Document* document = *it;
709 if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
710 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
711 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
715 if (!tagNameQuery.isEmpty() && startTagFound) {
716 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
717 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
721 if (!tagNameQuery.isEmpty() && endTagFound) {
722 // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
723 // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
724 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
725 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
729 bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*";
730 if (matchesEveryNode) {
731 // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
732 // so limit the search functions list to plain text and attribute matching for these.
733 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
734 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
738 m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
739 m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
740 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
741 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
742 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
743 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
744 if (!tagNameQuery.isEmpty())
745 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
746 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
747 m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
750 if (runSynchronously) {
752 ListHashSet<Node*> resultCollector;
753 for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it)
754 (*it)->match(resultCollector);
755 reportNodesAsSearchResults(resultCollector);
756 searchCanceled(error);
759 m_matchJobsTimer.startOneShot(0);
762 void InspectorDOMAgent::searchCanceled(ErrorString*)
764 if (m_matchJobsTimer.isActive())
765 m_matchJobsTimer.stop();
766 deleteAllValues(m_pendingMatchJobs);
767 m_pendingMatchJobs.clear();
768 m_searchResults.clear();
771 void InspectorDOMAgent::resolveNode(ErrorString*, long nodeId, RefPtr<InspectorValue>* result)
773 InjectedScript injectedScript = injectedScriptForNodeId(nodeId);
774 if (!injectedScript.hasNoValue())
775 injectedScript.resolveNode(nodeId, result);
778 void InspectorDOMAgent::getNodeProperties(ErrorString*, long nodeId, PassRefPtr<InspectorArray> propertiesArray, RefPtr<InspectorValue>* result)
780 InjectedScript injectedScript = injectedScriptForNodeId(nodeId);
781 if (!injectedScript.hasNoValue())
782 injectedScript.getNodeProperties(nodeId, propertiesArray, result);
785 void InspectorDOMAgent::getNodePrototypes(ErrorString*, long nodeId, RefPtr<InspectorValue>* result)
787 InjectedScript injectedScript = injectedScriptForNodeId(nodeId);
788 if (!injectedScript.hasNoValue())
789 injectedScript.getNodePrototypes(nodeId, result);
792 void InspectorDOMAgent::pushNodeToFrontend(ErrorString*, PassRefPtr<InspectorObject> objectId, long* nodeId)
794 InjectedScript injectedScript = m_injectedScriptHost->injectedScriptForObjectId(objectId.get());
795 Node* node = injectedScript.nodeForObjectId(objectId);
797 *nodeId = pushNodePathToFrontend(node);
802 String InspectorDOMAgent::documentURLString(Document* document) const
804 if (!document || document->url().isNull())
806 return document->url().string();
809 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
811 RefPtr<InspectorObject> value = InspectorObject::create();
813 long id = bind(node, nodesMap);
818 switch (node->nodeType()) {
819 case Node::TEXT_NODE:
820 case Node::COMMENT_NODE:
821 case Node::CDATA_SECTION_NODE:
822 nodeValue = node->nodeValue();
824 case Node::ATTRIBUTE_NODE:
825 localName = node->localName();
827 case Node::DOCUMENT_FRAGMENT_NODE:
829 case Node::DOCUMENT_NODE:
830 case Node::ELEMENT_NODE:
832 nodeName = node->nodeName();
833 localName = node->localName();
837 value->setNumber("id", id);
838 value->setNumber("nodeType", node->nodeType());
839 value->setString("nodeName", nodeName);
840 value->setString("localName", localName);
841 value->setString("nodeValue", nodeValue);
843 if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
844 int nodeCount = innerChildNodeCount(node);
845 value->setNumber("childNodeCount", nodeCount);
846 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap);
847 if (children->length() > 0)
848 value->setArray("children", children.release());
850 if (node->nodeType() == Node::ELEMENT_NODE) {
851 Element* element = static_cast<Element*>(node);
852 value->setArray("attributes", buildArrayForElementAttributes(element));
853 if (node->isFrameOwnerElement()) {
854 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
855 value->setString("documentURL", documentURLString(frameOwner->contentDocument()));
857 } else if (node->nodeType() == Node::DOCUMENT_NODE) {
858 Document* document = static_cast<Document*>(node);
859 value->setString("documentURL", documentURLString(document));
861 } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
862 DocumentType* docType = static_cast<DocumentType*>(node);
863 value->setString("publicId", docType->publicId());
864 value->setString("systemId", docType->systemId());
865 value->setString("internalSubset", docType->internalSubset());
866 } else if (node->nodeType() == Node::ATTRIBUTE_NODE) {
867 Attr* attribute = static_cast<Attr*>(node);
868 value->setString("name", attribute->name());
869 value->setString("value", attribute->value());
871 return value.release();
874 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
876 RefPtr<InspectorArray> attributesValue = InspectorArray::create();
877 // Go through all attributes and serialize them.
878 const NamedNodeMap* attrMap = element->attributes(true);
880 return attributesValue.release();
881 unsigned numAttrs = attrMap->length();
882 for (unsigned i = 0; i < numAttrs; ++i) {
883 // Add attribute pair
884 const Attribute *attribute = attrMap->attributeItem(i);
885 attributesValue->pushString(attribute->name().toString());
886 attributesValue->pushString(attribute->value());
888 return attributesValue.release();
891 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
893 RefPtr<InspectorArray> children = InspectorArray::create();
894 Node* child = innerFirstChild(container);
897 // Special case the_only text child.
898 if (child && child->nodeType() == Node::TEXT_NODE && !innerNextSibling(child))
899 children->pushObject(buildObjectForNode(child, 0, nodesMap));
900 return children.release();
901 } else if (depth > 0) {
906 children->pushObject(buildObjectForNode(child, depth, nodesMap));
907 child = innerNextSibling(child);
909 return children.release();
912 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
914 RefPtr<EventListener> eventListener = registeredEventListener.listener;
915 RefPtr<InspectorObject> value = InspectorObject::create();
916 value->setString("type", eventType);
917 value->setBoolean("useCapture", registeredEventListener.useCapture);
918 value->setBoolean("isAttribute", eventListener->isAttribute());
919 value->setNumber("nodeId", pushNodePathToFrontend(node));
920 value->setString("listenerBody", eventListenerHandlerBody(node->document(), eventListener.get()));
923 if (eventListenerHandlerLocation(node->document(), eventListener.get(), sourceName, lineNumber)) {
924 value->setString("sourceName", sourceName);
925 value->setNumber("lineNumber", lineNumber);
927 return value.release();
930 Node* InspectorDOMAgent::innerFirstChild(Node* node)
932 if (node->isFrameOwnerElement()) {
933 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
934 Document* doc = frameOwner->contentDocument();
936 return doc->firstChild();
938 node = node->firstChild();
939 while (isWhitespace(node))
940 node = node->nextSibling();
944 Node* InspectorDOMAgent::innerNextSibling(Node* node)
947 node = node->nextSibling();
948 } while (isWhitespace(node));
952 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
955 node = node->previousSibling();
956 } while (isWhitespace(node));
960 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
963 Node* child = innerFirstChild(node);
966 child = innerNextSibling(child);
971 Node* InspectorDOMAgent::innerParentNode(Node* node)
973 ContainerNode* parent = node->parentNode();
974 if (parent && parent->isDocumentNode())
975 return static_cast<Document*>(parent)->ownerElement();
979 bool InspectorDOMAgent::isWhitespace(Node* node)
981 //TODO: pull ignoreWhitespace setting from the frontend and use here.
982 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
985 void InspectorDOMAgent::mainFrameDOMContentLoaded()
987 // Re-push document once it is loaded.
989 pushDocumentToFrontend();
992 void InspectorDOMAgent::loadEventFired(Document* document)
994 Element* frameOwner = document->ownerElement();
998 long frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1002 if (!m_childrenRequested.contains(frameOwnerId)) {
1003 // No children are mapped yet -> only notify on changes of hasChildren.
1004 m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(frameOwner));
1006 // Re-add frame owner element together with its new children.
1007 long parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1008 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1009 RefPtr<InspectorObject> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1010 Node* previousSibling = innerPreviousSibling(frameOwner);
1011 long prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1012 m_frontend->childNodeInserted(parentId, prevId, value.release());
1013 // Invalidate children requested flag for the element.
1014 m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId));
1018 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1020 if (isWhitespace(node))
1023 // We could be attaching existing subtree. Forget the bindings.
1024 unbind(node, &m_documentNodeToIdMap);
1026 ContainerNode* parent = node->parentNode();
1027 long parentId = m_documentNodeToIdMap.get(parent);
1028 // Return if parent is not mapped yet.
1032 if (!m_childrenRequested.contains(parentId)) {
1033 // No children are mapped yet -> only notify on changes of hasChildren.
1034 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1036 // Children have been requested -> return value of a new child.
1037 Node* prevSibling = innerPreviousSibling(node);
1038 long prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1039 RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1040 m_frontend->childNodeInserted(parentId, prevId, value.release());
1044 void InspectorDOMAgent::didRemoveDOMNode(Node* node)
1046 if (isWhitespace(node))
1049 ContainerNode* parent = node->parentNode();
1050 long parentId = m_documentNodeToIdMap.get(parent);
1051 // If parent is not mapped yet -> ignore the event.
1056 m_domListener->didRemoveDOMNode(node);
1058 if (!m_childrenRequested.contains(parentId)) {
1059 // No children are mapped yet -> only notify on changes of hasChildren.
1060 if (innerChildNodeCount(parent) == 1)
1061 m_frontend->childNodeCountUpdated(parentId, 0);
1063 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1064 unbind(node, &m_documentNodeToIdMap);
1067 void InspectorDOMAgent::didModifyDOMAttr(Element* element)
1069 long id = m_documentNodeToIdMap.get(element);
1070 // If node is not mapped yet -> ignore the event.
1075 m_domListener->didModifyDOMAttr(element);
1077 m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
1080 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1082 long id = m_documentNodeToIdMap.get(characterData);
1085 m_frontend->characterDataModified(id, characterData->data());
1088 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1090 long id = m_documentNodeToIdMap.get(node);
1091 // If node is not mapped yet -> ignore the event.
1095 if (!m_revalidateStyleAttrTask)
1096 m_revalidateStyleAttrTask = new RevalidateStyleAttributeTask(this);
1097 m_revalidateStyleAttrTask->scheduleFor(static_cast<Element*>(node));
1100 Node* InspectorDOMAgent::nodeForPath(const String& path)
1102 // The path is of form "1,HTML,2,BODY,1,DIV"
1106 Node* node = m_document.get();
1107 Vector<String> pathTokens;
1108 path.split(",", false, pathTokens);
1109 if (!pathTokens.size())
1111 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1112 bool success = true;
1113 unsigned childNumber = pathTokens[i].toUInt(&success);
1116 if (childNumber >= innerChildNodeCount(node))
1119 Node* child = innerFirstChild(node);
1120 String childName = pathTokens[i + 1];
1121 for (size_t j = 0; child && j < childNumber; ++j)
1122 child = innerNextSibling(child);
1124 if (!child || child->nodeName() != childName)
1131 PassRefPtr<InspectorArray> InspectorDOMAgent::toArray(const Vector<String>& data)
1133 RefPtr<InspectorArray> result = InspectorArray::create();
1134 for (unsigned i = 0; i < data.size(); ++i)
1135 result->pushString(data[i]);
1136 return result.release();
1139 void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*)
1141 if (!m_pendingMatchJobs.size()) {
1143 searchCanceled(&error);
1147 ListHashSet<Node*> resultCollector;
1148 MatchJob* job = m_pendingMatchJobs.takeFirst();
1149 job->match(resultCollector);
1152 reportNodesAsSearchResults(resultCollector);
1154 m_matchJobsTimer.startOneShot(0.025);
1157 void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector)
1159 RefPtr<InspectorArray> nodeIds = InspectorArray::create();
1160 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) {
1161 if (m_searchResults.contains(*it))
1163 m_searchResults.add(*it);
1164 nodeIds->pushNumber(static_cast<long long>(pushNodePathToFrontend(*it)));
1166 m_frontend->addNodesToSearchResult(nodeIds.release());
1169 void InspectorDOMAgent::copyNode(ErrorString*, long nodeId)
1171 Node* node = nodeForId(nodeId);
1174 String markup = createMarkup(node);
1175 Pasteboard::generalPasteboard()->writePlainText(markup);
1178 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString*, const String& path, long* nodeId)
1180 if (Node* node = nodeForPath(path))
1181 *nodeId = pushNodePathToFrontend(node);
1184 InjectedScript InspectorDOMAgent::injectedScriptForNodeId(long nodeId)
1188 Node* node = nodeForId(nodeId);
1190 Document* document = node->ownerDocument();
1192 frame = document->frame();
1195 frame = m_document->frame();
1198 return m_injectedScriptHost->injectedScriptFor(mainWorldScriptState(frame));
1200 return InjectedScript();
1204 } // namespace WebCore
1206 #endif // ENABLE(INSPECTOR)