2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 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 "core/inspector/InspectorDOMAgent.h"
34 #include "HTMLNames.h"
35 #include "bindings/v8/ExceptionState.h"
36 #include "bindings/v8/ScriptEventListener.h"
37 #include "core/dom/Attr.h"
38 #include "core/dom/CharacterData.h"
39 #include "core/dom/ContainerNode.h"
40 #include "core/dom/DOMException.h"
41 #include "core/dom/Document.h"
42 #include "core/dom/DocumentFragment.h"
43 #include "core/dom/DocumentType.h"
44 #include "core/dom/Element.h"
45 #include "core/events/EventListener.h"
46 #include "core/events/EventTarget.h"
47 #include "core/dom/Node.h"
48 #include "core/dom/NodeList.h"
49 #include "core/dom/NodeTraversal.h"
50 #include "core/dom/PseudoElement.h"
51 #include "core/dom/Text.h"
52 #include "core/dom/shadow/ElementShadow.h"
53 #include "core/dom/shadow/ShadowRoot.h"
54 #include "core/editing/markup.h"
55 #include "core/fileapi/File.h"
56 #include "core/fileapi/FileList.h"
57 #include "core/html/HTMLFrameOwnerElement.h"
58 #include "core/html/HTMLImportChild.h"
59 #include "core/html/HTMLInputElement.h"
60 #include "core/html/HTMLLinkElement.h"
61 #include "core/html/HTMLTemplateElement.h"
62 #include "core/inspector/DOMEditor.h"
63 #include "core/inspector/DOMPatchSupport.h"
64 #include "core/inspector/IdentifiersFactory.h"
65 #include "core/inspector/InspectorHistory.h"
66 #include "core/inspector/InspectorOverlay.h"
67 #include "core/inspector/InspectorPageAgent.h"
68 #include "core/inspector/InspectorState.h"
69 #include "core/inspector/InstrumentingAgents.h"
70 #include "core/loader/DocumentLoader.h"
71 #include "core/frame/Frame.h"
72 #include "core/page/FrameTree.h"
73 #include "core/page/Page.h"
74 #include "core/rendering/HitTestResult.h"
75 #include "core/rendering/RenderView.h"
76 #include "core/xml/DocumentXPathEvaluator.h"
77 #include "core/xml/XPathResult.h"
78 #include "platform/PlatformGestureEvent.h"
79 #include "platform/PlatformMouseEvent.h"
80 #include "platform/PlatformTouchEvent.h"
81 #include "wtf/ListHashSet.h"
82 #include "wtf/text/CString.h"
83 #include "wtf/text/WTFString.h"
87 using namespace HTMLNames;
89 namespace DOMAgentState {
90 static const char documentRequested[] = "documentRequested";
93 static const size_t maxTextSize = 10000;
94 static const UChar ellipsisUChar[] = { 0x2026, 0 };
96 static Color parseColor(const RefPtr<JSONObject>* colorObject)
98 if (!colorObject || !(*colorObject))
99 return Color::transparent;
104 bool success = (*colorObject)->getNumber("r", &r);
105 success |= (*colorObject)->getNumber("g", &g);
106 success |= (*colorObject)->getNumber("b", &b);
108 return Color::transparent;
111 success = (*colorObject)->getNumber("a", &a);
113 return Color(r, g, b);
115 // Clamp alpha to the [0..1] range.
121 return Color(r, g, b, static_cast<int>(a * 255));
124 static Color parseConfigColor(const String& fieldName, JSONObject* configObject)
126 const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName);
127 return parseColor(&colorObject);
130 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad)
134 const size_t coordinatesInQuad = 8;
135 double coordinates[coordinatesInQuad];
136 if (quadArray->length() != coordinatesInQuad)
138 for (size_t i = 0; i < coordinatesInQuad; ++i) {
139 if (!quadArray->get(i)->asNumber(coordinates + i))
142 quad->setP1(FloatPoint(coordinates[0], coordinates[1]));
143 quad->setP2(FloatPoint(coordinates[2], coordinates[3]));
144 quad->setP3(FloatPoint(coordinates[4], coordinates[5]));
145 quad->setP4(FloatPoint(coordinates[6], coordinates[7]));
150 static Node* hoveredNodeForPoint(Frame* frame, const IntPoint& point, bool ignorePointerEventsNone)
152 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent;
153 if (ignorePointerEventsNone)
154 hitType |= HitTestRequest::IgnorePointerEventsNone;
155 HitTestRequest request(hitType);
156 HitTestResult result(frame->view()->windowToContents(point));
157 frame->contentRenderer()->hitTest(request, result);
158 Node* node = result.innerPossiblyPseudoNode();
159 while (node && node->nodeType() == Node::TEXT_NODE)
160 node = node->parentNode();
164 static Node* hoveredNodeForEvent(Frame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone)
166 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
169 static Node* hoveredNodeForEvent(Frame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone)
171 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
174 static Node* hoveredNodeForEvent(Frame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone)
176 const Vector<PlatformTouchPoint>& points = event.touchPoints();
179 return hoveredNodeForPoint(frame, points[0].pos(), ignorePointerEventsNone);
182 class RevalidateStyleAttributeTask {
183 WTF_MAKE_FAST_ALLOCATED;
185 RevalidateStyleAttributeTask(InspectorDOMAgent*);
186 void scheduleFor(Element*);
187 void reset() { m_timer.stop(); }
188 void onTimer(Timer<RevalidateStyleAttributeTask>*);
191 InspectorDOMAgent* m_domAgent;
192 Timer<RevalidateStyleAttributeTask> m_timer;
193 HashSet<RefPtr<Element> > m_elements;
196 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
197 : m_domAgent(domAgent)
198 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
202 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
204 m_elements.add(element);
205 if (!m_timer.isActive())
206 m_timer.startOneShot(0);
209 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
211 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
212 Vector<Element*> elements;
213 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
214 elements.append(it->get());
215 m_domAgent->styleAttributeInvalidated(elements);
220 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState)
222 if (exceptionState.hadException())
223 return DOMException::getErrorName(exceptionState.code());
227 InspectorDOMAgent::InspectorDOMAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
228 : InspectorBaseAgent<InspectorDOMAgent>("DOM")
229 , m_pageAgent(pageAgent)
230 , m_injectedScriptManager(injectedScriptManager)
235 , m_lastBackendNodeId(-1)
236 , m_searchingForNode(NotSearching)
237 , m_suppressAttributeModifiedEvent(false)
241 InspectorDOMAgent::~InspectorDOMAgent()
244 ASSERT(m_searchingForNode == NotSearching);
247 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
250 m_history = adoptPtr(new InspectorHistory());
251 m_domEditor = adoptPtr(new DOMEditor(m_history.get()));
253 m_frontend = frontend->dom();
254 m_instrumentingAgents->setInspectorDOMAgent(this);
255 m_document = m_pageAgent->mainFrame()->document();
258 void InspectorDOMAgent::clearFrontend()
266 setSearchingForNode(&error, NotSearching, 0);
267 hideHighlight(&error);
270 m_instrumentingAgents->setInspectorDOMAgent(0);
271 m_state->setBoolean(DOMAgentState::documentRequested, false);
275 void InspectorDOMAgent::restore()
277 // Reset document to avoid early return from setDocument.
279 setDocument(m_pageAgent->mainFrame()->document());
282 Vector<Document*> InspectorDOMAgent::documents()
284 Vector<Document*> result;
285 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
286 Document* document = frame->document();
289 result.append(document);
294 void InspectorDOMAgent::reset()
296 discardFrontendBindings();
297 discardBackendBindings();
301 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
303 m_domListener = listener;
306 void InspectorDOMAgent::setDocument(Document* doc)
308 if (doc == m_document.get())
315 if (!m_state->getBoolean(DOMAgentState::documentRequested))
318 // Immediately communicate 0 document or document that has finished loading.
319 if (!doc || !doc->parsing())
320 m_frontend->documentUpdated();
323 void InspectorDOMAgent::releaseDanglingNodes()
325 m_danglingNodeToIdMaps.clear();
328 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
330 int id = nodesMap->get(node);
334 nodesMap->set(node, id);
335 m_idToNode.set(id, node);
336 m_idToNodesMap.set(id, nodesMap);
340 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
342 int id = nodesMap->get(node);
346 m_idToNode.remove(id);
348 if (node->isFrameOwnerElement()) {
349 Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument();
351 m_domListener->didRemoveDocument(contentDocument);
353 unbind(contentDocument, nodesMap);
356 for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot())
357 unbind(root, nodesMap);
359 if (node->isElementNode()) {
360 Element* element = toElement(node);
361 if (element->pseudoElement(BEFORE))
362 unbind(element->pseudoElement(BEFORE), nodesMap);
363 if (element->pseudoElement(AFTER))
364 unbind(element->pseudoElement(AFTER), nodesMap);
366 if (element->hasTagName(HTMLNames::linkTag)) {
367 HTMLLinkElement* linkElement = toHTMLLinkElement(element);
368 if (linkElement->isImport() && linkElement->import())
369 unbind(linkElement->import(), nodesMap);
373 nodesMap->remove(node);
375 m_domListener->didRemoveDOMNode(node);
377 bool childrenRequested = m_childrenRequested.contains(id);
378 if (childrenRequested) {
379 // Unbind subtree known to client recursively.
380 m_childrenRequested.remove(id);
381 Node* child = innerFirstChild(node);
383 unbind(child, nodesMap);
384 child = innerNextSibling(child);
389 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
391 Node* node = nodeForId(nodeId);
393 *errorString = "Could not find node with given id";
399 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId)
401 Node* node = assertNode(errorString, nodeId);
405 if (!(node->isDocumentNode())) {
406 *errorString = "Document is not available";
409 return toDocument(node);
412 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
414 Node* node = assertNode(errorString, nodeId);
418 if (node->nodeType() != Node::ELEMENT_NODE) {
419 *errorString = "Node is not an Element";
422 return toElement(node);
425 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId)
427 Node* node = assertNode(errorString, nodeId);
431 if (node->isInShadowTree()) {
432 *errorString = "Cannot edit nodes from shadow trees";
436 if (node->isPseudoElement()) {
437 *errorString = "Cannot edit pseudo elements";
444 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId)
446 Element* element = assertElement(errorString, nodeId);
450 if (element->isInShadowTree()) {
451 *errorString = "Cannot edit elements from shadow trees";
455 if (element->isPseudoElement()) {
456 *errorString = "Cannot edit pseudo elements";
463 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root)
465 m_state->setBoolean(DOMAgentState::documentRequested, true);
468 *errorString = "Document is not available";
472 discardFrontendBindings();
474 root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
477 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
479 Node* node = nodeForId(nodeId);
480 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
483 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
485 if (m_childrenRequested.contains(nodeId)) {
491 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
492 int childNodeId = nodeMap->get(node);
494 pushChildNodesToFrontend(childNodeId, depth);
500 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap);
501 m_frontend->setChildNodes(nodeId, children.release());
504 void InspectorDOMAgent::discardFrontendBindings()
508 m_searchResults.clear();
509 m_documentNodeToIdMap.clear();
511 releaseDanglingNodes();
512 m_childrenRequested.clear();
513 if (m_revalidateStyleAttrTask)
514 m_revalidateStyleAttrTask->reset();
517 void InspectorDOMAgent::discardBackendBindings()
519 m_backendIdToNode.clear();
520 m_nodeGroupToBackendIdMap.clear();
523 int InspectorDOMAgent::pushNodeToFrontend(ErrorString* errorString, int documentNodeId, Node* nodeToPush)
525 Document* document = assertDocument(errorString, documentNodeId);
528 if (nodeToPush->document() != document) {
529 *errorString = "Node is not part of the document with given id";
533 return pushNodePathToFrontend(nodeToPush);
536 Node* InspectorDOMAgent::nodeForId(int id)
541 HashMap<int, Node*>::iterator it = m_idToNode.find(id);
542 if (it != m_idToNode.end())
547 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth)
553 else if (*depth == -1)
554 sanitizedDepth = INT_MAX;
556 sanitizedDepth = *depth;
558 *errorString = "Please provide a positive integer as a depth or -1 for entire subtree";
562 pushChildNodesToFrontend(nodeId, sanitizedDepth);
565 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
568 Node* node = assertNode(errorString, nodeId);
569 if (!node || !node->isContainerNode())
572 TrackExceptionState exceptionState;
573 RefPtr<Element> element = toContainerNode(node)->querySelector(AtomicString(selectors), exceptionState);
574 if (exceptionState.hadException()) {
575 *errorString = "DOM Error while querying";
580 *elementId = pushNodePathToFrontend(element.get());
583 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result)
585 Node* node = assertNode(errorString, nodeId);
586 if (!node || !node->isContainerNode())
589 TrackExceptionState exceptionState;
590 RefPtr<NodeList> nodes = toContainerNode(node)->querySelectorAll(AtomicString(selectors), exceptionState);
591 if (exceptionState.hadException()) {
592 *errorString = "DOM Error while querying";
596 result = TypeBuilder::Array<int>::create();
598 for (unsigned i = 0; i < nodes->length(); ++i)
599 result->addItem(pushNodePathToFrontend(nodes->item(i)));
602 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
604 ASSERT(nodeToPush); // Invalid input
608 if (!m_documentNodeToIdMap.contains(m_document))
611 // Return id in case the node is known.
612 int result = m_documentNodeToIdMap.get(nodeToPush);
616 Node* node = nodeToPush;
618 NodeToIdMap* danglingMap = 0;
621 Node* parent = innerParentNode(node);
623 // Node being pushed is detached -> push subtree root.
624 OwnPtr<NodeToIdMap> newMap = adoptPtr(new NodeToIdMap);
625 danglingMap = newMap.get();
626 m_danglingNodeToIdMaps.append(newMap.release());
627 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
628 children->addItem(buildObjectForNode(node, 0, danglingMap));
629 m_frontend->setChildNodes(0, children);
633 if (m_documentNodeToIdMap.get(parent))
640 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
641 for (int i = path.size() - 1; i >= 0; --i) {
642 int nodeId = map->get(path.at(i));
644 pushChildNodesToFrontend(nodeId);
646 return map->get(nodeToPush);
649 int InspectorDOMAgent::boundNodeId(Node* node)
651 return m_documentNodeToIdMap.get(node);
654 BackendNodeId InspectorDOMAgent::backendNodeIdForNode(Node* node, const String& nodeGroup)
659 if (!m_nodeGroupToBackendIdMap.contains(nodeGroup))
660 m_nodeGroupToBackendIdMap.set(nodeGroup, NodeToBackendIdMap());
662 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value;
663 BackendNodeId id = map.get(node);
665 id = --m_lastBackendNodeId;
667 m_backendIdToNode.set(id, std::make_pair(node, nodeGroup));
673 void InspectorDOMAgent::releaseBackendNodeIds(ErrorString* errorString, const String& nodeGroup)
675 if (m_nodeGroupToBackendIdMap.contains(nodeGroup)) {
676 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value;
677 for (NodeToBackendIdMap::iterator it = map.begin(); it != map.end(); ++it)
678 m_backendIdToNode.remove(it->value);
679 m_nodeGroupToBackendIdMap.remove(nodeGroup);
682 *errorString = "Group name not found";
685 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value)
687 Element* element = assertEditableElement(errorString, elementId);
691 m_domEditor->setAttribute(element, name, value, errorString);
694 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
696 Element* element = assertEditableElement(errorString, elementId);
700 String markup = "<span " + text + "></span>";
701 RefPtr<DocumentFragment> fragment = element->document().createDocumentFragment();
703 bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement();
704 // Not all elements can represent the context (i.e. IFRAME), hence using document.body.
705 if (shouldIgnoreCase && element->document().body())
706 fragment->parseHTML(markup, element->document().body(), AllowScriptingContent);
708 fragment->parseXML(markup, 0, AllowScriptingContent);
710 Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0;
711 if (!parsedElement) {
712 *errorString = "Could not parse value as attributes";
716 String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String();
718 if (!parsedElement->hasAttributes() && name) {
719 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
723 bool foundOriginalAttribute = false;
724 unsigned numAttrs = parsedElement->attributeCount();
725 for (unsigned i = 0; i < numAttrs; ++i) {
726 // Add attribute pair
727 const Attribute* attribute = parsedElement->attributeItem(i);
728 String attributeName = attribute->name().toString();
729 if (shouldIgnoreCase)
730 attributeName = attributeName.lower();
731 foundOriginalAttribute |= name && attributeName == caseAdjustedName;
732 if (!m_domEditor->setAttribute(element, attributeName, attribute->value(), errorString))
736 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
737 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
740 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
742 Element* element = assertEditableElement(errorString, elementId);
746 m_domEditor->removeAttribute(element, name, errorString);
749 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
751 Node* node = assertEditableNode(errorString, nodeId);
755 ContainerNode* parentNode = node->parentNode();
757 *errorString = "Cannot remove detached node";
761 m_domEditor->removeChild(parentNode, node, errorString);
764 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
768 Node* oldNode = nodeForId(nodeId);
769 if (!oldNode || !oldNode->isElementNode())
772 TrackExceptionState exceptionState;
773 RefPtr<Element> newElem = oldNode->document().createElement(AtomicString(tagName), exceptionState);
774 if (exceptionState.hadException())
777 // Copy over the original node's attributes.
778 newElem->cloneAttributesFromElement(*toElement(oldNode));
780 // Copy over the original node's children.
782 while ((child = oldNode->firstChild())) {
783 if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
787 // Replace the old node with the new node
788 ContainerNode* parent = oldNode->parentNode();
789 if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
791 if (!m_domEditor->removeChild(parent, oldNode, errorString))
794 *newId = pushNodePathToFrontend(newElem.get());
795 if (m_childrenRequested.contains(nodeId))
796 pushChildNodesToFrontend(*newId);
799 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
801 Node* node = assertNode(errorString, nodeId);
805 *outerHTML = createMarkup(node);
808 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
812 DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get());
813 domPatchSupport.patchDocument(outerHTML);
817 Node* node = assertEditableNode(errorString, nodeId);
821 Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument();
822 if (!document || (!document->isHTMLDocument() && !document->isXMLDocument())) {
823 *errorString = "Not an HTML/XML document";
828 if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString))
832 // The only child node has been deleted.
836 int newId = pushNodePathToFrontend(newNode);
838 bool childrenRequested = m_childrenRequested.contains(nodeId);
839 if (childrenRequested)
840 pushChildNodesToFrontend(newId);
843 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
845 Node* node = assertEditableNode(errorString, nodeId);
849 if (node->nodeType() != Node::TEXT_NODE) {
850 *errorString = "Can only set value of text nodes";
854 m_domEditor->replaceWholeText(toText(node), value, errorString);
857 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
859 listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
860 Node* node = assertNode(errorString, nodeId);
863 Vector<EventListenerInfo> eventInformation;
864 getEventListeners(node, eventInformation, true);
866 // Get Capturing Listeners (in this order)
867 size_t eventInformationLength = eventInformation.size();
868 for (size_t i = 0; i < eventInformationLength; ++i) {
869 const EventListenerInfo& info = eventInformation[i];
870 const EventListenerVector& vector = info.eventListenerVector;
871 for (size_t j = 0; j < vector.size(); ++j) {
872 const RegisteredEventListener& listener = vector[j];
873 if (listener.useCapture) {
874 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
876 listenersArray->addItem(listenerObject);
881 // Get Bubbling Listeners (reverse order)
882 for (size_t i = eventInformationLength; i; --i) {
883 const EventListenerInfo& info = eventInformation[i - 1];
884 const EventListenerVector& vector = info.eventListenerVector;
885 for (size_t j = 0; j < vector.size(); ++j) {
886 const RegisteredEventListener& listener = vector[j];
887 if (!listener.useCapture) {
888 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
890 listenersArray->addItem(listenerObject);
896 void InspectorDOMAgent::getEventListeners(EventTarget* target, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
898 // The Node's Ancestors including self.
899 Vector<EventTarget*> ancestors;
900 ancestors.append(target);
901 if (includeAncestors) {
902 Node* node = target->toNode();
903 for (ContainerNode* ancestor = node ? node->parentOrShadowHostNode() : 0; ancestor; ancestor = ancestor->parentOrShadowHostNode())
904 ancestors.append(ancestor);
907 // Nodes and their Listeners for the concerned event types (order is top to bottom)
908 for (size_t i = ancestors.size(); i; --i) {
909 EventTarget* ancestor = ancestors[i - 1];
910 Vector<AtomicString> eventTypes = ancestor->eventTypes();
911 for (size_t j = 0; j < eventTypes.size(); ++j) {
912 AtomicString& type = eventTypes[j];
913 const EventListenerVector& listeners = ancestor->getEventListeners(type);
914 EventListenerVector filteredListeners;
915 filteredListeners.reserveCapacity(listeners.size());
916 for (size_t k = 0; k < listeners.size(); ++k) {
917 if (listeners[k].listener->type() == EventListener::JSEventListenerType)
918 filteredListeners.append(listeners[k]);
920 if (!filteredListeners.isEmpty())
921 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners));
926 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount)
928 // FIXME: Few things are missing here:
929 // 1) Search works with node granularity - number of matches within node is not calculated.
930 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
933 unsigned queryLength = whitespaceTrimmedQuery.length();
934 bool startTagFound = !whitespaceTrimmedQuery.find('<');
935 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
936 bool startQuoteFound = !whitespaceTrimmedQuery.find('"');
937 bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength;
938 bool exactAttributeMatch = startQuoteFound && endQuoteFound;
940 String tagNameQuery = whitespaceTrimmedQuery;
941 String attributeQuery = whitespaceTrimmedQuery;
943 tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
945 tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
947 attributeQuery = attributeQuery.right(attributeQuery.length() - 1);
949 attributeQuery = attributeQuery.left(attributeQuery.length() - 1);
951 Vector<Document*> docs = documents();
952 ListHashSet<Node*> resultCollector;
954 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
955 Document* document = *it;
956 Node* node = document->documentElement();
960 // Manual plain text search.
961 while ((node = NodeTraversal::next(*node, document->documentElement()))) {
962 switch (node->nodeType()) {
963 case Node::TEXT_NODE:
964 case Node::COMMENT_NODE:
965 case Node::CDATA_SECTION_NODE: {
966 String text = node->nodeValue();
967 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound)
968 resultCollector.add(node);
971 case Node::ELEMENT_NODE: {
972 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound))
973 || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery))
974 || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false))
975 || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) {
976 resultCollector.add(node);
979 // Go through all attributes and serialize them.
980 const Element* element = toElement(node);
981 if (!element->hasAttributes())
984 unsigned numAttrs = element->attributeCount();
985 for (unsigned i = 0; i < numAttrs; ++i) {
986 // Add attribute pair
987 const Attribute* attribute = element->attributeItem(i);
988 if (attribute->localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) {
989 resultCollector.add(node);
992 size_t foundPosition = attribute->value().find(attributeQuery, 0, false);
993 if (foundPosition != kNotFound) {
994 if (!exactAttributeMatch || (!foundPosition && attribute->value().length() == attributeQuery.length())) {
995 resultCollector.add(node);
1008 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
1009 Document* document = *it;
1010 TrackExceptionState exceptionState;
1011 RefPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(document, whitespaceTrimmedQuery, document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState);
1012 if (exceptionState.hadException() || !result)
1015 unsigned long size = result->snapshotLength(exceptionState);
1016 for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) {
1017 Node* node = result->snapshotItem(i, exceptionState);
1018 if (exceptionState.hadException())
1021 if (node->nodeType() == Node::ATTRIBUTE_NODE)
1022 node = toAttr(node)->ownerElement();
1023 resultCollector.add(node);
1027 // Selector evaluation
1028 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
1029 Document* document = *it;
1030 TrackExceptionState exceptionState;
1031 RefPtr<NodeList> nodeList = document->querySelectorAll(AtomicString(whitespaceTrimmedQuery), exceptionState);
1032 if (exceptionState.hadException() || !nodeList)
1035 unsigned size = nodeList->length();
1036 for (unsigned i = 0; i < size; ++i)
1037 resultCollector.add(nodeList->item(i));
1041 *searchId = IdentifiersFactory::createIdentifier();
1042 Vector<RefPtr<Node> >* resultsIt = &m_searchResults.add(*searchId, Vector<RefPtr<Node> >()).storedValue->value;
1044 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
1045 resultsIt->append(*it);
1047 *resultCount = resultsIt->size();
1050 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds)
1052 SearchResults::iterator it = m_searchResults.find(searchId);
1053 if (it == m_searchResults.end()) {
1054 *errorString = "No search session with given id found";
1058 int size = it->value.size();
1059 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1060 *errorString = "Invalid search result range";
1064 nodeIds = TypeBuilder::Array<int>::create();
1065 for (int i = fromIndex; i < toIndex; ++i)
1066 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1069 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
1071 m_searchResults.remove(searchId);
1074 bool InspectorDOMAgent::handleMousePress()
1076 if (m_searchingForNode == NotSearching)
1079 if (Node* node = m_overlay->highlightedNode()) {
1086 bool InspectorDOMAgent::handleGestureEvent(Frame* frame, const PlatformGestureEvent& event)
1088 if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap)
1090 Node* node = hoveredNodeForEvent(frame, event, false);
1091 if (node && m_inspectModeHighlightConfig) {
1092 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig);
1099 bool InspectorDOMAgent::handleTouchEvent(Frame* frame, const PlatformTouchEvent& event)
1101 if (m_searchingForNode == NotSearching)
1103 Node* node = hoveredNodeForEvent(frame, event, false);
1104 if (node && m_inspectModeHighlightConfig) {
1105 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig);
1112 void InspectorDOMAgent::inspect(Node* inspectedNode)
1114 if (!m_frontend || !inspectedNode)
1117 Node* node = inspectedNode;
1118 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
1119 node = node->parentNode();
1121 int nodeId = pushNodePathToFrontend(node);
1123 m_frontend->inspectNodeRequested(nodeId);
1126 void InspectorDOMAgent::handleMouseMove(Frame* frame, const PlatformMouseEvent& event)
1128 if (m_searchingForNode == NotSearching)
1131 if (!frame->view() || !frame->contentRenderer())
1133 Node* node = hoveredNodeForEvent(frame, event, event.shiftKey());
1135 while (m_searchingForNode != SearchingForShadow && node && node->isInShadowTree())
1136 node = node->parentOrShadowHostNode();
1138 Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0;
1139 if (eventTarget == node)
1142 if (node && m_inspectModeHighlightConfig)
1143 m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig);
1146 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject)
1148 if (m_searchingForNode == searchMode)
1151 m_searchingForNode = searchMode;
1152 m_overlay->setInspectModeEnabled(searchMode != NotSearching);
1153 if (searchMode != NotSearching) {
1154 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1155 if (!m_inspectModeHighlightConfig)
1158 hideHighlight(errorString);
1161 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject)
1163 if (!highlightInspectorObject) {
1164 *errorString = "Internal error: highlight configuration parameter is missing";
1168 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1169 bool showInfo = false; // Default: false (do not show a tooltip).
1170 highlightInspectorObject->getBoolean("showInfo", &showInfo);
1171 highlightConfig->showInfo = showInfo;
1172 bool showRulers = false; // Default: false (do not show rulers).
1173 highlightInspectorObject->getBoolean("showRulers", &showRulers);
1174 highlightConfig->showRulers = showRulers;
1175 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1176 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1177 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1178 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1179 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1180 highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject);
1181 return highlightConfig.release();
1184 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectShadowDOM, const RefPtr<JSONObject>* highlightConfig)
1186 if (enabled && !pushDocumentUponHandlelessOperation(errorString))
1188 SearchMode searchMode = enabled ? (inspectShadowDOM && *inspectShadowDOM ? SearchingForShadow : SearchingForNormal) : NotSearching;
1189 setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0);
1192 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1194 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height)));
1195 innerHighlightQuad(quad.release(), color, outlineColor);
1198 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1200 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad());
1201 if (!parseQuad(quadArray, quad.get())) {
1202 *errorString = "Invalid Quad format";
1205 innerHighlightQuad(quad.release(), color, outlineColor);
1208 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1210 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1211 highlightConfig->content = parseColor(color);
1212 highlightConfig->contentOutline = parseColor(outlineColor);
1213 m_overlay->highlightQuad(quad, *highlightConfig);
1216 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId)
1220 node = assertNode(errorString, *nodeId);
1221 } else if (objectId) {
1222 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId);
1223 node = injectedScript.nodeForObjectId(*objectId);
1225 *errorString = "Node for given objectId not found";
1227 *errorString = "Either nodeId or objectId must be specified";
1232 OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get());
1233 if (!highlightConfig)
1236 m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig);
1239 void InspectorDOMAgent::highlightFrame(
1241 const String& frameId,
1242 const RefPtr<JSONObject>* color,
1243 const RefPtr<JSONObject>* outlineColor)
1245 Frame* frame = m_pageAgent->frameForId(frameId);
1246 if (frame && frame->ownerElement()) {
1247 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1248 highlightConfig->showInfo = true; // Always show tooltips for frames.
1249 highlightConfig->content = parseColor(color);
1250 highlightConfig->contentOutline = parseColor(outlineColor);
1251 m_overlay->highlightNode(frame->ownerElement(), 0 /* eventTarget */, *highlightConfig);
1255 void InspectorDOMAgent::hideHighlight(ErrorString*)
1257 m_overlay->hideHighlight();
1260 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1262 Node* node = assertEditableNode(errorString, nodeId);
1266 Element* targetElement = assertEditableElement(errorString, targetElementId);
1270 Node* anchorNode = 0;
1271 if (anchorNodeId && *anchorNodeId) {
1272 anchorNode = assertEditableNode(errorString, *anchorNodeId);
1275 if (anchorNode->parentNode() != targetElement) {
1276 *errorString = "Anchor node must be child of the target element";
1281 if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString))
1284 *newNodeId = pushNodePathToFrontend(node);
1287 void InspectorDOMAgent::undo(ErrorString* errorString)
1289 TrackExceptionState exceptionState;
1290 m_history->undo(exceptionState);
1291 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1294 void InspectorDOMAgent::redo(ErrorString* errorString)
1296 TrackExceptionState exceptionState;
1297 m_history->redo(exceptionState);
1298 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1301 void InspectorDOMAgent::markUndoableState(ErrorString*)
1303 m_history->markUndoableState();
1306 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId)
1308 Element* element = assertElement(errorString, nodeId);
1312 element->document().updateLayoutIgnorePendingStylesheets();
1313 if (!element->isFocusable()) {
1314 *errorString = "Element is not focusable";
1320 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files)
1322 Node* node = assertNode(errorString, nodeId);
1325 if (!node->hasTagName(inputTag) || !toHTMLInputElement(node)->isFileUpload()) {
1326 *errorString = "Node is not a file input element";
1330 RefPtr<FileList> fileList = FileList::create();
1331 for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) {
1333 if (!(*iter)->asString(&path)) {
1334 *errorString = "Files must be strings";
1337 fileList->append(File::create(path));
1339 toHTMLInputElement(node)->setFiles(fileList);
1342 static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad)
1344 RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create();
1345 array->addItem(quad.p1().x());
1346 array->addItem(quad.p1().y());
1347 array->addItem(quad.p2().x());
1348 array->addItem(quad.p2().y());
1349 array->addItem(quad.p3().x());
1350 array->addItem(quad.p3().y());
1351 array->addItem(quad.p4().x());
1352 array->addItem(quad.p4().y());
1353 return array.release();
1356 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model)
1358 Node* node = assertNode(errorString, nodeId);
1362 Vector<FloatQuad> quads;
1363 bool isInlineOrBox = m_overlay->getBoxModel(node, &quads);
1364 if (!isInlineOrBox) {
1365 *errorString = "Could not compute box model.";
1369 RenderObject* renderer = node->renderer();
1370 Frame* frame = node->document().frame();
1371 FrameView* view = frame->view();
1373 IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect()));
1374 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
1376 model = TypeBuilder::DOM::BoxModel::create()
1377 .setContent(buildArrayForQuad(quads.at(3)))
1378 .setPadding(buildArrayForQuad(quads.at(2)))
1379 .setBorder(buildArrayForQuad(quads.at(1)))
1380 .setMargin(buildArrayForQuad(quads.at(0)))
1381 .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())
1382 .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height());
1385 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId)
1387 if (!pushDocumentUponHandlelessOperation(errorString))
1390 Node* node = hoveredNodeForPoint(m_document->frame(), IntPoint(x, y), false);
1392 *errorString = "No node found at given location";
1395 *nodeId = pushNodePathToFrontend(node);
1398 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
1400 String objectGroupName = objectGroup ? *objectGroup : "";
1401 Node* node = nodeForId(nodeId);
1403 *errorString = "No node with given id found";
1406 RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1408 *errorString = "Node with given id does not belong to the document";
1414 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result)
1416 Element* element = assertElement(errorString, nodeId);
1420 result = buildArrayForElementAttributes(element);
1423 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId)
1425 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
1426 Node* node = injectedScript.nodeForObjectId(objectId);
1428 *nodeId = pushNodePathToFrontend(node);
1434 String InspectorDOMAgent::documentURLString(Document* document)
1436 if (!document || document->url().isNull())
1438 return document->url().string();
1441 static String documentBaseURLString(Document* document)
1443 return document->completeURL("").string();
1446 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot)
1448 switch (shadowRoot->type()) {
1449 case ShadowRoot::UserAgentShadowRoot:
1450 return TypeBuilder::DOM::ShadowRootType::User_agent;
1451 case ShadowRoot::AuthorShadowRoot:
1452 return TypeBuilder::DOM::ShadowRootType::Author;
1454 ASSERT_NOT_REACHED();
1455 return TypeBuilder::DOM::ShadowRootType::User_agent;
1458 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1460 int id = bind(node, nodesMap);
1465 switch (node->nodeType()) {
1466 case Node::TEXT_NODE:
1467 case Node::COMMENT_NODE:
1468 case Node::CDATA_SECTION_NODE:
1469 nodeValue = node->nodeValue();
1470 if (nodeValue.length() > maxTextSize)
1471 nodeValue = nodeValue.left(maxTextSize) + ellipsisUChar;
1473 case Node::ATTRIBUTE_NODE:
1474 localName = node->localName();
1476 case Node::DOCUMENT_FRAGMENT_NODE:
1477 case Node::DOCUMENT_NODE:
1478 case Node::ELEMENT_NODE:
1480 nodeName = node->nodeName();
1481 localName = node->localName();
1485 RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create()
1487 .setNodeType(static_cast<int>(node->nodeType()))
1488 .setNodeName(nodeName)
1489 .setLocalName(localName)
1490 .setNodeValue(nodeValue);
1492 bool forcePushChildren = false;
1493 if (node->isElementNode()) {
1494 Element* element = toElement(node);
1495 value->setAttributes(buildArrayForElementAttributes(element));
1497 if (node->isFrameOwnerElement()) {
1498 HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node);
1499 if (Frame* frame = frameOwner->contentFrame())
1500 value->setFrameId(m_pageAgent->frameId(frame));
1501 if (Document* doc = frameOwner->contentDocument())
1502 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
1505 ElementShadow* shadow = element->shadow();
1507 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1508 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
1509 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1510 value->setShadowRoots(shadowRoots);
1511 forcePushChildren = true;
1514 if (element->hasTagName(linkTag)) {
1515 HTMLLinkElement* linkElement = toHTMLLinkElement(element);
1516 if (linkElement->isImport() && linkElement->import() && innerParentNode(linkElement->import()) == linkElement)
1517 value->setImportedDocument(buildObjectForNode(linkElement->import(), 0, nodesMap));
1518 forcePushChildren = true;
1521 if (element->hasTagName(templateTag)) {
1522 value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(element)->content(), 0, nodesMap));
1523 forcePushChildren = true;
1526 switch (element->pseudoId()) {
1528 value->setPseudoType(TypeBuilder::DOM::PseudoType::Before);
1531 value->setPseudoType(TypeBuilder::DOM::PseudoType::After);
1534 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap);
1535 if (pseudoElements) {
1536 value->setPseudoElements(pseudoElements.release());
1537 forcePushChildren = true;
1542 } else if (node->isDocumentNode()) {
1543 Document* document = toDocument(node);
1544 value->setDocumentURL(documentURLString(document));
1545 value->setBaseURL(documentBaseURLString(document));
1546 value->setXmlVersion(document->xmlVersion());
1547 } else if (node->isDocumentTypeNode()) {
1548 DocumentType* docType = toDocumentType(node);
1549 value->setPublicId(docType->publicId());
1550 value->setSystemId(docType->systemId());
1551 value->setInternalSubset(docType->internalSubset());
1552 } else if (node->isAttributeNode()) {
1553 Attr* attribute = toAttr(node);
1554 value->setName(attribute->name());
1555 value->setValue(attribute->value());
1556 } else if (node->isShadowRoot()) {
1557 value->setShadowRootType(shadowRootType(toShadowRoot(node)));
1560 if (node->isContainerNode()) {
1561 int nodeCount = innerChildNodeCount(node);
1562 value->setChildNodeCount(nodeCount);
1563 if (forcePushChildren && !depth)
1565 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap);
1566 if (children->length() > 0 || depth) // Push children along with shadow in any case.
1567 value->setChildren(children.release());
1570 return value.release();
1573 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1575 RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create();
1576 // Go through all attributes and serialize them.
1577 if (!element->hasAttributes())
1578 return attributesValue.release();
1579 unsigned numAttrs = element->attributeCount();
1580 for (unsigned i = 0; i < numAttrs; ++i) {
1581 // Add attribute pair
1582 const Attribute* attribute = element->attributeItem(i);
1583 attributesValue->addItem(attribute->name().toString());
1584 attributesValue->addItem(attribute->value());
1586 return attributesValue.release();
1589 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1591 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1593 // Special-case the only text child - pretend that container's children have been requested.
1594 Node* firstChild = container->firstChild();
1595 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1596 children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1597 m_childrenRequested.add(bind(container, nodesMap));
1599 return children.release();
1602 Node* child = innerFirstChild(container);
1604 m_childrenRequested.add(bind(container, nodesMap));
1607 children->addItem(buildObjectForNode(child, depth, nodesMap));
1608 child = innerNextSibling(child);
1610 return children.release();
1613 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
1615 RefPtr<EventListener> eventListener = registeredEventListener.listener;
1619 if (!eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber))
1622 Document& document = node->document();
1623 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
1624 .setScriptId(scriptId)
1625 .setLineNumber(lineNumber);
1626 RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
1628 .setUseCapture(registeredEventListener.useCapture)
1629 .setIsAttribute(eventListener->isAttribute())
1630 .setNodeId(pushNodePathToFrontend(node))
1631 .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get()))
1632 .setLocation(location);
1633 if (objectGroupId) {
1634 ScriptValue functionValue = eventListenerHandler(&document, eventListener.get());
1635 if (!functionValue.hasNoValue()) {
1636 Frame* frame = document.frame();
1638 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
1640 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1641 if (!injectedScript.hasNoValue()) {
1642 RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId);
1643 value->setHandler(valueJson);
1649 if (!sourceName.isEmpty())
1650 value->setSourceName(sourceName);
1651 return value.release();
1654 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap)
1656 if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER))
1659 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1660 if (element->pseudoElement(BEFORE))
1661 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap));
1662 if (element->pseudoElement(AFTER))
1663 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap));
1664 return pseudoElements.release();
1667 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1669 node = node->firstChild();
1670 while (isWhitespace(node))
1671 node = node->nextSibling();
1675 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1678 node = node->nextSibling();
1679 } while (isWhitespace(node));
1683 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1686 node = node->previousSibling();
1687 } while (isWhitespace(node));
1691 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1694 Node* child = innerFirstChild(node);
1697 child = innerNextSibling(child);
1702 Node* InspectorDOMAgent::innerParentNode(Node* node)
1704 if (node->isDocumentNode()) {
1705 Document* document = toDocument(node);
1706 HTMLImportChild* importChild = toHTMLImportChild(document->import());
1708 return importChild->link();
1709 return document->ownerElement();
1711 return node->parentOrShadowHostNode();
1714 bool InspectorDOMAgent::isWhitespace(Node* node)
1716 //TODO: pull ignoreWhitespace setting from the frontend and use here.
1717 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1720 void InspectorDOMAgent::domContentLoadedEventFired(Frame* frame)
1722 if (!frame->isMainFrame())
1725 // Re-push document once it is loaded.
1726 discardFrontendBindings();
1727 if (m_state->getBoolean(DOMAgentState::documentRequested))
1728 m_frontend->documentUpdated();
1731 void InspectorDOMAgent::invalidateFrameOwnerElement(Frame* frame)
1733 Element* frameOwner = frame->document()->ownerElement();
1737 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1741 // Re-add frame owner element together with its new children.
1742 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1743 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1744 unbind(frameOwner, &m_documentNodeToIdMap);
1746 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1747 Node* previousSibling = innerPreviousSibling(frameOwner);
1748 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1749 m_frontend->childNodeInserted(parentId, prevId, value.release());
1752 void InspectorDOMAgent::didCommitLoad(Frame* frame, DocumentLoader* loader)
1754 // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame()
1755 // then all we need to check here is loader->frame()->isMainFrame()
1756 // and we don't need "frame" at all.
1757 Frame* mainFrame = frame->page()->mainFrame();
1758 if (loader->frame() != mainFrame) {
1759 invalidateFrameOwnerElement(loader->frame());
1763 setDocument(mainFrame->document());
1766 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1768 if (isWhitespace(node))
1771 // We could be attaching existing subtree. Forget the bindings.
1772 unbind(node, &m_documentNodeToIdMap);
1774 ContainerNode* parent = node->parentNode();
1778 int parentId = m_documentNodeToIdMap.get(parent);
1779 // Return if parent is not mapped yet.
1783 if (!m_childrenRequested.contains(parentId)) {
1784 // No children are mapped yet -> only notify on changes of hasChildren.
1785 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1787 // Children have been requested -> return value of a new child.
1788 Node* prevSibling = innerPreviousSibling(node);
1789 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1790 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1791 m_frontend->childNodeInserted(parentId, prevId, value.release());
1795 void InspectorDOMAgent::willRemoveDOMNode(Node* node)
1797 if (isWhitespace(node))
1800 ContainerNode* parent = node->parentNode();
1802 // If parent is not mapped yet -> ignore the event.
1803 if (!m_documentNodeToIdMap.contains(parent))
1806 int parentId = m_documentNodeToIdMap.get(parent);
1808 if (!m_childrenRequested.contains(parentId)) {
1809 // No children are mapped yet -> only notify on changes of hasChildren.
1810 if (innerChildNodeCount(parent) == 1)
1811 m_frontend->childNodeCountUpdated(parentId, 0);
1813 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1814 unbind(node, &m_documentNodeToIdMap);
1817 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue)
1819 m_suppressAttributeModifiedEvent = (oldValue == newValue);
1822 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value)
1824 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
1825 m_suppressAttributeModifiedEvent = false;
1826 if (shouldSuppressEvent)
1829 int id = boundNodeId(element);
1830 // If node is not mapped yet -> ignore the event.
1835 m_domListener->didModifyDOMAttr(element);
1837 m_frontend->attributeModified(id, name, value);
1840 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name)
1842 int id = boundNodeId(element);
1843 // If node is not mapped yet -> ignore the event.
1848 m_domListener->didModifyDOMAttr(element);
1850 m_frontend->attributeRemoved(id, name);
1853 void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elements)
1855 RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create();
1856 for (unsigned i = 0, size = elements.size(); i < size; ++i) {
1857 Element* element = elements.at(i);
1858 int id = boundNodeId(element);
1859 // If node is not mapped yet -> ignore the event.
1864 m_domListener->didModifyDOMAttr(element);
1865 nodeIds->addItem(id);
1867 m_frontend->inlineStyleInvalidated(nodeIds.release());
1870 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1872 int id = m_documentNodeToIdMap.get(characterData);
1874 // Push text node if it is being created.
1875 didInsertDOMNode(characterData);
1878 m_frontend->characterDataModified(id, characterData->data());
1881 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1883 int id = m_documentNodeToIdMap.get(node);
1884 // If node is not mapped yet -> ignore the event.
1888 if (!m_revalidateStyleAttrTask)
1889 m_revalidateStyleAttrTask = adoptPtr(new RevalidateStyleAttributeTask(this));
1890 m_revalidateStyleAttrTask->scheduleFor(toElement(node));
1893 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root)
1895 if (!host->ownerDocument())
1898 int hostId = m_documentNodeToIdMap.get(host);
1902 pushChildNodesToFrontend(hostId, 1);
1903 m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, &m_documentNodeToIdMap));
1906 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root)
1908 if (!host->ownerDocument())
1911 int hostId = m_documentNodeToIdMap.get(host);
1912 int rootId = m_documentNodeToIdMap.get(root);
1913 if (hostId && rootId)
1914 m_frontend->shadowRootPopped(hostId, rootId);
1917 void InspectorDOMAgent::frameDocumentUpdated(Frame* frame)
1919 Document* document = frame->document();
1923 Page* page = frame->page();
1925 if (frame != page->mainFrame())
1928 // Only update the main frame document, nested frame document updates are not required
1929 // (will be handled by invalidateFrameOwnerElement()).
1930 setDocument(document);
1933 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement)
1935 Element* parent = pseudoElement->parentOrShadowHostElement();
1938 int parentId = m_documentNodeToIdMap.get(parent);
1942 pushChildNodesToFrontend(parentId, 1);
1943 m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, &m_documentNodeToIdMap));
1946 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
1948 int pseudoElementId = m_documentNodeToIdMap.get(pseudoElement);
1949 if (!pseudoElementId)
1952 // If a PseudoElement is bound, its parent element must be bound, too.
1953 Element* parent = pseudoElement->parentOrShadowHostElement();
1955 int parentId = m_documentNodeToIdMap.get(parent);
1958 unbind(pseudoElement, &m_documentNodeToIdMap);
1959 m_frontend->pseudoElementRemoved(parentId, pseudoElementId);
1962 Node* InspectorDOMAgent::nodeForPath(const String& path)
1964 // The path is of form "1,HTML,2,BODY,1,DIV"
1968 Node* node = m_document.get();
1969 Vector<String> pathTokens;
1970 path.split(",", false, pathTokens);
1971 if (!pathTokens.size())
1973 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1974 bool success = true;
1975 unsigned childNumber = pathTokens[i].toUInt(&success);
1978 if (childNumber >= innerChildNodeCount(node))
1981 Node* child = innerFirstChild(node);
1982 String childName = pathTokens[i + 1];
1983 for (size_t j = 0; child && j < childNumber; ++j)
1984 child = innerNextSibling(child);
1986 if (!child || child->nodeName() != childName)
1993 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId)
1995 if (Node* node = nodeForPath(path))
1996 *nodeId = pushNodePathToFrontend(node);
1998 *errorString = "No node with given path found";
2001 void InspectorDOMAgent::pushNodeByBackendIdToFrontend(ErrorString* errorString, BackendNodeId backendNodeId, int* nodeId)
2003 if (!m_backendIdToNode.contains(backendNodeId)) {
2004 *errorString = "No node with given backend id found";
2008 Node* node = m_backendIdToNode.get(backendNodeId).first;
2009 String nodeGroup = m_backendIdToNode.get(backendNodeId).second;
2010 *nodeId = pushNodePathToFrontend(node);
2012 if (nodeGroup == "") {
2013 m_backendIdToNode.remove(backendNodeId);
2014 m_nodeGroupToBackendIdMap.find(nodeGroup)->value.remove(node);
2018 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId)
2020 Node* node = assertNode(errorString, nodeId);
2023 RenderObject* renderer = node->renderer();
2025 *errorString = "No renderer for node, perhaps orphan or hidden node";
2028 while (renderer && !renderer->isRoot() && !renderer->isRelayoutBoundaryForInspector())
2029 renderer = renderer->container();
2030 Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument();
2031 *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode);
2034 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2036 Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument();
2037 Frame* frame = document ? document->frame() : 0;
2041 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
2042 if (injectedScript.hasNoValue())
2045 return injectedScript.wrapNode(node, objectGroup);
2048 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString)
2050 if (!m_documentNodeToIdMap.contains(m_document)) {
2051 RefPtr<TypeBuilder::DOM::Node> root;
2052 getDocument(errorString, root);
2053 return errorString->isEmpty();
2058 } // namespace WebCore