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/HTMLInputElement.h"
59 #include "core/html/HTMLTemplateElement.h"
60 #include "core/inspector/DOMEditor.h"
61 #include "core/inspector/DOMPatchSupport.h"
62 #include "core/inspector/IdentifiersFactory.h"
63 #include "core/inspector/InspectorHistory.h"
64 #include "core/inspector/InspectorOverlay.h"
65 #include "core/inspector/InspectorPageAgent.h"
66 #include "core/inspector/InspectorState.h"
67 #include "core/inspector/InstrumentingAgents.h"
68 #include "core/loader/DocumentLoader.h"
69 #include "core/frame/Frame.h"
70 #include "core/page/FrameTree.h"
71 #include "core/page/Page.h"
72 #include "core/rendering/HitTestResult.h"
73 #include "core/rendering/RenderView.h"
74 #include "core/xml/DocumentXPathEvaluator.h"
75 #include "core/xml/XPathResult.h"
76 #include "platform/PlatformGestureEvent.h"
77 #include "platform/PlatformMouseEvent.h"
78 #include "platform/PlatformTouchEvent.h"
79 #include "wtf/ListHashSet.h"
80 #include "wtf/text/CString.h"
81 #include "wtf/text/WTFString.h"
85 using namespace HTMLNames;
87 namespace DOMAgentState {
88 static const char documentRequested[] = "documentRequested";
91 static const size_t maxTextSize = 10000;
92 static const UChar ellipsisUChar[] = { 0x2026, 0 };
94 static Color parseColor(const RefPtr<JSONObject>* colorObject)
96 if (!colorObject || !(*colorObject))
97 return Color::transparent;
102 bool success = (*colorObject)->getNumber("r", &r);
103 success |= (*colorObject)->getNumber("g", &g);
104 success |= (*colorObject)->getNumber("b", &b);
106 return Color::transparent;
109 success = (*colorObject)->getNumber("a", &a);
111 return Color(r, g, b);
113 // Clamp alpha to the [0..1] range.
119 return Color(r, g, b, static_cast<int>(a * 255));
122 static Color parseConfigColor(const String& fieldName, JSONObject* configObject)
124 const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName);
125 return parseColor(&colorObject);
128 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad)
132 const size_t coordinatesInQuad = 8;
133 double coordinates[coordinatesInQuad];
134 if (quadArray->length() != coordinatesInQuad)
136 for (size_t i = 0; i < coordinatesInQuad; ++i) {
137 if (!quadArray->get(i)->asNumber(coordinates + i))
140 quad->setP1(FloatPoint(coordinates[0], coordinates[1]));
141 quad->setP2(FloatPoint(coordinates[2], coordinates[3]));
142 quad->setP3(FloatPoint(coordinates[4], coordinates[5]));
143 quad->setP4(FloatPoint(coordinates[6], coordinates[7]));
148 static Node* hoveredNodeForPoint(Frame* frame, const IntPoint& point, bool ignorePointerEventsNone)
150 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent;
151 if (ignorePointerEventsNone)
152 hitType |= HitTestRequest::IgnorePointerEventsNone;
153 HitTestRequest request(hitType);
154 HitTestResult result(frame->view()->windowToContents(point));
155 frame->contentRenderer()->hitTest(request, result);
156 Node* node = result.innerPossiblyPseudoNode();
157 while (node && node->nodeType() == Node::TEXT_NODE)
158 node = node->parentNode();
162 static Node* hoveredNodeForEvent(Frame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone)
164 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
167 static Node* hoveredNodeForEvent(Frame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone)
169 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
172 static Node* hoveredNodeForEvent(Frame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone)
174 const Vector<PlatformTouchPoint>& points = event.touchPoints();
177 return hoveredNodeForPoint(frame, points[0].pos(), ignorePointerEventsNone);
180 class RevalidateStyleAttributeTask {
181 WTF_MAKE_FAST_ALLOCATED;
183 RevalidateStyleAttributeTask(InspectorDOMAgent*);
184 void scheduleFor(Element*);
185 void reset() { m_timer.stop(); }
186 void onTimer(Timer<RevalidateStyleAttributeTask>*);
189 InspectorDOMAgent* m_domAgent;
190 Timer<RevalidateStyleAttributeTask> m_timer;
191 HashSet<RefPtr<Element> > m_elements;
194 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
195 : m_domAgent(domAgent)
196 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
200 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
202 m_elements.add(element);
203 if (!m_timer.isActive())
204 m_timer.startOneShot(0);
207 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
209 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
210 Vector<Element*> elements;
211 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
212 elements.append(it->get());
213 m_domAgent->styleAttributeInvalidated(elements);
218 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState)
220 if (exceptionState.hadException())
221 return DOMException::getErrorName(exceptionState.code());
225 InspectorDOMAgent::InspectorDOMAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
226 : InspectorBaseAgent<InspectorDOMAgent>("DOM")
227 , m_pageAgent(pageAgent)
228 , m_injectedScriptManager(injectedScriptManager)
233 , m_lastBackendNodeId(-1)
234 , m_searchingForNode(NotSearching)
235 , m_suppressAttributeModifiedEvent(false)
239 InspectorDOMAgent::~InspectorDOMAgent()
242 ASSERT(m_searchingForNode == NotSearching);
245 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
248 m_history = adoptPtr(new InspectorHistory());
249 m_domEditor = adoptPtr(new DOMEditor(m_history.get()));
251 m_frontend = frontend->dom();
252 m_instrumentingAgents->setInspectorDOMAgent(this);
253 m_document = m_pageAgent->mainFrame()->document();
256 void InspectorDOMAgent::clearFrontend()
264 setSearchingForNode(&error, NotSearching, 0);
265 hideHighlight(&error);
268 m_instrumentingAgents->setInspectorDOMAgent(0);
269 m_state->setBoolean(DOMAgentState::documentRequested, false);
273 void InspectorDOMAgent::restore()
275 // Reset document to avoid early return from setDocument.
277 setDocument(m_pageAgent->mainFrame()->document());
280 Vector<Document*> InspectorDOMAgent::documents()
282 Vector<Document*> result;
283 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
284 Document* document = frame->document();
287 result.append(document);
292 void InspectorDOMAgent::reset()
294 discardFrontendBindings();
295 discardBackendBindings();
299 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
301 m_domListener = listener;
304 void InspectorDOMAgent::setDocument(Document* doc)
306 if (doc == m_document.get())
313 if (!m_state->getBoolean(DOMAgentState::documentRequested))
316 // Immediately communicate 0 document or document that has finished loading.
317 if (!doc || !doc->parsing())
318 m_frontend->documentUpdated();
321 void InspectorDOMAgent::releaseDanglingNodes()
323 m_danglingNodeToIdMaps.clear();
326 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
328 int id = nodesMap->get(node);
332 nodesMap->set(node, id);
333 m_idToNode.set(id, node);
334 m_idToNodesMap.set(id, nodesMap);
338 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
340 int id = nodesMap->get(node);
344 m_idToNode.remove(id);
346 if (node->isFrameOwnerElement()) {
347 Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument();
349 m_domListener->didRemoveDocument(contentDocument);
351 unbind(contentDocument, nodesMap);
354 for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot())
355 unbind(root, nodesMap);
357 if (node->isElementNode()) {
358 Element* element = toElement(node);
359 if (element->pseudoElement(BEFORE))
360 unbind(element->pseudoElement(BEFORE), nodesMap);
361 if (element->pseudoElement(AFTER))
362 unbind(element->pseudoElement(AFTER), nodesMap);
365 nodesMap->remove(node);
367 m_domListener->didRemoveDOMNode(node);
369 bool childrenRequested = m_childrenRequested.contains(id);
370 if (childrenRequested) {
371 // Unbind subtree known to client recursively.
372 m_childrenRequested.remove(id);
373 Node* child = innerFirstChild(node);
375 unbind(child, nodesMap);
376 child = innerNextSibling(child);
381 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
383 Node* node = nodeForId(nodeId);
385 *errorString = "Could not find node with given id";
391 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId)
393 Node* node = assertNode(errorString, nodeId);
397 if (!(node->isDocumentNode())) {
398 *errorString = "Document is not available";
401 return toDocument(node);
404 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
406 Node* node = assertNode(errorString, nodeId);
410 if (node->nodeType() != Node::ELEMENT_NODE) {
411 *errorString = "Node is not an Element";
414 return toElement(node);
417 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId)
419 Node* node = assertNode(errorString, nodeId);
423 if (node->isInShadowTree()) {
424 *errorString = "Cannot edit nodes from shadow trees";
428 if (node->isPseudoElement()) {
429 *errorString = "Cannot edit pseudo elements";
436 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId)
438 Element* element = assertElement(errorString, nodeId);
442 if (element->isInShadowTree()) {
443 *errorString = "Cannot edit elements from shadow trees";
447 if (element->isPseudoElement()) {
448 *errorString = "Cannot edit pseudo elements";
455 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root)
457 m_state->setBoolean(DOMAgentState::documentRequested, true);
460 *errorString = "Document is not available";
464 discardFrontendBindings();
466 root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
469 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
471 Node* node = nodeForId(nodeId);
472 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
475 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
477 if (m_childrenRequested.contains(nodeId)) {
483 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
484 int childNodeId = nodeMap->get(node);
486 pushChildNodesToFrontend(childNodeId, depth);
492 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap);
493 m_frontend->setChildNodes(nodeId, children.release());
496 void InspectorDOMAgent::discardFrontendBindings()
500 m_searchResults.clear();
501 m_documentNodeToIdMap.clear();
503 releaseDanglingNodes();
504 m_childrenRequested.clear();
505 if (m_revalidateStyleAttrTask)
506 m_revalidateStyleAttrTask->reset();
509 void InspectorDOMAgent::discardBackendBindings()
511 m_backendIdToNode.clear();
512 m_nodeGroupToBackendIdMap.clear();
515 int InspectorDOMAgent::pushNodeToFrontend(ErrorString* errorString, int documentNodeId, Node* nodeToPush)
517 Document* document = assertDocument(errorString, documentNodeId);
520 if (nodeToPush->document() != document) {
521 *errorString = "Node is not part of the document with given id";
525 return pushNodePathToFrontend(nodeToPush);
528 Node* InspectorDOMAgent::nodeForId(int id)
533 HashMap<int, Node*>::iterator it = m_idToNode.find(id);
534 if (it != m_idToNode.end())
539 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth)
545 else if (*depth == -1)
546 sanitizedDepth = INT_MAX;
548 sanitizedDepth = *depth;
550 *errorString = "Please provide a positive integer as a depth or -1 for entire subtree";
554 pushChildNodesToFrontend(nodeId, sanitizedDepth);
557 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
560 Node* node = assertNode(errorString, nodeId);
561 if (!node || !node->isContainerNode())
564 TrackExceptionState exceptionState;
565 RefPtr<Element> element = toContainerNode(node)->querySelector(AtomicString(selectors), exceptionState);
566 if (exceptionState.hadException()) {
567 *errorString = "DOM Error while querying";
572 *elementId = pushNodePathToFrontend(element.get());
575 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result)
577 Node* node = assertNode(errorString, nodeId);
578 if (!node || !node->isContainerNode())
581 TrackExceptionState exceptionState;
582 RefPtr<NodeList> nodes = toContainerNode(node)->querySelectorAll(AtomicString(selectors), exceptionState);
583 if (exceptionState.hadException()) {
584 *errorString = "DOM Error while querying";
588 result = TypeBuilder::Array<int>::create();
590 for (unsigned i = 0; i < nodes->length(); ++i)
591 result->addItem(pushNodePathToFrontend(nodes->item(i)));
594 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
596 ASSERT(nodeToPush); // Invalid input
600 if (!m_documentNodeToIdMap.contains(m_document))
603 // Return id in case the node is known.
604 int result = m_documentNodeToIdMap.get(nodeToPush);
608 Node* node = nodeToPush;
610 NodeToIdMap* danglingMap = 0;
613 Node* parent = innerParentNode(node);
615 // Node being pushed is detached -> push subtree root.
616 OwnPtr<NodeToIdMap> newMap = adoptPtr(new NodeToIdMap);
617 danglingMap = newMap.get();
618 m_danglingNodeToIdMaps.append(newMap.release());
619 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
620 children->addItem(buildObjectForNode(node, 0, danglingMap));
621 m_frontend->setChildNodes(0, children);
625 if (m_documentNodeToIdMap.get(parent))
632 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
633 for (int i = path.size() - 1; i >= 0; --i) {
634 int nodeId = map->get(path.at(i));
636 pushChildNodesToFrontend(nodeId);
638 return map->get(nodeToPush);
641 int InspectorDOMAgent::boundNodeId(Node* node)
643 return m_documentNodeToIdMap.get(node);
646 BackendNodeId InspectorDOMAgent::backendNodeIdForNode(Node* node, const String& nodeGroup)
651 if (!m_nodeGroupToBackendIdMap.contains(nodeGroup))
652 m_nodeGroupToBackendIdMap.set(nodeGroup, NodeToBackendIdMap());
654 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value;
655 BackendNodeId id = map.get(node);
657 id = --m_lastBackendNodeId;
659 m_backendIdToNode.set(id, std::make_pair(node, nodeGroup));
665 void InspectorDOMAgent::releaseBackendNodeIds(ErrorString* errorString, const String& nodeGroup)
667 if (m_nodeGroupToBackendIdMap.contains(nodeGroup)) {
668 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value;
669 for (NodeToBackendIdMap::iterator it = map.begin(); it != map.end(); ++it)
670 m_backendIdToNode.remove(it->value);
671 m_nodeGroupToBackendIdMap.remove(nodeGroup);
674 *errorString = "Group name not found";
677 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value)
679 Element* element = assertEditableElement(errorString, elementId);
683 m_domEditor->setAttribute(element, name, value, errorString);
686 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
688 Element* element = assertEditableElement(errorString, elementId);
692 String markup = "<span " + text + "></span>";
693 RefPtr<DocumentFragment> fragment = element->document().createDocumentFragment();
695 bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement();
696 // Not all elements can represent the context (i.e. IFRAME), hence using document.body.
697 if (shouldIgnoreCase && element->document().body())
698 fragment->parseHTML(markup, element->document().body(), DisallowScriptingContent);
700 fragment->parseXML(markup, 0, DisallowScriptingContent);
702 Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0;
703 if (!parsedElement) {
704 *errorString = "Could not parse value as attributes";
708 String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String();
710 if (!parsedElement->hasAttributes() && name) {
711 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
715 bool foundOriginalAttribute = false;
716 unsigned numAttrs = parsedElement->attributeCount();
717 for (unsigned i = 0; i < numAttrs; ++i) {
718 // Add attribute pair
719 const Attribute* attribute = parsedElement->attributeItem(i);
720 String attributeName = attribute->name().toString();
721 if (shouldIgnoreCase)
722 attributeName = attributeName.lower();
723 foundOriginalAttribute |= name && attributeName == caseAdjustedName;
724 if (!m_domEditor->setAttribute(element, attributeName, attribute->value(), errorString))
728 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
729 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
732 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
734 Element* element = assertEditableElement(errorString, elementId);
738 m_domEditor->removeAttribute(element, name, errorString);
741 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
743 Node* node = assertEditableNode(errorString, nodeId);
747 ContainerNode* parentNode = node->parentNode();
749 *errorString = "Cannot remove detached node";
753 m_domEditor->removeChild(parentNode, node, errorString);
756 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
760 Node* oldNode = nodeForId(nodeId);
761 if (!oldNode || !oldNode->isElementNode())
764 TrackExceptionState exceptionState;
765 RefPtr<Element> newElem = oldNode->document().createElement(AtomicString(tagName), exceptionState);
766 if (exceptionState.hadException())
769 // Copy over the original node's attributes.
770 newElem->cloneAttributesFromElement(*toElement(oldNode));
772 // Copy over the original node's children.
774 while ((child = oldNode->firstChild())) {
775 if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
779 // Replace the old node with the new node
780 ContainerNode* parent = oldNode->parentNode();
781 if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
783 if (!m_domEditor->removeChild(parent, oldNode, errorString))
786 *newId = pushNodePathToFrontend(newElem.get());
787 if (m_childrenRequested.contains(nodeId))
788 pushChildNodesToFrontend(*newId);
791 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
793 Node* node = assertNode(errorString, nodeId);
797 *outerHTML = createMarkup(node);
800 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
804 DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get());
805 domPatchSupport.patchDocument(outerHTML);
809 Node* node = assertEditableNode(errorString, nodeId);
813 Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument();
814 if (!document || (!document->isHTMLDocument() && !document->isXMLDocument())) {
815 *errorString = "Not an HTML/XML document";
820 if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString))
824 // The only child node has been deleted.
828 int newId = pushNodePathToFrontend(newNode);
830 bool childrenRequested = m_childrenRequested.contains(nodeId);
831 if (childrenRequested)
832 pushChildNodesToFrontend(newId);
835 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
837 Node* node = assertEditableNode(errorString, nodeId);
841 if (node->nodeType() != Node::TEXT_NODE) {
842 *errorString = "Can only set value of text nodes";
846 m_domEditor->replaceWholeText(toText(node), value, errorString);
849 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
851 listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
852 Node* node = assertNode(errorString, nodeId);
855 Vector<EventListenerInfo> eventInformation;
856 getEventListeners(node, eventInformation, true);
858 // Get Capturing Listeners (in this order)
859 size_t eventInformationLength = eventInformation.size();
860 for (size_t i = 0; i < eventInformationLength; ++i) {
861 const EventListenerInfo& info = eventInformation[i];
862 const EventListenerVector& vector = info.eventListenerVector;
863 for (size_t j = 0; j < vector.size(); ++j) {
864 const RegisteredEventListener& listener = vector[j];
865 if (listener.useCapture)
866 listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup));
870 // Get Bubbling Listeners (reverse order)
871 for (size_t i = eventInformationLength; i; --i) {
872 const EventListenerInfo& info = eventInformation[i - 1];
873 const EventListenerVector& vector = info.eventListenerVector;
874 for (size_t j = 0; j < vector.size(); ++j) {
875 const RegisteredEventListener& listener = vector[j];
876 if (!listener.useCapture)
877 listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup));
882 void InspectorDOMAgent::getEventListeners(EventTarget* target, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
884 // The Node's Ancestors including self.
885 Vector<EventTarget*> ancestors;
886 ancestors.append(target);
887 if (includeAncestors) {
888 Node* node = target->toNode();
889 for (ContainerNode* ancestor = node ? node->parentOrShadowHostNode() : 0; ancestor; ancestor = ancestor->parentOrShadowHostNode())
890 ancestors.append(ancestor);
893 // Nodes and their Listeners for the concerned event types (order is top to bottom)
894 for (size_t i = ancestors.size(); i; --i) {
895 EventTarget* ancestor = ancestors[i - 1];
896 Vector<AtomicString> eventTypes = ancestor->eventTypes();
897 for (size_t j = 0; j < eventTypes.size(); ++j) {
898 AtomicString& type = eventTypes[j];
899 const EventListenerVector& listeners = ancestor->getEventListeners(type);
900 EventListenerVector filteredListeners;
901 filteredListeners.reserveCapacity(listeners.size());
902 for (size_t k = 0; k < listeners.size(); ++k) {
903 if (listeners[k].listener->type() == EventListener::JSEventListenerType)
904 filteredListeners.append(listeners[k]);
906 if (!filteredListeners.isEmpty())
907 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners));
912 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount)
914 // FIXME: Few things are missing here:
915 // 1) Search works with node granularity - number of matches within node is not calculated.
916 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
919 unsigned queryLength = whitespaceTrimmedQuery.length();
920 bool startTagFound = !whitespaceTrimmedQuery.find('<');
921 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
922 bool startQuoteFound = !whitespaceTrimmedQuery.find('"');
923 bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength;
924 bool exactAttributeMatch = startQuoteFound && endQuoteFound;
926 String tagNameQuery = whitespaceTrimmedQuery;
927 String attributeQuery = whitespaceTrimmedQuery;
929 tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
931 tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
933 attributeQuery = attributeQuery.right(attributeQuery.length() - 1);
935 attributeQuery = attributeQuery.left(attributeQuery.length() - 1);
937 Vector<Document*> docs = documents();
938 ListHashSet<Node*> resultCollector;
940 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
941 Document* document = *it;
942 Node* node = document->documentElement();
946 // Manual plain text search.
947 while ((node = NodeTraversal::next(*node, document->documentElement()))) {
948 switch (node->nodeType()) {
949 case Node::TEXT_NODE:
950 case Node::COMMENT_NODE:
951 case Node::CDATA_SECTION_NODE: {
952 String text = node->nodeValue();
953 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound)
954 resultCollector.add(node);
957 case Node::ELEMENT_NODE: {
958 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound))
959 || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery))
960 || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false))
961 || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) {
962 resultCollector.add(node);
965 // Go through all attributes and serialize them.
966 const Element* element = toElement(node);
967 if (!element->hasAttributes())
970 unsigned numAttrs = element->attributeCount();
971 for (unsigned i = 0; i < numAttrs; ++i) {
972 // Add attribute pair
973 const Attribute* attribute = element->attributeItem(i);
974 if (attribute->localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) {
975 resultCollector.add(node);
978 size_t foundPosition = attribute->value().find(attributeQuery, 0, false);
979 if (foundPosition != kNotFound) {
980 if (!exactAttributeMatch || (!foundPosition && attribute->value().length() == attributeQuery.length())) {
981 resultCollector.add(node);
994 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
995 Document* document = *it;
996 TrackExceptionState exceptionState;
997 RefPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(document, whitespaceTrimmedQuery, document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState);
998 if (exceptionState.hadException() || !result)
1001 unsigned long size = result->snapshotLength(exceptionState);
1002 for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) {
1003 Node* node = result->snapshotItem(i, exceptionState);
1004 if (exceptionState.hadException())
1007 if (node->nodeType() == Node::ATTRIBUTE_NODE)
1008 node = toAttr(node)->ownerElement();
1009 resultCollector.add(node);
1013 // Selector evaluation
1014 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
1015 Document* document = *it;
1016 TrackExceptionState exceptionState;
1017 RefPtr<NodeList> nodeList = document->querySelectorAll(AtomicString(whitespaceTrimmedQuery), exceptionState);
1018 if (exceptionState.hadException() || !nodeList)
1021 unsigned size = nodeList->length();
1022 for (unsigned i = 0; i < size; ++i)
1023 resultCollector.add(nodeList->item(i));
1027 *searchId = IdentifiersFactory::createIdentifier();
1028 SearchResults::iterator resultsIt = m_searchResults.add(*searchId, Vector<RefPtr<Node> >()).iterator;
1030 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
1031 resultsIt->value.append(*it);
1033 *resultCount = resultsIt->value.size();
1036 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds)
1038 SearchResults::iterator it = m_searchResults.find(searchId);
1039 if (it == m_searchResults.end()) {
1040 *errorString = "No search session with given id found";
1044 int size = it->value.size();
1045 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1046 *errorString = "Invalid search result range";
1050 nodeIds = TypeBuilder::Array<int>::create();
1051 for (int i = fromIndex; i < toIndex; ++i)
1052 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1055 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
1057 m_searchResults.remove(searchId);
1060 bool InspectorDOMAgent::handleMousePress()
1062 if (m_searchingForNode == NotSearching)
1065 if (Node* node = m_overlay->highlightedNode()) {
1072 bool InspectorDOMAgent::handleGestureEvent(Frame* frame, const PlatformGestureEvent& event)
1074 if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap)
1076 Node* node = hoveredNodeForEvent(frame, event, false);
1077 if (node && m_inspectModeHighlightConfig) {
1078 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig);
1085 bool InspectorDOMAgent::handleTouchEvent(Frame* frame, const PlatformTouchEvent& event)
1087 if (m_searchingForNode == NotSearching)
1089 Node* node = hoveredNodeForEvent(frame, event, false);
1090 if (node && m_inspectModeHighlightConfig) {
1091 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig);
1098 void InspectorDOMAgent::inspect(Node* inspectedNode)
1100 if (!m_frontend || !inspectedNode)
1103 Node* node = inspectedNode;
1104 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
1105 node = node->parentNode();
1107 int nodeId = pushNodePathToFrontend(node);
1109 m_frontend->inspectNodeRequested(nodeId);
1112 void InspectorDOMAgent::handleMouseMove(Frame* frame, const PlatformMouseEvent& event)
1114 if (m_searchingForNode == NotSearching)
1117 if (!frame->view() || !frame->contentRenderer())
1119 Node* node = hoveredNodeForEvent(frame, event, event.shiftKey());
1121 while (m_searchingForNode != SearchingForShadow && node && node->isInShadowTree())
1122 node = node->parentOrShadowHostNode();
1124 Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0;
1125 if (eventTarget == node)
1128 if (node && m_inspectModeHighlightConfig)
1129 m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig);
1132 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject)
1134 if (m_searchingForNode == searchMode)
1137 m_searchingForNode = searchMode;
1138 m_overlay->setInspectModeEnabled(searchMode != NotSearching);
1139 if (searchMode != NotSearching) {
1140 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1141 if (!m_inspectModeHighlightConfig)
1144 hideHighlight(errorString);
1147 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject)
1149 if (!highlightInspectorObject) {
1150 *errorString = "Internal error: highlight configuration parameter is missing";
1154 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1155 bool showInfo = false; // Default: false (do not show a tooltip).
1156 highlightInspectorObject->getBoolean("showInfo", &showInfo);
1157 highlightConfig->showInfo = showInfo;
1158 bool showRulers = false; // Default: false (do not show rulers).
1159 highlightInspectorObject->getBoolean("showRulers", &showRulers);
1160 highlightConfig->showRulers = showRulers;
1161 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1162 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1163 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1164 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1165 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1166 highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject);
1167 return highlightConfig.release();
1170 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectShadowDOM, const RefPtr<JSONObject>* highlightConfig)
1172 if (enabled && !pushDocumentUponHandlelessOperation(errorString))
1174 SearchMode searchMode = enabled ? (inspectShadowDOM && *inspectShadowDOM ? SearchingForShadow : SearchingForNormal) : NotSearching;
1175 setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0);
1178 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1180 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height)));
1181 innerHighlightQuad(quad.release(), color, outlineColor);
1184 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1186 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad());
1187 if (!parseQuad(quadArray, quad.get())) {
1188 *errorString = "Invalid Quad format";
1191 innerHighlightQuad(quad.release(), color, outlineColor);
1194 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1196 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1197 highlightConfig->content = parseColor(color);
1198 highlightConfig->contentOutline = parseColor(outlineColor);
1199 m_overlay->highlightQuad(quad, *highlightConfig);
1202 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId)
1206 node = assertNode(errorString, *nodeId);
1207 } else if (objectId) {
1208 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId);
1209 node = injectedScript.nodeForObjectId(*objectId);
1211 *errorString = "Node for given objectId not found";
1213 *errorString = "Either nodeId or objectId must be specified";
1218 OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get());
1219 if (!highlightConfig)
1222 m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig);
1225 void InspectorDOMAgent::highlightFrame(
1227 const String& frameId,
1228 const RefPtr<JSONObject>* color,
1229 const RefPtr<JSONObject>* outlineColor)
1231 Frame* frame = m_pageAgent->frameForId(frameId);
1232 if (frame && frame->ownerElement()) {
1233 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1234 highlightConfig->showInfo = true; // Always show tooltips for frames.
1235 highlightConfig->content = parseColor(color);
1236 highlightConfig->contentOutline = parseColor(outlineColor);
1237 m_overlay->highlightNode(frame->ownerElement(), 0 /* eventTarget */, *highlightConfig);
1241 void InspectorDOMAgent::hideHighlight(ErrorString*)
1243 m_overlay->hideHighlight();
1246 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1248 Node* node = assertEditableNode(errorString, nodeId);
1252 Element* targetElement = assertEditableElement(errorString, targetElementId);
1256 Node* anchorNode = 0;
1257 if (anchorNodeId && *anchorNodeId) {
1258 anchorNode = assertEditableNode(errorString, *anchorNodeId);
1261 if (anchorNode->parentNode() != targetElement) {
1262 *errorString = "Anchor node must be child of the target element";
1267 if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString))
1270 *newNodeId = pushNodePathToFrontend(node);
1273 void InspectorDOMAgent::undo(ErrorString* errorString)
1275 TrackExceptionState exceptionState;
1276 m_history->undo(exceptionState);
1277 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1280 void InspectorDOMAgent::redo(ErrorString* errorString)
1282 TrackExceptionState exceptionState;
1283 m_history->redo(exceptionState);
1284 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1287 void InspectorDOMAgent::markUndoableState(ErrorString*)
1289 m_history->markUndoableState();
1292 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId)
1294 Element* element = assertElement(errorString, nodeId);
1298 element->document().updateLayoutIgnorePendingStylesheets();
1299 if (!element->isFocusable()) {
1300 *errorString = "Element is not focusable";
1306 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files)
1308 Node* node = assertNode(errorString, nodeId);
1311 if (!node->hasTagName(inputTag) || !toHTMLInputElement(node)->isFileUpload()) {
1312 *errorString = "Node is not a file input element";
1316 RefPtr<FileList> fileList = FileList::create();
1317 for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) {
1319 if (!(*iter)->asString(&path)) {
1320 *errorString = "Files must be strings";
1323 fileList->append(File::create(path));
1325 toHTMLInputElement(node)->setFiles(fileList);
1328 static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad)
1330 RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create();
1331 array->addItem(quad.p1().x());
1332 array->addItem(quad.p1().y());
1333 array->addItem(quad.p2().x());
1334 array->addItem(quad.p2().y());
1335 array->addItem(quad.p3().x());
1336 array->addItem(quad.p3().y());
1337 array->addItem(quad.p4().x());
1338 array->addItem(quad.p4().y());
1339 return array.release();
1342 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model)
1344 Node* node = assertNode(errorString, nodeId);
1348 Vector<FloatQuad> quads;
1349 bool isInlineOrBox = m_overlay->getBoxModel(node, &quads);
1350 if (!isInlineOrBox) {
1351 *errorString = "Could not compute box model.";
1355 RenderObject* renderer = node->renderer();
1356 Frame* frame = node->document().frame();
1357 FrameView* view = frame->view();
1359 IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect()));
1360 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0;
1362 model = TypeBuilder::DOM::BoxModel::create()
1363 .setContent(buildArrayForQuad(quads.at(3)))
1364 .setPadding(buildArrayForQuad(quads.at(2)))
1365 .setBorder(buildArrayForQuad(quads.at(1)))
1366 .setMargin(buildArrayForQuad(quads.at(0)))
1367 .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())
1368 .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height());
1371 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId)
1373 if (!pushDocumentUponHandlelessOperation(errorString))
1376 Node* node = hoveredNodeForPoint(m_document->frame(), IntPoint(x, y), false);
1378 *errorString = "No node found at given location";
1381 *nodeId = pushNodePathToFrontend(node);
1384 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
1386 String objectGroupName = objectGroup ? *objectGroup : "";
1387 Node* node = nodeForId(nodeId);
1389 *errorString = "No node with given id found";
1392 RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1394 *errorString = "Node with given id does not belong to the document";
1400 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result)
1402 Element* element = assertElement(errorString, nodeId);
1406 result = buildArrayForElementAttributes(element);
1409 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId)
1411 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
1412 Node* node = injectedScript.nodeForObjectId(objectId);
1414 *nodeId = pushNodePathToFrontend(node);
1420 String InspectorDOMAgent::documentURLString(Document* document)
1422 if (!document || document->url().isNull())
1424 return document->url().string();
1427 static String documentBaseURLString(Document* document)
1429 return document->completeURL("").string();
1432 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot)
1434 switch (shadowRoot->type()) {
1435 case ShadowRoot::UserAgentShadowRoot:
1436 return TypeBuilder::DOM::ShadowRootType::User_agent;
1437 case ShadowRoot::AuthorShadowRoot:
1438 return TypeBuilder::DOM::ShadowRootType::Author;
1440 ASSERT_NOT_REACHED();
1441 return TypeBuilder::DOM::ShadowRootType::User_agent;
1444 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1446 int id = bind(node, nodesMap);
1451 switch (node->nodeType()) {
1452 case Node::TEXT_NODE:
1453 case Node::COMMENT_NODE:
1454 case Node::CDATA_SECTION_NODE:
1455 nodeValue = node->nodeValue();
1456 if (nodeValue.length() > maxTextSize)
1457 nodeValue = nodeValue.left(maxTextSize) + ellipsisUChar;
1459 case Node::ATTRIBUTE_NODE:
1460 localName = node->localName();
1462 case Node::DOCUMENT_FRAGMENT_NODE:
1463 case Node::DOCUMENT_NODE:
1464 case Node::ELEMENT_NODE:
1466 nodeName = node->nodeName();
1467 localName = node->localName();
1471 RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create()
1473 .setNodeType(static_cast<int>(node->nodeType()))
1474 .setNodeName(nodeName)
1475 .setLocalName(localName)
1476 .setNodeValue(nodeValue);
1478 bool forcePushChildren = false;
1479 if (node->isElementNode()) {
1480 Element* element = toElement(node);
1481 value->setAttributes(buildArrayForElementAttributes(element));
1482 if (node->isFrameOwnerElement()) {
1483 HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node);
1484 if (Frame* frame = frameOwner->contentFrame())
1485 value->setFrameId(m_pageAgent->frameId(frame));
1486 if (Document* doc = frameOwner->contentDocument())
1487 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
1490 ElementShadow* shadow = element->shadow();
1492 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1493 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
1494 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1495 value->setShadowRoots(shadowRoots);
1496 forcePushChildren = true;
1499 if (element->hasTagName(templateTag)) {
1500 value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(element)->content(), 0, nodesMap));
1501 forcePushChildren = true;
1504 switch (element->pseudoId()) {
1506 value->setPseudoType(TypeBuilder::DOM::PseudoType::Before);
1509 value->setPseudoType(TypeBuilder::DOM::PseudoType::After);
1512 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap);
1513 if (pseudoElements) {
1514 value->setPseudoElements(pseudoElements.release());
1515 forcePushChildren = true;
1520 } else if (node->isDocumentNode()) {
1521 Document* document = toDocument(node);
1522 value->setDocumentURL(documentURLString(document));
1523 value->setBaseURL(documentBaseURLString(document));
1524 value->setXmlVersion(document->xmlVersion());
1525 } else if (node->isDocumentTypeNode()) {
1526 DocumentType* docType = toDocumentType(node);
1527 value->setPublicId(docType->publicId());
1528 value->setSystemId(docType->systemId());
1529 value->setInternalSubset(docType->internalSubset());
1530 } else if (node->isAttributeNode()) {
1531 Attr* attribute = toAttr(node);
1532 value->setName(attribute->name());
1533 value->setValue(attribute->value());
1534 } else if (node->isShadowRoot()) {
1535 value->setShadowRootType(shadowRootType(toShadowRoot(node)));
1538 if (node->isContainerNode()) {
1539 int nodeCount = innerChildNodeCount(node);
1540 value->setChildNodeCount(nodeCount);
1541 if (forcePushChildren && !depth)
1543 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap);
1544 if (children->length() > 0 || depth) // Push children along with shadow in any case.
1545 value->setChildren(children.release());
1548 return value.release();
1551 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1553 RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create();
1554 // Go through all attributes and serialize them.
1555 if (!element->hasAttributes())
1556 return attributesValue.release();
1557 unsigned numAttrs = element->attributeCount();
1558 for (unsigned i = 0; i < numAttrs; ++i) {
1559 // Add attribute pair
1560 const Attribute* attribute = element->attributeItem(i);
1561 attributesValue->addItem(attribute->name().toString());
1562 attributesValue->addItem(attribute->value());
1564 return attributesValue.release();
1567 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1569 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1571 // Special-case the only text child - pretend that container's children have been requested.
1572 Node* firstChild = container->firstChild();
1573 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1574 children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1575 m_childrenRequested.add(bind(container, nodesMap));
1577 return children.release();
1580 Node* child = innerFirstChild(container);
1582 m_childrenRequested.add(bind(container, nodesMap));
1585 children->addItem(buildObjectForNode(child, depth, nodesMap));
1586 child = innerNextSibling(child);
1588 return children.release();
1591 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
1593 RefPtr<EventListener> eventListener = registeredEventListener.listener;
1594 Document& document = node->document();
1595 RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
1597 .setUseCapture(registeredEventListener.useCapture)
1598 .setIsAttribute(eventListener->isAttribute())
1599 .setNodeId(pushNodePathToFrontend(node))
1600 .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get()));
1601 if (objectGroupId) {
1602 ScriptValue functionValue = eventListenerHandler(&document, eventListener.get());
1603 if (!functionValue.hasNoValue()) {
1604 Frame* frame = document.frame();
1606 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
1608 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1609 if (!injectedScript.hasNoValue()) {
1610 RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId);
1611 value->setHandler(valueJson);
1620 if (eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber)) {
1621 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
1622 .setScriptId(scriptId)
1623 .setLineNumber(lineNumber);
1624 value->setLocation(location);
1625 if (!sourceName.isEmpty())
1626 value->setSourceName(sourceName);
1628 return value.release();
1631 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap)
1633 if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER))
1636 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1637 if (element->pseudoElement(BEFORE))
1638 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap));
1639 if (element->pseudoElement(AFTER))
1640 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap));
1641 return pseudoElements.release();
1644 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1646 node = node->firstChild();
1647 while (isWhitespace(node))
1648 node = node->nextSibling();
1652 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1655 node = node->nextSibling();
1656 } while (isWhitespace(node));
1660 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1663 node = node->previousSibling();
1664 } while (isWhitespace(node));
1668 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1671 Node* child = innerFirstChild(node);
1674 child = innerNextSibling(child);
1679 Node* InspectorDOMAgent::innerParentNode(Node* node)
1681 if (node->isDocumentNode()) {
1682 Document* document = toDocument(node);
1683 return document->ownerElement();
1685 return node->parentOrShadowHostNode();
1688 bool InspectorDOMAgent::isWhitespace(Node* node)
1690 //TODO: pull ignoreWhitespace setting from the frontend and use here.
1691 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1694 void InspectorDOMAgent::domContentLoadedEventFired(Frame* frame)
1696 if (!frame->isMainFrame())
1699 // Re-push document once it is loaded.
1700 discardFrontendBindings();
1701 if (m_state->getBoolean(DOMAgentState::documentRequested))
1702 m_frontend->documentUpdated();
1705 void InspectorDOMAgent::invalidateFrameOwnerElement(Frame* frame)
1707 Element* frameOwner = frame->document()->ownerElement();
1711 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1715 // Re-add frame owner element together with its new children.
1716 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1717 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1718 unbind(frameOwner, &m_documentNodeToIdMap);
1720 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1721 Node* previousSibling = innerPreviousSibling(frameOwner);
1722 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1723 m_frontend->childNodeInserted(parentId, prevId, value.release());
1726 void InspectorDOMAgent::didCommitLoad(Frame* frame, DocumentLoader* loader)
1728 // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame()
1729 // then all we need to check here is loader->frame()->isMainFrame()
1730 // and we don't need "frame" at all.
1731 Frame* mainFrame = frame->page()->mainFrame();
1732 if (loader->frame() != mainFrame) {
1733 invalidateFrameOwnerElement(loader->frame());
1737 setDocument(mainFrame->document());
1740 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1742 if (isWhitespace(node))
1745 // We could be attaching existing subtree. Forget the bindings.
1746 unbind(node, &m_documentNodeToIdMap);
1748 ContainerNode* parent = node->parentNode();
1752 int parentId = m_documentNodeToIdMap.get(parent);
1753 // Return if parent is not mapped yet.
1757 if (!m_childrenRequested.contains(parentId)) {
1758 // No children are mapped yet -> only notify on changes of hasChildren.
1759 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1761 // Children have been requested -> return value of a new child.
1762 Node* prevSibling = innerPreviousSibling(node);
1763 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1764 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1765 m_frontend->childNodeInserted(parentId, prevId, value.release());
1769 void InspectorDOMAgent::willRemoveDOMNode(Node* node)
1771 if (isWhitespace(node))
1774 ContainerNode* parent = node->parentNode();
1776 // If parent is not mapped yet -> ignore the event.
1777 if (!m_documentNodeToIdMap.contains(parent))
1780 int parentId = m_documentNodeToIdMap.get(parent);
1782 if (!m_childrenRequested.contains(parentId)) {
1783 // No children are mapped yet -> only notify on changes of hasChildren.
1784 if (innerChildNodeCount(parent) == 1)
1785 m_frontend->childNodeCountUpdated(parentId, 0);
1787 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1788 unbind(node, &m_documentNodeToIdMap);
1791 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue)
1793 m_suppressAttributeModifiedEvent = (oldValue == newValue);
1796 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value)
1798 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
1799 m_suppressAttributeModifiedEvent = false;
1800 if (shouldSuppressEvent)
1803 int id = boundNodeId(element);
1804 // If node is not mapped yet -> ignore the event.
1809 m_domListener->didModifyDOMAttr(element);
1811 m_frontend->attributeModified(id, name, value);
1814 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name)
1816 int id = boundNodeId(element);
1817 // If node is not mapped yet -> ignore the event.
1822 m_domListener->didModifyDOMAttr(element);
1824 m_frontend->attributeRemoved(id, name);
1827 void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elements)
1829 RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create();
1830 for (unsigned i = 0, size = elements.size(); i < size; ++i) {
1831 Element* element = elements.at(i);
1832 int id = boundNodeId(element);
1833 // If node is not mapped yet -> ignore the event.
1838 m_domListener->didModifyDOMAttr(element);
1839 nodeIds->addItem(id);
1841 m_frontend->inlineStyleInvalidated(nodeIds.release());
1844 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1846 int id = m_documentNodeToIdMap.get(characterData);
1848 // Push text node if it is being created.
1849 didInsertDOMNode(characterData);
1852 m_frontend->characterDataModified(id, characterData->data());
1855 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1857 int id = m_documentNodeToIdMap.get(node);
1858 // If node is not mapped yet -> ignore the event.
1862 if (!m_revalidateStyleAttrTask)
1863 m_revalidateStyleAttrTask = adoptPtr(new RevalidateStyleAttributeTask(this));
1864 m_revalidateStyleAttrTask->scheduleFor(toElement(node));
1867 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root)
1869 if (!host->ownerDocument())
1872 int hostId = m_documentNodeToIdMap.get(host);
1876 pushChildNodesToFrontend(hostId, 1);
1877 m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, &m_documentNodeToIdMap));
1880 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root)
1882 if (!host->ownerDocument())
1885 int hostId = m_documentNodeToIdMap.get(host);
1886 int rootId = m_documentNodeToIdMap.get(root);
1887 if (hostId && rootId)
1888 m_frontend->shadowRootPopped(hostId, rootId);
1891 void InspectorDOMAgent::frameDocumentUpdated(Frame* frame)
1893 Document* document = frame->document();
1897 Page* page = frame->page();
1899 if (frame != page->mainFrame())
1902 // Only update the main frame document, nested frame document updates are not required
1903 // (will be handled by invalidateFrameOwnerElement()).
1904 setDocument(document);
1907 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement)
1909 Element* parent = pseudoElement->parentOrShadowHostElement();
1912 int parentId = m_documentNodeToIdMap.get(parent);
1916 pushChildNodesToFrontend(parentId, 1);
1917 m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, &m_documentNodeToIdMap));
1920 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
1922 int pseudoElementId = m_documentNodeToIdMap.get(pseudoElement);
1923 if (!pseudoElementId)
1926 // If a PseudoElement is bound, its parent element must be bound, too.
1927 Element* parent = pseudoElement->parentOrShadowHostElement();
1929 int parentId = m_documentNodeToIdMap.get(parent);
1932 unbind(pseudoElement, &m_documentNodeToIdMap);
1933 m_frontend->pseudoElementRemoved(parentId, pseudoElementId);
1936 Node* InspectorDOMAgent::nodeForPath(const String& path)
1938 // The path is of form "1,HTML,2,BODY,1,DIV"
1942 Node* node = m_document.get();
1943 Vector<String> pathTokens;
1944 path.split(",", false, pathTokens);
1945 if (!pathTokens.size())
1947 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1948 bool success = true;
1949 unsigned childNumber = pathTokens[i].toUInt(&success);
1952 if (childNumber >= innerChildNodeCount(node))
1955 Node* child = innerFirstChild(node);
1956 String childName = pathTokens[i + 1];
1957 for (size_t j = 0; child && j < childNumber; ++j)
1958 child = innerNextSibling(child);
1960 if (!child || child->nodeName() != childName)
1967 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId)
1969 if (Node* node = nodeForPath(path))
1970 *nodeId = pushNodePathToFrontend(node);
1972 *errorString = "No node with given path found";
1975 void InspectorDOMAgent::pushNodeByBackendIdToFrontend(ErrorString* errorString, BackendNodeId backendNodeId, int* nodeId)
1977 if (!m_backendIdToNode.contains(backendNodeId)) {
1978 *errorString = "No node with given backend id found";
1982 Node* node = m_backendIdToNode.get(backendNodeId).first;
1983 String nodeGroup = m_backendIdToNode.get(backendNodeId).second;
1984 *nodeId = pushNodePathToFrontend(node);
1986 if (nodeGroup == "") {
1987 m_backendIdToNode.remove(backendNodeId);
1988 m_nodeGroupToBackendIdMap.find(nodeGroup)->value.remove(node);
1992 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId)
1994 Node* node = assertNode(errorString, nodeId);
1997 RenderObject* renderer = node->renderer();
1999 *errorString = "No renderer for node, perhaps orphan or hidden node";
2002 while (renderer && !renderer->isRoot() && !renderer->isRelayoutBoundaryForInspector())
2003 renderer = renderer->container();
2004 Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument();
2005 *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode);
2008 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2010 Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument();
2011 Frame* frame = document ? document->frame() : 0;
2015 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
2016 if (injectedScript.hasNoValue())
2019 return injectedScript.wrapNode(node, objectGroup);
2022 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString)
2024 if (!m_documentNodeToIdMap.contains(m_document)) {
2025 RefPtr<TypeBuilder::DOM::Node> root;
2026 getDocument(errorString, root);
2027 return errorString->isEmpty();
2032 } // namespace WebCore