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 "bindings/v8/ExceptionState.h"
35 #include "bindings/v8/ScriptEventListener.h"
36 #include "core/dom/Attr.h"
37 #include "core/dom/CharacterData.h"
38 #include "core/dom/ContainerNode.h"
39 #include "core/dom/DOMException.h"
40 #include "core/dom/Document.h"
41 #include "core/dom/DocumentFragment.h"
42 #include "core/dom/DocumentType.h"
43 #include "core/dom/Element.h"
44 #include "core/events/EventListener.h"
45 #include "core/events/EventTarget.h"
46 #include "core/dom/Node.h"
47 #include "core/dom/NodeList.h"
48 #include "core/dom/NodeTraversal.h"
49 #include "core/dom/PseudoElement.h"
50 #include "core/dom/Text.h"
51 #include "core/dom/shadow/ElementShadow.h"
52 #include "core/dom/shadow/ShadowRoot.h"
53 #include "core/editing/markup.h"
54 #include "core/fileapi/File.h"
55 #include "core/fileapi/FileList.h"
56 #include "core/frame/LocalFrame.h"
57 #include "core/html/HTMLFrameOwnerElement.h"
58 #include "core/html/HTMLInputElement.h"
59 #include "core/html/HTMLLinkElement.h"
60 #include "core/html/HTMLTemplateElement.h"
61 #include "core/html/imports/HTMLImportChild.h"
62 #include "core/html/imports/HTMLImportLoader.h"
63 #include "core/inspector/DOMEditor.h"
64 #include "core/inspector/DOMPatchSupport.h"
65 #include "core/inspector/IdentifiersFactory.h"
66 #include "core/inspector/InspectorHistory.h"
67 #include "core/inspector/InspectorNodeIds.h"
68 #include "core/inspector/InspectorOverlay.h"
69 #include "core/inspector/InspectorPageAgent.h"
70 #include "core/inspector/InspectorState.h"
71 #include "core/inspector/InstrumentingAgents.h"
72 #include "core/loader/DocumentLoader.h"
73 #include "core/page/FrameTree.h"
74 #include "core/page/Page.h"
75 #include "core/rendering/HitTestResult.h"
76 #include "core/rendering/RenderView.h"
77 #include "core/xml/DocumentXPathEvaluator.h"
78 #include "core/xml/XPathResult.h"
79 #include "platform/PlatformGestureEvent.h"
80 #include "platform/PlatformMouseEvent.h"
81 #include "platform/PlatformTouchEvent.h"
82 #include "wtf/ListHashSet.h"
83 #include "wtf/text/CString.h"
84 #include "wtf/text/WTFString.h"
88 using namespace HTMLNames;
90 namespace DOMAgentState {
91 static const char documentRequested[] = "documentRequested";
94 static const size_t maxTextSize = 10000;
95 static const UChar ellipsisUChar[] = { 0x2026, 0 };
97 static Color parseColor(const RefPtr<JSONObject>* colorObject)
99 if (!colorObject || !(*colorObject))
100 return Color::transparent;
105 bool success = (*colorObject)->getNumber("r", &r);
106 success |= (*colorObject)->getNumber("g", &g);
107 success |= (*colorObject)->getNumber("b", &b);
109 return Color::transparent;
112 success = (*colorObject)->getNumber("a", &a);
114 return Color(r, g, b);
116 // Clamp alpha to the [0..1] range.
122 return Color(r, g, b, static_cast<int>(a * 255));
125 static Color parseConfigColor(const String& fieldName, JSONObject* configObject)
127 const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName);
128 return parseColor(&colorObject);
131 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad)
135 const size_t coordinatesInQuad = 8;
136 double coordinates[coordinatesInQuad];
137 if (quadArray->length() != coordinatesInQuad)
139 for (size_t i = 0; i < coordinatesInQuad; ++i) {
140 if (!quadArray->get(i)->asNumber(coordinates + i))
143 quad->setP1(FloatPoint(coordinates[0], coordinates[1]));
144 quad->setP2(FloatPoint(coordinates[2], coordinates[3]));
145 quad->setP3(FloatPoint(coordinates[4], coordinates[5]));
146 quad->setP4(FloatPoint(coordinates[6], coordinates[7]));
151 static Node* hoveredNodeForPoint(LocalFrame* frame, const IntPoint& point, bool ignorePointerEventsNone)
153 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent;
154 if (ignorePointerEventsNone)
155 hitType |= HitTestRequest::IgnorePointerEventsNone;
156 HitTestRequest request(hitType);
157 HitTestResult result(frame->view()->windowToContents(point));
158 frame->contentRenderer()->hitTest(request, result);
159 Node* node = result.innerPossiblyPseudoNode();
160 while (node && node->nodeType() == Node::TEXT_NODE)
161 node = node->parentNode();
165 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone)
167 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
170 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone)
172 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
175 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone)
177 const Vector<PlatformTouchPoint>& points = event.touchPoints();
180 return hoveredNodeForPoint(frame, points[0].pos(), ignorePointerEventsNone);
183 class RevalidateStyleAttributeTask {
184 WTF_MAKE_FAST_ALLOCATED;
186 RevalidateStyleAttributeTask(InspectorDOMAgent*);
187 void scheduleFor(Element*);
188 void reset() { m_timer.stop(); }
189 void onTimer(Timer<RevalidateStyleAttributeTask>*);
192 InspectorDOMAgent* m_domAgent;
193 Timer<RevalidateStyleAttributeTask> m_timer;
194 HashSet<RefPtr<Element> > m_elements;
197 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
198 : m_domAgent(domAgent)
199 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
203 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
205 m_elements.add(element);
206 if (!m_timer.isActive())
207 m_timer.startOneShot(0, FROM_HERE);
210 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
212 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
213 Vector<Element*> elements;
214 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
215 elements.append(it->get());
216 m_domAgent->styleAttributeInvalidated(elements);
221 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState)
223 if (exceptionState.hadException())
224 return DOMException::getErrorName(exceptionState.code()) + " " + exceptionState.message();
228 InspectorDOMAgent::InspectorDOMAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
229 : InspectorBaseAgent<InspectorDOMAgent>("DOM")
230 , m_pageAgent(pageAgent)
231 , m_injectedScriptManager(injectedScriptManager)
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.
278 m_document = nullptr;
279 setDocument(m_pageAgent->mainFrame()->document());
282 Vector<Document*> InspectorDOMAgent::documents()
284 Vector<Document*> result;
285 for (LocalFrame* 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 m_document = nullptr;
300 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
302 m_domListener = listener;
305 void InspectorDOMAgent::setDocument(Document* doc)
307 if (doc == m_document.get())
314 if (!m_state->getBoolean(DOMAgentState::documentRequested))
317 // Immediately communicate 0 document or document that has finished loading.
318 if (!doc || !doc->parsing())
319 m_frontend->documentUpdated();
322 void InspectorDOMAgent::releaseDanglingNodes()
324 m_danglingNodeToIdMaps.clear();
327 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
329 int id = nodesMap->get(node);
333 nodesMap->set(node, id);
334 m_idToNode.set(id, node);
335 m_idToNodesMap.set(id, nodesMap);
339 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
341 int id = nodesMap->get(node);
345 m_idToNode.remove(id);
347 if (node->isFrameOwnerElement()) {
348 Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument();
350 m_domListener->didRemoveDocument(contentDocument);
352 unbind(contentDocument, nodesMap);
355 for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot())
356 unbind(root, nodesMap);
358 if (node->isElementNode()) {
359 Element* element = toElement(node);
360 if (element->pseudoElement(BEFORE))
361 unbind(element->pseudoElement(BEFORE), nodesMap);
362 if (element->pseudoElement(AFTER))
363 unbind(element->pseudoElement(AFTER), nodesMap);
365 if (isHTMLLinkElement(*element)) {
366 HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
367 if (linkElement.isImport() && linkElement.import())
368 unbind(linkElement.import(), nodesMap);
372 nodesMap->remove(node);
374 m_domListener->didRemoveDOMNode(node);
376 bool childrenRequested = m_childrenRequested.contains(id);
377 if (childrenRequested) {
378 // Unbind subtree known to client recursively.
379 m_childrenRequested.remove(id);
380 Node* child = innerFirstChild(node);
382 unbind(child, nodesMap);
383 child = innerNextSibling(child);
386 if (nodesMap == &m_documentNodeToIdMap)
387 m_cachedChildCount.remove(id);
390 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
392 Node* node = nodeForId(nodeId);
394 *errorString = "Could not find node with given id";
400 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId)
402 Node* node = assertNode(errorString, nodeId);
406 if (!(node->isDocumentNode())) {
407 *errorString = "Document is not available";
410 return toDocument(node);
413 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
415 Node* node = assertNode(errorString, nodeId);
419 if (node->nodeType() != Node::ELEMENT_NODE) {
420 *errorString = "Node is not an Element";
423 return toElement(node);
426 static ShadowRoot* userAgentShadowRoot(Node* node)
428 if (!node || !node->isInShadowTree())
431 Node* candidate = node;
432 while (candidate && !candidate->isShadowRoot())
433 candidate = candidate->parentOrShadowHostNode();
435 ShadowRoot* shadowRoot = toShadowRoot(candidate);
437 return shadowRoot->type() == ShadowRoot::UserAgentShadowRoot ? shadowRoot : 0;
440 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId)
442 Node* node = assertNode(errorString, nodeId);
446 if (node->isInShadowTree()) {
447 if (node->isShadowRoot()) {
448 *errorString = "Cannot edit shadow roots";
451 if (userAgentShadowRoot(node)) {
452 *errorString = "Cannot edit nodes from user-agent shadow trees";
457 if (node->isPseudoElement()) {
458 *errorString = "Cannot edit pseudo elements";
465 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId)
467 Element* element = assertElement(errorString, nodeId);
471 if (element->isInShadowTree() && userAgentShadowRoot(element)) {
472 *errorString = "Cannot edit elements from user-agent shadow trees";
476 if (element->isPseudoElement()) {
477 *errorString = "Cannot edit pseudo elements";
484 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root)
486 m_state->setBoolean(DOMAgentState::documentRequested, true);
489 *errorString = "Document is not available";
493 discardFrontendBindings();
495 root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
498 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
500 Node* node = nodeForId(nodeId);
501 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
504 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
506 if (m_childrenRequested.contains(nodeId)) {
512 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
513 int childNodeId = nodeMap->get(node);
515 pushChildNodesToFrontend(childNodeId, depth);
521 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap);
522 m_frontend->setChildNodes(nodeId, children.release());
525 void InspectorDOMAgent::discardFrontendBindings()
529 m_searchResults.clear();
530 m_documentNodeToIdMap.clear();
532 releaseDanglingNodes();
533 m_childrenRequested.clear();
534 m_cachedChildCount.clear();
535 if (m_revalidateStyleAttrTask)
536 m_revalidateStyleAttrTask->reset();
539 Node* InspectorDOMAgent::nodeForId(int id)
544 HashMap<int, Node*>::iterator it = m_idToNode.find(id);
545 if (it != m_idToNode.end())
550 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth)
556 else if (*depth == -1)
557 sanitizedDepth = INT_MAX;
559 sanitizedDepth = *depth;
561 *errorString = "Please provide a positive integer as a depth or -1 for entire subtree";
565 pushChildNodesToFrontend(nodeId, sanitizedDepth);
568 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
571 Node* node = assertNode(errorString, nodeId);
572 if (!node || !node->isContainerNode())
575 TrackExceptionState exceptionState;
576 RefPtr<Element> element = toContainerNode(node)->querySelector(AtomicString(selectors), exceptionState);
577 if (exceptionState.hadException()) {
578 *errorString = "DOM Error while querying";
583 *elementId = pushNodePathToFrontend(element.get());
586 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result)
588 Node* node = assertNode(errorString, nodeId);
589 if (!node || !node->isContainerNode())
592 TrackExceptionState exceptionState;
593 RefPtr<NodeList> nodes = toContainerNode(node)->querySelectorAll(AtomicString(selectors), exceptionState);
594 if (exceptionState.hadException()) {
595 *errorString = "DOM Error while querying";
599 result = TypeBuilder::Array<int>::create();
601 for (unsigned i = 0; i < nodes->length(); ++i)
602 result->addItem(pushNodePathToFrontend(nodes->item(i)));
605 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
607 ASSERT(nodeToPush); // Invalid input
611 if (!m_documentNodeToIdMap.contains(m_document))
614 // Return id in case the node is known.
615 int result = m_documentNodeToIdMap.get(nodeToPush);
619 Node* node = nodeToPush;
621 NodeToIdMap* danglingMap = 0;
624 Node* parent = innerParentNode(node);
626 // Node being pushed is detached -> push subtree root.
627 OwnPtr<NodeToIdMap> newMap = adoptPtr(new NodeToIdMap);
628 danglingMap = newMap.get();
629 m_danglingNodeToIdMaps.append(newMap.release());
630 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
631 children->addItem(buildObjectForNode(node, 0, danglingMap));
632 m_frontend->setChildNodes(0, children);
636 if (m_documentNodeToIdMap.get(parent))
643 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
644 for (int i = path.size() - 1; i >= 0; --i) {
645 int nodeId = map->get(path.at(i));
647 pushChildNodesToFrontend(nodeId);
649 return map->get(nodeToPush);
652 int InspectorDOMAgent::boundNodeId(Node* node)
654 return m_documentNodeToIdMap.get(node);
657 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value)
659 Element* element = assertEditableElement(errorString, elementId);
663 m_domEditor->setAttribute(element, name, value, errorString);
666 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
668 Element* element = assertEditableElement(errorString, elementId);
672 String markup = "<span " + text + "></span>";
673 RefPtr<DocumentFragment> fragment = element->document().createDocumentFragment();
675 bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement();
676 // Not all elements can represent the context (i.e. IFRAME), hence using document.body.
677 if (shouldIgnoreCase && element->document().body())
678 fragment->parseHTML(markup, element->document().body(), AllowScriptingContent);
680 fragment->parseXML(markup, 0, AllowScriptingContent);
682 Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0;
683 if (!parsedElement) {
684 *errorString = "Could not parse value as attributes";
688 String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String();
690 if (!parsedElement->hasAttributes() && name) {
691 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
695 bool foundOriginalAttribute = false;
696 unsigned numAttrs = parsedElement->attributeCount();
697 for (unsigned i = 0; i < numAttrs; ++i) {
698 // Add attribute pair
699 const Attribute& attribute = parsedElement->attributeItem(i);
700 String attributeName = attribute.name().toString();
701 if (shouldIgnoreCase)
702 attributeName = attributeName.lower();
703 foundOriginalAttribute |= name && attributeName == caseAdjustedName;
704 if (!m_domEditor->setAttribute(element, attributeName, attribute.value(), errorString))
708 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
709 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
712 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
714 Element* element = assertEditableElement(errorString, elementId);
718 m_domEditor->removeAttribute(element, name, errorString);
721 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
723 Node* node = assertEditableNode(errorString, nodeId);
727 ContainerNode* parentNode = node->parentNode();
729 *errorString = "Cannot remove detached node";
733 m_domEditor->removeChild(parentNode, node, errorString);
736 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
740 Node* oldNode = nodeForId(nodeId);
741 if (!oldNode || !oldNode->isElementNode())
744 TrackExceptionState exceptionState;
745 RefPtr<Element> newElem = oldNode->document().createElement(AtomicString(tagName), exceptionState);
746 if (exceptionState.hadException())
749 // Copy over the original node's attributes.
750 newElem->cloneAttributesFromElement(*toElement(oldNode));
752 // Copy over the original node's children.
754 while ((child = oldNode->firstChild())) {
755 if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
759 // Replace the old node with the new node
760 ContainerNode* parent = oldNode->parentNode();
761 if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
763 if (!m_domEditor->removeChild(parent, oldNode, errorString))
766 *newId = pushNodePathToFrontend(newElem.get());
767 if (m_childrenRequested.contains(nodeId))
768 pushChildNodesToFrontend(*newId);
771 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
773 Node* node = assertNode(errorString, nodeId);
777 *outerHTML = createMarkup(node);
780 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
784 DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get());
785 domPatchSupport.patchDocument(outerHTML);
789 Node* node = assertEditableNode(errorString, nodeId);
793 Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument();
794 if (!document || (!document->isHTMLDocument() && !document->isXMLDocument())) {
795 *errorString = "Not an HTML/XML document";
800 if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString))
804 // The only child node has been deleted.
808 int newId = pushNodePathToFrontend(newNode);
810 bool childrenRequested = m_childrenRequested.contains(nodeId);
811 if (childrenRequested)
812 pushChildNodesToFrontend(newId);
815 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
817 Node* node = assertEditableNode(errorString, nodeId);
821 if (node->nodeType() != Node::TEXT_NODE) {
822 *errorString = "Can only set value of text nodes";
826 m_domEditor->replaceWholeText(toText(node), value, errorString);
829 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
831 listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
832 Node* node = assertNode(errorString, nodeId);
835 Vector<EventListenerInfo> eventInformation;
836 getEventListeners(node, eventInformation, true);
838 // Get Capturing Listeners (in this order)
839 size_t eventInformationLength = eventInformation.size();
840 for (size_t i = 0; i < eventInformationLength; ++i) {
841 const EventListenerInfo& info = eventInformation[i];
842 const EventListenerVector& vector = info.eventListenerVector;
843 for (size_t j = 0; j < vector.size(); ++j) {
844 const RegisteredEventListener& listener = vector[j];
845 if (listener.useCapture) {
846 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
848 listenersArray->addItem(listenerObject);
853 // Get Bubbling Listeners (reverse order)
854 for (size_t i = eventInformationLength; i; --i) {
855 const EventListenerInfo& info = eventInformation[i - 1];
856 const EventListenerVector& vector = info.eventListenerVector;
857 for (size_t j = 0; j < vector.size(); ++j) {
858 const RegisteredEventListener& listener = vector[j];
859 if (!listener.useCapture) {
860 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
862 listenersArray->addItem(listenerObject);
868 void InspectorDOMAgent::getEventListeners(EventTarget* target, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
870 // The Node's Ancestors including self.
871 Vector<EventTarget*> ancestors;
872 ancestors.append(target);
873 if (includeAncestors) {
874 Node* node = target->toNode();
875 for (ContainerNode* ancestor = node ? node->parentOrShadowHostNode() : 0; ancestor; ancestor = ancestor->parentOrShadowHostNode())
876 ancestors.append(ancestor);
879 // Nodes and their Listeners for the concerned event types (order is top to bottom)
880 for (size_t i = ancestors.size(); i; --i) {
881 EventTarget* ancestor = ancestors[i - 1];
882 Vector<AtomicString> eventTypes = ancestor->eventTypes();
883 for (size_t j = 0; j < eventTypes.size(); ++j) {
884 AtomicString& type = eventTypes[j];
885 const EventListenerVector& listeners = ancestor->getEventListeners(type);
886 EventListenerVector filteredListeners;
887 filteredListeners.reserveCapacity(listeners.size());
888 for (size_t k = 0; k < listeners.size(); ++k) {
889 if (listeners[k].listener->type() == EventListener::JSEventListenerType)
890 filteredListeners.append(listeners[k]);
892 if (!filteredListeners.isEmpty())
893 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners));
898 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount)
900 // FIXME: Few things are missing here:
901 // 1) Search works with node granularity - number of matches within node is not calculated.
902 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
905 unsigned queryLength = whitespaceTrimmedQuery.length();
906 bool startTagFound = !whitespaceTrimmedQuery.find('<');
907 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
908 bool startQuoteFound = !whitespaceTrimmedQuery.find('"');
909 bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength;
910 bool exactAttributeMatch = startQuoteFound && endQuoteFound;
912 String tagNameQuery = whitespaceTrimmedQuery;
913 String attributeQuery = whitespaceTrimmedQuery;
915 tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
917 tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
919 attributeQuery = attributeQuery.right(attributeQuery.length() - 1);
921 attributeQuery = attributeQuery.left(attributeQuery.length() - 1);
923 Vector<Document*> docs = documents();
924 ListHashSet<Node*> resultCollector;
926 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
927 Document* document = *it;
928 Node* node = document->documentElement();
932 // Manual plain text search.
933 while ((node = NodeTraversal::next(*node, document->documentElement()))) {
934 switch (node->nodeType()) {
935 case Node::TEXT_NODE:
936 case Node::COMMENT_NODE:
937 case Node::CDATA_SECTION_NODE: {
938 String text = node->nodeValue();
939 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound)
940 resultCollector.add(node);
943 case Node::ELEMENT_NODE: {
944 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound))
945 || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery))
946 || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false))
947 || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) {
948 resultCollector.add(node);
951 // Go through all attributes and serialize them.
952 const Element* element = toElement(node);
953 if (!element->hasAttributes())
956 unsigned numAttrs = element->attributeCount();
957 for (unsigned i = 0; i < numAttrs; ++i) {
958 // Add attribute pair
959 const Attribute& attribute = element->attributeItem(i);
960 if (attribute.localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) {
961 resultCollector.add(node);
964 size_t foundPosition = attribute.value().find(attributeQuery, 0, false);
965 if (foundPosition != kNotFound) {
966 if (!exactAttributeMatch || (!foundPosition && attribute.value().length() == attributeQuery.length())) {
967 resultCollector.add(node);
980 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
981 Document* document = *it;
983 TrackExceptionState exceptionState;
984 RefPtrWillBeRawPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(*document, whitespaceTrimmedQuery, document, nullptr, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState);
985 if (exceptionState.hadException() || !result)
988 unsigned long size = result->snapshotLength(exceptionState);
989 for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) {
990 Node* node = result->snapshotItem(i, exceptionState);
991 if (exceptionState.hadException())
994 if (node->nodeType() == Node::ATTRIBUTE_NODE)
995 node = toAttr(node)->ownerElement();
996 resultCollector.add(node);
1000 // Selector evaluation
1001 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
1002 Document* document = *it;
1003 TrackExceptionState exceptionState;
1004 RefPtr<NodeList> nodeList = document->querySelectorAll(AtomicString(whitespaceTrimmedQuery), exceptionState);
1005 if (exceptionState.hadException() || !nodeList)
1008 unsigned size = nodeList->length();
1009 for (unsigned i = 0; i < size; ++i)
1010 resultCollector.add(nodeList->item(i));
1014 *searchId = IdentifiersFactory::createIdentifier();
1015 Vector<RefPtr<Node> >* resultsIt = &m_searchResults.add(*searchId, Vector<RefPtr<Node> >()).storedValue->value;
1017 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
1018 resultsIt->append(*it);
1020 *resultCount = resultsIt->size();
1023 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds)
1025 SearchResults::iterator it = m_searchResults.find(searchId);
1026 if (it == m_searchResults.end()) {
1027 *errorString = "No search session with given id found";
1031 int size = it->value.size();
1032 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1033 *errorString = "Invalid search result range";
1037 nodeIds = TypeBuilder::Array<int>::create();
1038 for (int i = fromIndex; i < toIndex; ++i)
1039 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1042 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
1044 m_searchResults.remove(searchId);
1047 bool InspectorDOMAgent::handleMousePress()
1049 if (m_searchingForNode == NotSearching)
1052 if (Node* node = m_overlay->highlightedNode()) {
1059 bool InspectorDOMAgent::handleGestureEvent(LocalFrame* frame, const PlatformGestureEvent& event)
1061 if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap)
1063 Node* node = hoveredNodeForEvent(frame, event, false);
1064 if (node && m_inspectModeHighlightConfig) {
1065 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1072 bool InspectorDOMAgent::handleTouchEvent(LocalFrame* frame, const PlatformTouchEvent& event)
1074 if (m_searchingForNode == NotSearching)
1076 Node* node = hoveredNodeForEvent(frame, event, false);
1077 if (node && m_inspectModeHighlightConfig) {
1078 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1085 void InspectorDOMAgent::inspect(Node* inspectedNode)
1087 if (!m_frontend || !inspectedNode)
1090 Node* node = inspectedNode;
1091 while (node && node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE)
1092 node = node->parentOrShadowHostNode();
1097 int nodeId = pushNodePathToFrontend(node);
1099 m_frontend->inspectNodeRequested(nodeId);
1102 void InspectorDOMAgent::handleMouseMove(LocalFrame* frame, const PlatformMouseEvent& event)
1104 if (m_searchingForNode == NotSearching)
1107 if (!frame->view() || !frame->contentRenderer())
1109 Node* node = hoveredNodeForEvent(frame, event, event.shiftKey());
1111 // Do not highlight within UA shadow root unless requested.
1112 if (m_searchingForNode != SearchingForUAShadow) {
1113 ShadowRoot* uaShadowRoot = userAgentShadowRoot(node);
1115 node = uaShadowRoot->host();
1118 // Shadow roots don't have boxes - use host element instead.
1119 if (node && node->isShadowRoot())
1120 node = node->parentOrShadowHostNode();
1125 Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0;
1126 if (eventTarget == node)
1129 if (node && m_inspectModeHighlightConfig)
1130 m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig, event.ctrlKey() || event.metaKey());
1133 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject)
1135 if (m_searchingForNode == searchMode)
1138 m_searchingForNode = searchMode;
1139 m_overlay->setInspectModeEnabled(searchMode != NotSearching);
1140 if (searchMode != NotSearching) {
1141 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1142 if (!m_inspectModeHighlightConfig)
1145 hideHighlight(errorString);
1148 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject)
1150 if (!highlightInspectorObject) {
1151 *errorString = "Internal error: highlight configuration parameter is missing";
1155 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1156 bool showInfo = false; // Default: false (do not show a tooltip).
1157 highlightInspectorObject->getBoolean("showInfo", &showInfo);
1158 highlightConfig->showInfo = showInfo;
1159 bool showRulers = false; // Default: false (do not show rulers).
1160 highlightInspectorObject->getBoolean("showRulers", &showRulers);
1161 highlightConfig->showRulers = showRulers;
1162 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1163 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1164 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1165 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1166 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1167 highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject);
1168 return highlightConfig.release();
1171 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectUAShadowDOM, const RefPtr<JSONObject>* highlightConfig)
1173 if (enabled && !pushDocumentUponHandlelessOperation(errorString))
1175 SearchMode searchMode = enabled ? (inspectUAShadowDOM && *inspectUAShadowDOM ? SearchingForUAShadow : SearchingForNormal) : NotSearching;
1176 setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0);
1179 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1181 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height)));
1182 innerHighlightQuad(quad.release(), color, outlineColor);
1185 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1187 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad());
1188 if (!parseQuad(quadArray, quad.get())) {
1189 *errorString = "Invalid Quad format";
1192 innerHighlightQuad(quad.release(), color, outlineColor);
1195 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1197 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1198 highlightConfig->content = parseColor(color);
1199 highlightConfig->contentOutline = parseColor(outlineColor);
1200 m_overlay->highlightQuad(quad, *highlightConfig);
1203 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId)
1207 node = assertNode(errorString, *nodeId);
1208 } else if (objectId) {
1209 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId);
1210 node = injectedScript.nodeForObjectId(*objectId);
1212 *errorString = "Node for given objectId not found";
1214 *errorString = "Either nodeId or objectId must be specified";
1219 OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get());
1220 if (!highlightConfig)
1223 m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig, false);
1226 void InspectorDOMAgent::highlightFrame(
1228 const String& frameId,
1229 const RefPtr<JSONObject>* color,
1230 const RefPtr<JSONObject>* outlineColor)
1232 LocalFrame* frame = m_pageAgent->frameForId(frameId);
1233 if (frame && frame->ownerElement()) {
1234 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1235 highlightConfig->showInfo = true; // Always show tooltips for frames.
1236 highlightConfig->content = parseColor(color);
1237 highlightConfig->contentOutline = parseColor(outlineColor);
1238 m_overlay->highlightNode(frame->ownerElement(), 0 /* eventTarget */, *highlightConfig, false);
1242 void InspectorDOMAgent::hideHighlight(ErrorString*)
1244 m_overlay->hideHighlight();
1247 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1249 Node* node = assertEditableNode(errorString, nodeId);
1253 Element* targetElement = assertEditableElement(errorString, targetElementId);
1257 Node* anchorNode = 0;
1258 if (anchorNodeId && *anchorNodeId) {
1259 anchorNode = assertEditableNode(errorString, *anchorNodeId);
1262 if (anchorNode->parentNode() != targetElement) {
1263 *errorString = "Anchor node must be child of the target element";
1268 if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString))
1271 *newNodeId = pushNodePathToFrontend(node);
1274 void InspectorDOMAgent::undo(ErrorString* errorString)
1276 TrackExceptionState exceptionState;
1277 m_history->undo(exceptionState);
1278 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1281 void InspectorDOMAgent::redo(ErrorString* errorString)
1283 TrackExceptionState exceptionState;
1284 m_history->redo(exceptionState);
1285 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1288 void InspectorDOMAgent::markUndoableState(ErrorString*)
1290 m_history->markUndoableState();
1293 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId)
1295 Element* element = assertElement(errorString, nodeId);
1299 element->document().updateLayoutIgnorePendingStylesheets();
1300 if (!element->isFocusable()) {
1301 *errorString = "Element is not focusable";
1307 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files)
1309 Node* node = assertNode(errorString, nodeId);
1312 if (!isHTMLInputElement(*node) || !toHTMLInputElement(*node).isFileUpload()) {
1313 *errorString = "Node is not a file input element";
1317 RefPtrWillBeRawPtr<FileList> fileList = FileList::create();
1318 for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) {
1320 if (!(*iter)->asString(&path)) {
1321 *errorString = "Files must be strings";
1324 fileList->append(File::create(path));
1326 toHTMLInputElement(node)->setFiles(fileList);
1329 static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad)
1331 RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create();
1332 array->addItem(quad.p1().x());
1333 array->addItem(quad.p1().y());
1334 array->addItem(quad.p2().x());
1335 array->addItem(quad.p2().y());
1336 array->addItem(quad.p3().x());
1337 array->addItem(quad.p3().y());
1338 array->addItem(quad.p4().x());
1339 array->addItem(quad.p4().y());
1340 return array.release();
1343 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model)
1345 Node* node = assertNode(errorString, nodeId);
1349 Vector<FloatQuad> quads;
1350 bool isInlineOrBox = m_overlay->getBoxModel(node, &quads);
1351 if (!isInlineOrBox) {
1352 *errorString = "Could not compute box model.";
1356 RenderObject* renderer = node->renderer();
1357 LocalFrame* frame = node->document().frame();
1358 FrameView* view = frame->view();
1360 IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect()));
1361 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
1362 RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeOutsideInfo = m_overlay->buildObjectForShapeOutside(node);
1364 model = TypeBuilder::DOM::BoxModel::create()
1365 .setContent(buildArrayForQuad(quads.at(3)))
1366 .setPadding(buildArrayForQuad(quads.at(2)))
1367 .setBorder(buildArrayForQuad(quads.at(1)))
1368 .setMargin(buildArrayForQuad(quads.at(0)))
1369 .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())
1370 .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height());
1371 if (shapeOutsideInfo)
1372 model->setShapeOutside(shapeOutsideInfo);
1375 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId)
1377 if (!pushDocumentUponHandlelessOperation(errorString))
1380 Node* node = hoveredNodeForPoint(m_document->frame(), IntPoint(x, y), false);
1382 *errorString = "No node found at given location";
1385 *nodeId = pushNodePathToFrontend(node);
1388 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
1390 String objectGroupName = objectGroup ? *objectGroup : "";
1391 Node* node = nodeForId(nodeId);
1393 *errorString = "No node with given id found";
1396 RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1398 *errorString = "Node with given id does not belong to the document";
1404 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result)
1406 Element* element = assertElement(errorString, nodeId);
1410 result = buildArrayForElementAttributes(element);
1413 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId)
1415 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
1416 Node* node = injectedScript.nodeForObjectId(objectId);
1418 *nodeId = pushNodePathToFrontend(node);
1424 String InspectorDOMAgent::documentURLString(Document* document)
1426 if (!document || document->url().isNull())
1428 return document->url().string();
1431 static String documentBaseURLString(Document* document)
1433 return document->completeURL("").string();
1436 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot)
1438 switch (shadowRoot->type()) {
1439 case ShadowRoot::UserAgentShadowRoot:
1440 return TypeBuilder::DOM::ShadowRootType::User_agent;
1441 case ShadowRoot::AuthorShadowRoot:
1442 return TypeBuilder::DOM::ShadowRootType::Author;
1444 ASSERT_NOT_REACHED();
1445 return TypeBuilder::DOM::ShadowRootType::User_agent;
1448 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1450 int id = bind(node, nodesMap);
1455 switch (node->nodeType()) {
1456 case Node::TEXT_NODE:
1457 case Node::COMMENT_NODE:
1458 case Node::CDATA_SECTION_NODE:
1459 nodeValue = node->nodeValue();
1460 if (nodeValue.length() > maxTextSize)
1461 nodeValue = nodeValue.left(maxTextSize) + ellipsisUChar;
1463 case Node::ATTRIBUTE_NODE:
1464 localName = node->localName();
1466 case Node::DOCUMENT_FRAGMENT_NODE:
1467 case Node::DOCUMENT_NODE:
1468 case Node::ELEMENT_NODE:
1470 nodeName = node->nodeName();
1471 localName = node->localName();
1475 RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create()
1477 .setNodeType(static_cast<int>(node->nodeType()))
1478 .setNodeName(nodeName)
1479 .setLocalName(localName)
1480 .setNodeValue(nodeValue);
1482 bool forcePushChildren = false;
1483 if (node->isElementNode()) {
1484 Element* element = toElement(node);
1485 value->setAttributes(buildArrayForElementAttributes(element));
1487 if (node->isFrameOwnerElement()) {
1488 HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node);
1489 LocalFrame* frame = (frameOwner->contentFrame() && frameOwner->contentFrame()->isLocalFrame()) ? toLocalFrame(frameOwner->contentFrame()) : 0;
1491 value->setFrameId(m_pageAgent->frameId(frame));
1492 if (Document* doc = frameOwner->contentDocument())
1493 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
1496 ElementShadow* shadow = element->shadow();
1498 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1499 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
1500 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1501 value->setShadowRoots(shadowRoots);
1502 forcePushChildren = true;
1505 if (isHTMLLinkElement(*element)) {
1506 HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
1507 if (linkElement.isImport() && linkElement.import() && innerParentNode(linkElement.import()) == linkElement)
1508 value->setImportedDocument(buildObjectForNode(linkElement.import(), 0, nodesMap));
1509 forcePushChildren = true;
1512 if (isHTMLTemplateElement(*element)) {
1513 value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(*element).content(), 0, nodesMap));
1514 forcePushChildren = true;
1517 switch (element->pseudoId()) {
1519 value->setPseudoType(TypeBuilder::DOM::PseudoType::Before);
1522 value->setPseudoType(TypeBuilder::DOM::PseudoType::After);
1525 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap);
1526 if (pseudoElements) {
1527 value->setPseudoElements(pseudoElements.release());
1528 forcePushChildren = true;
1533 } else if (node->isDocumentNode()) {
1534 Document* document = toDocument(node);
1535 value->setDocumentURL(documentURLString(document));
1536 value->setBaseURL(documentBaseURLString(document));
1537 value->setXmlVersion(document->xmlVersion());
1538 } else if (node->isDocumentTypeNode()) {
1539 DocumentType* docType = toDocumentType(node);
1540 value->setPublicId(docType->publicId());
1541 value->setSystemId(docType->systemId());
1542 value->setInternalSubset(docType->internalSubset());
1543 } else if (node->isAttributeNode()) {
1544 Attr* attribute = toAttr(node);
1545 value->setName(attribute->name());
1546 value->setValue(attribute->value());
1547 } else if (node->isShadowRoot()) {
1548 value->setShadowRootType(shadowRootType(toShadowRoot(node)));
1551 if (node->isContainerNode()) {
1552 int nodeCount = innerChildNodeCount(node);
1553 value->setChildNodeCount(nodeCount);
1554 if (nodesMap == &m_documentNodeToIdMap)
1555 m_cachedChildCount.set(id, nodeCount);
1556 if (forcePushChildren && !depth)
1558 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap);
1559 if (children->length() > 0 || depth) // Push children along with shadow in any case.
1560 value->setChildren(children.release());
1563 return value.release();
1566 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1568 RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create();
1569 // Go through all attributes and serialize them.
1570 if (!element->hasAttributes())
1571 return attributesValue.release();
1572 unsigned numAttrs = element->attributeCount();
1573 for (unsigned i = 0; i < numAttrs; ++i) {
1574 // Add attribute pair
1575 const Attribute& attribute = element->attributeItem(i);
1576 attributesValue->addItem(attribute.name().toString());
1577 attributesValue->addItem(attribute.value());
1579 return attributesValue.release();
1582 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1584 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1586 // Special-case the only text child - pretend that container's children have been requested.
1587 Node* firstChild = container->firstChild();
1588 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1589 children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1590 m_childrenRequested.add(bind(container, nodesMap));
1592 return children.release();
1595 Node* child = innerFirstChild(container);
1597 m_childrenRequested.add(bind(container, nodesMap));
1600 children->addItem(buildObjectForNode(child, depth, nodesMap));
1601 child = innerNextSibling(child);
1603 return children.release();
1606 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
1608 RefPtr<EventListener> eventListener = registeredEventListener.listener;
1612 if (!eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber))
1615 Document& document = node->document();
1616 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
1617 .setScriptId(scriptId)
1618 .setLineNumber(lineNumber);
1619 RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
1621 .setUseCapture(registeredEventListener.useCapture)
1622 .setIsAttribute(eventListener->isAttribute())
1623 .setNodeId(pushNodePathToFrontend(node))
1624 .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get()))
1625 .setLocation(location);
1626 if (objectGroupId) {
1627 ScriptValue functionValue = eventListenerHandler(&document, eventListener.get());
1628 if (!functionValue.isEmpty()) {
1629 LocalFrame* frame = document.frame();
1631 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
1633 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1634 if (!injectedScript.isEmpty()) {
1635 RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId);
1636 value->setHandler(valueJson);
1642 if (!sourceName.isEmpty())
1643 value->setSourceName(sourceName);
1644 return value.release();
1647 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap)
1649 if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER))
1652 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1653 if (element->pseudoElement(BEFORE))
1654 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap));
1655 if (element->pseudoElement(AFTER))
1656 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap));
1657 return pseudoElements.release();
1660 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1662 node = node->firstChild();
1663 while (isWhitespace(node))
1664 node = node->nextSibling();
1668 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1671 node = node->nextSibling();
1672 } while (isWhitespace(node));
1676 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1679 node = node->previousSibling();
1680 } while (isWhitespace(node));
1684 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1687 Node* child = innerFirstChild(node);
1690 child = innerNextSibling(child);
1695 Node* InspectorDOMAgent::innerParentNode(Node* node)
1697 if (node->isDocumentNode()) {
1698 Document* document = toDocument(node);
1699 if (HTMLImportLoader* loader = document->importLoader())
1700 return loader->firstImport()->link();
1701 return document->ownerElement();
1703 return node->parentOrShadowHostNode();
1706 bool InspectorDOMAgent::isWhitespace(Node* node)
1708 //TODO: pull ignoreWhitespace setting from the frontend and use here.
1709 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1712 void InspectorDOMAgent::domContentLoadedEventFired(LocalFrame* frame)
1714 if (!frame->isMainFrame())
1717 // Re-push document once it is loaded.
1718 discardFrontendBindings();
1719 if (m_state->getBoolean(DOMAgentState::documentRequested))
1720 m_frontend->documentUpdated();
1723 void InspectorDOMAgent::invalidateFrameOwnerElement(LocalFrame* frame)
1725 Element* frameOwner = frame->document()->ownerElement();
1729 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1733 // Re-add frame owner element together with its new children.
1734 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1735 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1736 unbind(frameOwner, &m_documentNodeToIdMap);
1738 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1739 Node* previousSibling = innerPreviousSibling(frameOwner);
1740 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1741 m_frontend->childNodeInserted(parentId, prevId, value.release());
1744 void InspectorDOMAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
1746 // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame()
1747 // then all we need to check here is loader->frame()->isMainFrame()
1748 // and we don't need "frame" at all.
1749 LocalFrame* mainFrame = frame->page()->mainFrame();
1750 if (loader->frame() != mainFrame) {
1751 invalidateFrameOwnerElement(loader->frame());
1755 setDocument(mainFrame->document());
1758 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1760 if (isWhitespace(node))
1763 // We could be attaching existing subtree. Forget the bindings.
1764 unbind(node, &m_documentNodeToIdMap);
1766 ContainerNode* parent = node->parentNode();
1769 int parentId = m_documentNodeToIdMap.get(parent);
1770 // Return if parent is not mapped yet.
1774 if (!m_childrenRequested.contains(parentId)) {
1775 // No children are mapped yet -> only notify on changes of child count.
1776 int count = m_cachedChildCount.get(parentId) + 1;
1777 m_cachedChildCount.set(parentId, count);
1778 m_frontend->childNodeCountUpdated(parentId, count);
1780 // Children have been requested -> return value of a new child.
1781 Node* prevSibling = innerPreviousSibling(node);
1782 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1783 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1784 m_frontend->childNodeInserted(parentId, prevId, value.release());
1788 void InspectorDOMAgent::willRemoveDOMNode(Node* node)
1790 if (isWhitespace(node))
1793 ContainerNode* parent = node->parentNode();
1795 // If parent is not mapped yet -> ignore the event.
1796 if (!m_documentNodeToIdMap.contains(parent))
1799 int parentId = m_documentNodeToIdMap.get(parent);
1801 if (!m_childrenRequested.contains(parentId)) {
1802 // No children are mapped yet -> only notify on changes of child count.
1803 int count = m_cachedChildCount.get(parentId) - 1;
1804 m_cachedChildCount.set(parentId, count);
1805 m_frontend->childNodeCountUpdated(parentId, count);
1807 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1809 unbind(node, &m_documentNodeToIdMap);
1812 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue)
1814 m_suppressAttributeModifiedEvent = (oldValue == newValue);
1817 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value)
1819 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
1820 m_suppressAttributeModifiedEvent = false;
1821 if (shouldSuppressEvent)
1824 int id = boundNodeId(element);
1825 // If node is not mapped yet -> ignore the event.
1830 m_domListener->didModifyDOMAttr(element);
1832 m_frontend->attributeModified(id, name, value);
1835 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name)
1837 int id = boundNodeId(element);
1838 // If node is not mapped yet -> ignore the event.
1843 m_domListener->didModifyDOMAttr(element);
1845 m_frontend->attributeRemoved(id, name);
1848 void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elements)
1850 RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create();
1851 for (unsigned i = 0, size = elements.size(); i < size; ++i) {
1852 Element* element = elements.at(i);
1853 int id = boundNodeId(element);
1854 // If node is not mapped yet -> ignore the event.
1859 m_domListener->didModifyDOMAttr(element);
1860 nodeIds->addItem(id);
1862 m_frontend->inlineStyleInvalidated(nodeIds.release());
1865 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1867 int id = m_documentNodeToIdMap.get(characterData);
1869 // Push text node if it is being created.
1870 didInsertDOMNode(characterData);
1873 m_frontend->characterDataModified(id, characterData->data());
1876 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1878 int id = m_documentNodeToIdMap.get(node);
1879 // If node is not mapped yet -> ignore the event.
1883 if (!m_revalidateStyleAttrTask)
1884 m_revalidateStyleAttrTask = adoptPtr(new RevalidateStyleAttributeTask(this));
1885 m_revalidateStyleAttrTask->scheduleFor(toElement(node));
1888 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root)
1890 if (!host->ownerDocument())
1893 int hostId = m_documentNodeToIdMap.get(host);
1897 pushChildNodesToFrontend(hostId, 1);
1898 m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, &m_documentNodeToIdMap));
1901 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root)
1903 if (!host->ownerDocument())
1906 int hostId = m_documentNodeToIdMap.get(host);
1907 int rootId = m_documentNodeToIdMap.get(root);
1908 if (hostId && rootId)
1909 m_frontend->shadowRootPopped(hostId, rootId);
1912 void InspectorDOMAgent::frameDocumentUpdated(LocalFrame* frame)
1914 Document* document = frame->document();
1918 Page* page = frame->page();
1920 if (frame != page->mainFrame())
1923 // Only update the main frame document, nested frame document updates are not required
1924 // (will be handled by invalidateFrameOwnerElement()).
1925 setDocument(document);
1928 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement)
1930 Element* parent = pseudoElement->parentOrShadowHostElement();
1933 int parentId = m_documentNodeToIdMap.get(parent);
1937 pushChildNodesToFrontend(parentId, 1);
1938 m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, &m_documentNodeToIdMap));
1941 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
1943 int pseudoElementId = m_documentNodeToIdMap.get(pseudoElement);
1944 if (!pseudoElementId)
1947 // If a PseudoElement is bound, its parent element must be bound, too.
1948 Element* parent = pseudoElement->parentOrShadowHostElement();
1950 int parentId = m_documentNodeToIdMap.get(parent);
1953 unbind(pseudoElement, &m_documentNodeToIdMap);
1954 m_frontend->pseudoElementRemoved(parentId, pseudoElementId);
1957 static ShadowRoot* shadowRootForNode(Node* node, const String& type)
1959 if (!node->isElementNode())
1962 return toElement(node)->shadowRoot();
1964 return toElement(node)->userAgentShadowRoot();
1968 Node* InspectorDOMAgent::nodeForPath(const String& path)
1970 // The path is of form "1,HTML,2,BODY,1,DIV" (<index> and <nodeName> interleaved).
1971 // <index> may also be "a" (author shadow root) or "u" (user-agent shadow root),
1972 // in which case <nodeName> MUST be "#document-fragment".
1976 Node* node = m_document.get();
1977 Vector<String> pathTokens;
1978 path.split(",", false, pathTokens);
1979 if (!pathTokens.size())
1981 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1982 bool success = true;
1983 String& indexValue = pathTokens[i];
1984 unsigned childNumber = indexValue.toUInt(&success);
1987 child = shadowRootForNode(node, indexValue);
1989 if (childNumber >= innerChildNodeCount(node))
1992 child = innerFirstChild(node);
1994 String childName = pathTokens[i + 1];
1995 for (size_t j = 0; child && j < childNumber; ++j)
1996 child = innerNextSibling(child);
1998 if (!child || child->nodeName() != childName)
2005 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId)
2007 if (Node* node = nodeForPath(path))
2008 *nodeId = pushNodePathToFrontend(node);
2010 *errorString = "No node with given path found";
2013 void InspectorDOMAgent::pushNodesByBackendIdsToFrontend(ErrorString* errorString, const RefPtr<JSONArray>& backendNodeIds, RefPtr<TypeBuilder::Array<int> >& result)
2015 result = TypeBuilder::Array<int>::create();
2016 for (JSONArray::const_iterator it = backendNodeIds->begin(); it != backendNodeIds->end(); ++it) {
2019 if (!(*it)->asNumber(&backendNodeId)) {
2020 *errorString = "Invalid argument type";
2024 Node* node = InspectorNodeIds::nodeForId(backendNodeId);
2025 if (node && node->document().page() == m_pageAgent->page())
2026 result->addItem(pushNodePathToFrontend(node));
2032 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId)
2034 Node* node = assertNode(errorString, nodeId);
2037 RenderObject* renderer = node->renderer();
2039 *errorString = "No renderer for node, perhaps orphan or hidden node";
2042 while (renderer && !renderer->isDocumentElement() && !renderer->isRelayoutBoundaryForInspector())
2043 renderer = renderer->container();
2044 Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument();
2045 *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode);
2048 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2050 Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument();
2051 LocalFrame* frame = document ? document->frame() : 0;
2055 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(ScriptState::forMainWorld(frame));
2056 if (injectedScript.isEmpty())
2059 return injectedScript.wrapNode(node, objectGroup);
2062 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString)
2064 if (!m_documentNodeToIdMap.contains(m_document)) {
2065 RefPtr<TypeBuilder::DOM::Node> root;
2066 getDocument(errorString, root);
2067 return errorString->isEmpty();
2072 } // namespace WebCore