1 /*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Copyright (C) 2009 Joseph Pecoraro
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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.
18 *
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.
29 */
30
31 #include "config.h"
32 #include "core/inspector/InspectorDOMAgent.h"
33
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "bindings/core/v8/ScriptEventListener.h"
36 #include "core/InputTypeNames.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/dom/Node.h"
46 #include "core/dom/PseudoElement.h"
47 #include "core/dom/StaticNodeList.h"
48 #include "core/dom/Text.h"
49 #include "core/dom/shadow/ElementShadow.h"
50 #include "core/dom/shadow/ShadowRoot.h"
51 #include "core/editing/markup.h"
52 #include "core/events/EventListener.h"
53 #include "core/events/EventTarget.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"
85
86 namespace blink {
87
88 using namespace HTMLNames;
89
90 namespace DOMAgentState {
91 static const char domAgentEnabled[] = "domAgentEnabled";
92 };
93
94 static const size_t maxTextSize = 10000;
95 static const UChar ellipsisUChar[] = { 0x2026, 0 };
96
parseColor(const RefPtr<JSONObject> * colorObject)97 static Color parseColor(const RefPtr<JSONObject>* colorObject)
98 {
99 if (!colorObject || !(*colorObject))
100 return Color::transparent;
101
102 int r;
103 int g;
104 int b;
105 bool success = (*colorObject)->getNumber("r", &r);
106 success |= (*colorObject)->getNumber("g", &g);
107 success |= (*colorObject)->getNumber("b", &b);
108 if (!success)
109 return Color::transparent;
110
111 double a;
112 success = (*colorObject)->getNumber("a", &a);
113 if (!success)
114 return Color(r, g, b);
115
116 // Clamp alpha to the [0..1] range.
117 if (a < 0)
118 a = 0;
119 else if (a > 1)
120 a = 1;
121
122 return Color(r, g, b, static_cast<int>(a * 255));
123 }
124
parseConfigColor(const String & fieldName,JSONObject * configObject)125 static Color parseConfigColor(const String& fieldName, JSONObject* configObject)
126 {
127 const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName);
128 return parseColor(&colorObject);
129 }
130
parseQuad(const RefPtr<JSONArray> & quadArray,FloatQuad * quad)131 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad)
132 {
133 if (!quadArray)
134 return false;
135 const size_t coordinatesInQuad = 8;
136 double coordinates[coordinatesInQuad];
137 if (quadArray->length() != coordinatesInQuad)
138 return false;
139 for (size_t i = 0; i < coordinatesInQuad; ++i) {
140 if (!quadArray->get(i)->asNumber(coordinates + i))
141 return false;
142 }
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]));
147
148 return true;
149 }
150
hoveredNodeForPoint(LocalFrame * frame,const IntPoint & point,bool ignorePointerEventsNone)151 static Node* hoveredNodeForPoint(LocalFrame* frame, const IntPoint& point, bool ignorePointerEventsNone)
152 {
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();
162 return node;
163 }
164
hoveredNodeForEvent(LocalFrame * frame,const PlatformGestureEvent & event,bool ignorePointerEventsNone)165 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone)
166 {
167 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
168 }
169
hoveredNodeForEvent(LocalFrame * frame,const PlatformMouseEvent & event,bool ignorePointerEventsNone)170 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone)
171 {
172 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone);
173 }
174
hoveredNodeForEvent(LocalFrame * frame,const PlatformTouchEvent & event,bool ignorePointerEventsNone)175 static Node* hoveredNodeForEvent(LocalFrame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone)
176 {
177 const Vector<PlatformTouchPoint>& points = event.touchPoints();
178 if (!points.size())
179 return 0;
180 return hoveredNodeForPoint(frame, roundedIntPoint(points[0].pos()), ignorePointerEventsNone);
181 }
182
183 class RevalidateStyleAttributeTask FINAL : public NoBaseWillBeGarbageCollectedFinalized<RevalidateStyleAttributeTask> {
184 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
185 public:
186 explicit RevalidateStyleAttributeTask(InspectorDOMAgent*);
187 void scheduleFor(Element*);
reset()188 void reset() { m_timer.stop(); }
189 void onTimer(Timer<RevalidateStyleAttributeTask>*);
190 void trace(Visitor*);
191
192 private:
193 RawPtrWillBeMember<InspectorDOMAgent> m_domAgent;
194 Timer<RevalidateStyleAttributeTask> m_timer;
195 WillBeHeapHashSet<RefPtrWillBeMember<Element> > m_elements;
196 };
197
RevalidateStyleAttributeTask(InspectorDOMAgent * domAgent)198 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
199 : m_domAgent(domAgent)
200 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
201 {
202 }
203
scheduleFor(Element * element)204 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
205 {
206 m_elements.add(element);
207 if (!m_timer.isActive())
208 m_timer.startOneShot(0, FROM_HERE);
209 }
210
onTimer(Timer<RevalidateStyleAttributeTask> *)211 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
212 {
213 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
214 WillBeHeapVector<RawPtrWillBeMember<Element> > elements;
215 for (WillBePersistentHeapHashSet<RefPtrWillBeMember<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
216 elements.append(it->get());
217 m_domAgent->styleAttributeInvalidated(elements);
218
219 m_elements.clear();
220 }
221
trace(Visitor * visitor)222 void RevalidateStyleAttributeTask::trace(Visitor* visitor)
223 {
224 visitor->trace(m_domAgent);
225 #if ENABLE(OILPAN)
226 visitor->trace(m_elements);
227 #endif
228 }
229
toErrorString(ExceptionState & exceptionState)230 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState)
231 {
232 if (exceptionState.hadException())
233 return DOMException::getErrorName(exceptionState.code()) + " " + exceptionState.message();
234 return "";
235 }
236
InspectorDOMAgent(InspectorPageAgent * pageAgent,InjectedScriptManager * injectedScriptManager,InspectorOverlay * overlay)237 InspectorDOMAgent::InspectorDOMAgent(InspectorPageAgent* pageAgent, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
238 : InspectorBaseAgent<InspectorDOMAgent>("DOM")
239 , m_pageAgent(pageAgent)
240 , m_injectedScriptManager(injectedScriptManager)
241 , m_overlay(overlay)
242 , m_frontend(0)
243 , m_domListener(nullptr)
244 , m_documentNodeToIdMap(adoptPtrWillBeNoop(new NodeToIdMap()))
245 , m_lastNodeId(1)
246 , m_searchingForNode(NotSearching)
247 , m_suppressAttributeModifiedEvent(false)
248 , m_listener(nullptr)
249 {
250 }
251
~InspectorDOMAgent()252 InspectorDOMAgent::~InspectorDOMAgent()
253 {
254 #if !ENABLE(OILPAN)
255 reset();
256 ASSERT(m_searchingForNode == NotSearching);
257 #endif
258 }
259
setFrontend(InspectorFrontend * frontend)260 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
261 {
262 ASSERT(!m_frontend);
263 m_history = adoptPtrWillBeNoop(new InspectorHistory());
264 m_domEditor = adoptPtrWillBeNoop(new DOMEditor(m_history.get()));
265
266 m_frontend = frontend->dom();
267 m_instrumentingAgents->setInspectorDOMAgent(this);
268 m_document = m_pageAgent->mainFrame()->document();
269 }
270
clearFrontend()271 void InspectorDOMAgent::clearFrontend()
272 {
273 ASSERT(m_frontend);
274
275 m_history.clear();
276 m_domEditor.clear();
277
278 ErrorString error;
279 setSearchingForNode(&error, NotSearching, 0);
280 hideHighlight(&error);
281
282 m_frontend = 0;
283 m_instrumentingAgents->setInspectorDOMAgent(0);
284 disable(0);
285 reset();
286 }
287
restore()288 void InspectorDOMAgent::restore()
289 {
290 if (!enabled())
291 return;
292 innerEnable();
293 notifyDocumentUpdated();
294 }
295
documents()296 WillBeHeapVector<RawPtrWillBeMember<Document> > InspectorDOMAgent::documents()
297 {
298 WillBeHeapVector<RawPtrWillBeMember<Document> > result;
299 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
300 if (!frame->isLocalFrame())
301 continue;
302 Document* document = toLocalFrame(frame)->document();
303 if (!document)
304 continue;
305 result.append(document);
306 }
307 return result;
308 }
309
reset()310 void InspectorDOMAgent::reset()
311 {
312 discardFrontendBindings();
313 m_document = nullptr;
314 }
315
setDOMListener(DOMListener * listener)316 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
317 {
318 m_domListener = listener;
319 }
320
setDocument(Document * doc)321 void InspectorDOMAgent::setDocument(Document* doc)
322 {
323 if (doc == m_document.get())
324 return;
325
326 reset();
327
328 m_document = doc;
329
330 if (!enabled())
331 return;
332
333 // Immediately communicate 0 document or document that has finished loading.
334 if (!doc || !doc->parsing())
335 m_frontend->documentUpdated();
336 }
337
releaseDanglingNodes()338 void InspectorDOMAgent::releaseDanglingNodes()
339 {
340 m_danglingNodeToIdMaps.clear();
341 }
342
bind(Node * node,NodeToIdMap * nodesMap)343 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
344 {
345 int id = nodesMap->get(node);
346 if (id)
347 return id;
348 id = m_lastNodeId++;
349 nodesMap->set(node, id);
350 m_idToNode.set(id, node);
351 m_idToNodesMap.set(id, nodesMap);
352 return id;
353 }
354
unbind(Node * node,NodeToIdMap * nodesMap)355 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
356 {
357 int id = nodesMap->get(node);
358 if (!id)
359 return;
360
361 m_idToNode.remove(id);
362 m_idToNodesMap.remove(id);
363
364 if (node->isFrameOwnerElement()) {
365 Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument();
366 if (m_domListener)
367 m_domListener->didRemoveDocument(contentDocument);
368 if (contentDocument)
369 unbind(contentDocument, nodesMap);
370 }
371
372 for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot())
373 unbind(root, nodesMap);
374
375 if (node->isElementNode()) {
376 Element* element = toElement(node);
377 if (element->pseudoElement(BEFORE))
378 unbind(element->pseudoElement(BEFORE), nodesMap);
379 if (element->pseudoElement(AFTER))
380 unbind(element->pseudoElement(AFTER), nodesMap);
381
382 if (isHTMLLinkElement(*element)) {
383 HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
384 if (linkElement.isImport() && linkElement.import())
385 unbind(linkElement.import(), nodesMap);
386 }
387 }
388
389 nodesMap->remove(node);
390 if (m_domListener)
391 m_domListener->didRemoveDOMNode(node);
392
393 bool childrenRequested = m_childrenRequested.contains(id);
394 if (childrenRequested) {
395 // Unbind subtree known to client recursively.
396 m_childrenRequested.remove(id);
397 Node* child = innerFirstChild(node);
398 while (child) {
399 unbind(child, nodesMap);
400 child = innerNextSibling(child);
401 }
402 }
403 if (nodesMap == m_documentNodeToIdMap.get())
404 m_cachedChildCount.remove(id);
405 }
406
assertNode(ErrorString * errorString,int nodeId)407 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
408 {
409 Node* node = nodeForId(nodeId);
410 if (!node) {
411 *errorString = "Could not find node with given id";
412 return 0;
413 }
414 return node;
415 }
416
assertDocument(ErrorString * errorString,int nodeId)417 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId)
418 {
419 Node* node = assertNode(errorString, nodeId);
420 if (!node)
421 return 0;
422
423 if (!(node->isDocumentNode())) {
424 *errorString = "Document is not available";
425 return 0;
426 }
427 return toDocument(node);
428 }
429
assertElement(ErrorString * errorString,int nodeId)430 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
431 {
432 Node* node = assertNode(errorString, nodeId);
433 if (!node)
434 return 0;
435
436 if (!node->isElementNode()) {
437 *errorString = "Node is not an Element";
438 return 0;
439 }
440 return toElement(node);
441 }
442
userAgentShadowRoot(Node * node)443 static ShadowRoot* userAgentShadowRoot(Node* node)
444 {
445 if (!node || !node->isInShadowTree())
446 return 0;
447
448 Node* candidate = node;
449 while (candidate && !candidate->isShadowRoot())
450 candidate = candidate->parentOrShadowHostNode();
451 ASSERT(candidate);
452 ShadowRoot* shadowRoot = toShadowRoot(candidate);
453
454 return shadowRoot->type() == ShadowRoot::UserAgentShadowRoot ? shadowRoot : 0;
455 }
456
assertEditableNode(ErrorString * errorString,int nodeId)457 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId)
458 {
459 Node* node = assertNode(errorString, nodeId);
460 if (!node)
461 return 0;
462
463 if (node->isInShadowTree()) {
464 if (node->isShadowRoot()) {
465 *errorString = "Cannot edit shadow roots";
466 return 0;
467 }
468 if (userAgentShadowRoot(node)) {
469 *errorString = "Cannot edit nodes from user-agent shadow trees";
470 return 0;
471 }
472 }
473
474 if (node->isPseudoElement()) {
475 *errorString = "Cannot edit pseudo elements";
476 return 0;
477 }
478
479 return node;
480 }
481
assertEditableChildNode(ErrorString * errorString,Element * parentElement,int nodeId)482 Node* InspectorDOMAgent::assertEditableChildNode(ErrorString* errorString, Element* parentElement, int nodeId)
483 {
484 Node* node = assertEditableNode(errorString, nodeId);
485 if (!node)
486 return 0;
487 if (node->parentNode() != parentElement) {
488 *errorString = "Anchor node must be child of the target element";
489 return 0;
490 }
491 return node;
492 }
493
assertEditableElement(ErrorString * errorString,int nodeId)494 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId)
495 {
496 Element* element = assertElement(errorString, nodeId);
497 if (!element)
498 return 0;
499
500 if (element->isInShadowTree() && userAgentShadowRoot(element)) {
501 *errorString = "Cannot edit elements from user-agent shadow trees";
502 return 0;
503 }
504
505 if (element->isPseudoElement()) {
506 *errorString = "Cannot edit pseudo elements";
507 return 0;
508 }
509
510 return element;
511 }
512
innerEnable()513 void InspectorDOMAgent::innerEnable()
514 {
515 m_state->setBoolean(DOMAgentState::domAgentEnabled, true);
516 if (m_listener)
517 m_listener->domAgentWasEnabled();
518 }
519
enable(ErrorString *)520 void InspectorDOMAgent::enable(ErrorString*)
521 {
522 if (enabled())
523 return;
524 innerEnable();
525 notifyDocumentUpdated();
526 }
527
notifyDocumentUpdated()528 void InspectorDOMAgent::notifyDocumentUpdated()
529 {
530 m_document = nullptr;
531 setDocument(m_pageAgent->mainFrame()->document());
532 }
533
enabled() const534 bool InspectorDOMAgent::enabled() const
535 {
536 return m_state->getBoolean(DOMAgentState::domAgentEnabled);
537 }
538
disable(ErrorString * errorString)539 void InspectorDOMAgent::disable(ErrorString* errorString)
540 {
541 if (!enabled()) {
542 if (errorString)
543 *errorString = "DOM agent hasn't been enabled";
544 return;
545 }
546 m_state->setBoolean(DOMAgentState::domAgentEnabled, false);
547 reset();
548 if (m_listener)
549 m_listener->domAgentWasDisabled();
550 }
551
getDocument(ErrorString * errorString,RefPtr<TypeBuilder::DOM::Node> & root)552 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root)
553 {
554 // Backward compatibility. Mark agent as enabled when it requests document.
555 if (!enabled())
556 innerEnable();
557
558 if (!m_document) {
559 *errorString = "Document is not available";
560 return;
561 }
562
563 discardFrontendBindings();
564
565 root = buildObjectForNode(m_document.get(), 2, m_documentNodeToIdMap.get());
566 }
567
pushChildNodesToFrontend(int nodeId,int depth)568 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
569 {
570 Node* node = nodeForId(nodeId);
571 if (!node || (!node->isElementNode() && !node->isDocumentNode() && !node->isDocumentFragment()))
572 return;
573
574 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
575
576 if (m_childrenRequested.contains(nodeId)) {
577 if (depth <= 1)
578 return;
579
580 depth--;
581
582 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
583 int childNodeId = nodeMap->get(node);
584 ASSERT(childNodeId);
585 pushChildNodesToFrontend(childNodeId, depth);
586 }
587
588 return;
589 }
590
591 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap);
592 m_frontend->setChildNodes(nodeId, children.release());
593 }
594
discardFrontendBindings()595 void InspectorDOMAgent::discardFrontendBindings()
596 {
597 if (m_history)
598 m_history->reset();
599 m_searchResults.clear();
600 m_documentNodeToIdMap->clear();
601 m_idToNode.clear();
602 m_idToNodesMap.clear();
603 releaseDanglingNodes();
604 m_childrenRequested.clear();
605 m_cachedChildCount.clear();
606 if (m_revalidateStyleAttrTask)
607 m_revalidateStyleAttrTask->reset();
608 }
609
nodeForId(int id)610 Node* InspectorDOMAgent::nodeForId(int id)
611 {
612 if (!id)
613 return 0;
614
615 WillBeHeapHashMap<int, RawPtrWillBeMember<Node> >::iterator it = m_idToNode.find(id);
616 if (it != m_idToNode.end())
617 return it->value;
618 return 0;
619 }
620
requestChildNodes(ErrorString * errorString,int nodeId,const int * depth)621 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth)
622 {
623 int sanitizedDepth;
624
625 if (!depth)
626 sanitizedDepth = 1;
627 else if (*depth == -1)
628 sanitizedDepth = INT_MAX;
629 else if (*depth > 0)
630 sanitizedDepth = *depth;
631 else {
632 *errorString = "Please provide a positive integer as a depth or -1 for entire subtree";
633 return;
634 }
635
636 pushChildNodesToFrontend(nodeId, sanitizedDepth);
637 }
638
querySelector(ErrorString * errorString,int nodeId,const String & selectors,int * elementId)639 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
640 {
641 *elementId = 0;
642 Node* node = assertNode(errorString, nodeId);
643 if (!node || !node->isContainerNode())
644 return;
645
646 TrackExceptionState exceptionState;
647 RefPtrWillBeRawPtr<Element> element = toContainerNode(node)->querySelector(AtomicString(selectors), exceptionState);
648 if (exceptionState.hadException()) {
649 *errorString = "DOM Error while querying";
650 return;
651 }
652
653 if (element)
654 *elementId = pushNodePathToFrontend(element.get());
655 }
656
querySelectorAll(ErrorString * errorString,int nodeId,const String & selectors,RefPtr<TypeBuilder::Array<int>> & result)657 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result)
658 {
659 Node* node = assertNode(errorString, nodeId);
660 if (!node || !node->isContainerNode())
661 return;
662
663 TrackExceptionState exceptionState;
664 RefPtrWillBeRawPtr<StaticElementList> elements = toContainerNode(node)->querySelectorAll(AtomicString(selectors), exceptionState);
665 if (exceptionState.hadException()) {
666 *errorString = "DOM Error while querying";
667 return;
668 }
669
670 result = TypeBuilder::Array<int>::create();
671
672 for (unsigned i = 0; i < elements->length(); ++i)
673 result->addItem(pushNodePathToFrontend(elements->item(i)));
674 }
675
pushNodePathToFrontend(Node * nodeToPush)676 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
677 {
678 ASSERT(nodeToPush); // Invalid input
679
680 if (!m_document)
681 return 0;
682 if (!m_documentNodeToIdMap->contains(m_document))
683 return 0;
684
685 // Return id in case the node is known.
686 int result = m_documentNodeToIdMap->get(nodeToPush);
687 if (result)
688 return result;
689
690 Node* node = nodeToPush;
691 WillBeHeapVector<RawPtrWillBeMember<Node> > path;
692 NodeToIdMap* danglingMap = 0;
693
694 while (true) {
695 Node* parent = innerParentNode(node);
696 if (!parent) {
697 // Node being pushed is detached -> push subtree root.
698 OwnPtrWillBeRawPtr<NodeToIdMap> newMap = adoptPtrWillBeNoop(new NodeToIdMap);
699 danglingMap = newMap.get();
700 m_danglingNodeToIdMaps.append(newMap.release());
701 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
702 children->addItem(buildObjectForNode(node, 0, danglingMap));
703 m_frontend->setChildNodes(0, children);
704 break;
705 } else {
706 path.append(parent);
707 if (m_documentNodeToIdMap->get(parent))
708 break;
709 node = parent;
710 }
711 }
712
713 NodeToIdMap* map = danglingMap ? danglingMap : m_documentNodeToIdMap.get();
714 for (int i = path.size() - 1; i >= 0; --i) {
715 int nodeId = map->get(path.at(i).get());
716 ASSERT(nodeId);
717 pushChildNodesToFrontend(nodeId);
718 }
719 return map->get(nodeToPush);
720 }
721
boundNodeId(Node * node)722 int InspectorDOMAgent::boundNodeId(Node* node)
723 {
724 return m_documentNodeToIdMap->get(node);
725 }
726
setAttributeValue(ErrorString * errorString,int elementId,const String & name,const String & value)727 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value)
728 {
729 Element* element = assertEditableElement(errorString, elementId);
730 if (!element)
731 return;
732
733 m_domEditor->setAttribute(element, name, value, errorString);
734 }
735
setAttributesAsText(ErrorString * errorString,int elementId,const String & text,const String * const name)736 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
737 {
738 Element* element = assertEditableElement(errorString, elementId);
739 if (!element)
740 return;
741
742 String markup = "<span " + text + "></span>";
743 RefPtrWillBeRawPtr<DocumentFragment> fragment = element->document().createDocumentFragment();
744
745 bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement();
746 // Not all elements can represent the context (i.e. IFRAME), hence using document.body.
747 if (shouldIgnoreCase && element->document().body())
748 fragment->parseHTML(markup, element->document().body(), AllowScriptingContent);
749 else
750 fragment->parseXML(markup, 0, AllowScriptingContent);
751
752 Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0;
753 if (!parsedElement) {
754 *errorString = "Could not parse value as attributes";
755 return;
756 }
757
758 String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String();
759
760 AttributeCollection attributes = parsedElement->attributes();
761 if (attributes.isEmpty() && name) {
762 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
763 return;
764 }
765
766 bool foundOriginalAttribute = false;
767 AttributeCollection::iterator end = attributes.end();
768 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
769 // Add attribute pair
770 String attributeName = it->name().toString();
771 if (shouldIgnoreCase)
772 attributeName = attributeName.lower();
773 foundOriginalAttribute |= name && attributeName == caseAdjustedName;
774 if (!m_domEditor->setAttribute(element, attributeName, it->value(), errorString))
775 return;
776 }
777
778 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
779 m_domEditor->removeAttribute(element, caseAdjustedName, errorString);
780 }
781
removeAttribute(ErrorString * errorString,int elementId,const String & name)782 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
783 {
784 Element* element = assertEditableElement(errorString, elementId);
785 if (!element)
786 return;
787
788 m_domEditor->removeAttribute(element, name, errorString);
789 }
790
removeNode(ErrorString * errorString,int nodeId)791 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
792 {
793 Node* node = assertEditableNode(errorString, nodeId);
794 if (!node)
795 return;
796
797 ContainerNode* parentNode = node->parentNode();
798 if (!parentNode) {
799 *errorString = "Cannot remove detached node";
800 return;
801 }
802
803 m_domEditor->removeChild(parentNode, node, errorString);
804 }
805
setNodeName(ErrorString * errorString,int nodeId,const String & tagName,int * newId)806 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
807 {
808 *newId = 0;
809
810 Node* oldNode = nodeForId(nodeId);
811 if (!oldNode || !oldNode->isElementNode())
812 return;
813
814 TrackExceptionState exceptionState;
815 RefPtrWillBeRawPtr<Element> newElem = oldNode->document().createElement(AtomicString(tagName), exceptionState);
816 if (exceptionState.hadException())
817 return;
818
819 // Copy over the original node's attributes.
820 newElem->cloneAttributesFromElement(*toElement(oldNode));
821
822 // Copy over the original node's children.
823 for (Node* child = oldNode->firstChild(); child; child = oldNode->firstChild()) {
824 if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString))
825 return;
826 }
827
828 // Replace the old node with the new node
829 ContainerNode* parent = oldNode->parentNode();
830 if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString))
831 return;
832 if (!m_domEditor->removeChild(parent, oldNode, errorString))
833 return;
834
835 *newId = pushNodePathToFrontend(newElem.get());
836 if (m_childrenRequested.contains(nodeId))
837 pushChildNodesToFrontend(*newId);
838 }
839
getOuterHTML(ErrorString * errorString,int nodeId,WTF::String * outerHTML)840 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
841 {
842 Node* node = assertNode(errorString, nodeId);
843 if (!node)
844 return;
845
846 *outerHTML = createMarkup(node);
847 }
848
setOuterHTML(ErrorString * errorString,int nodeId,const String & outerHTML)849 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML)
850 {
851 if (!nodeId) {
852 ASSERT(m_document);
853 DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get());
854 domPatchSupport.patchDocument(outerHTML);
855 return;
856 }
857
858 Node* node = assertEditableNode(errorString, nodeId);
859 if (!node)
860 return;
861
862 Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument();
863 if (!document || (!document->isHTMLDocument() && !document->isXMLDocument())) {
864 *errorString = "Not an HTML/XML document";
865 return;
866 }
867
868 Node* newNode = 0;
869 if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString))
870 return;
871
872 if (!newNode) {
873 // The only child node has been deleted.
874 return;
875 }
876
877 int newId = pushNodePathToFrontend(newNode);
878
879 bool childrenRequested = m_childrenRequested.contains(nodeId);
880 if (childrenRequested)
881 pushChildNodesToFrontend(newId);
882 }
883
setNodeValue(ErrorString * errorString,int nodeId,const String & value)884 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
885 {
886 Node* node = assertEditableNode(errorString, nodeId);
887 if (!node)
888 return;
889
890 if (node->nodeType() != Node::TEXT_NODE) {
891 *errorString = "Can only set value of text nodes";
892 return;
893 }
894
895 m_domEditor->replaceWholeText(toText(node), value, errorString);
896 }
897
getEventListenersForNode(ErrorString * errorString,int nodeId,const String * objectGroup,RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener>> & listenersArray)898 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray)
899 {
900 listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create();
901 Node* node = assertNode(errorString, nodeId);
902 if (!node)
903 return;
904 Vector<EventListenerInfo> eventInformation;
905 getEventListeners(node, eventInformation, true);
906
907 // Get Capturing Listeners (in this order)
908 size_t eventInformationLength = eventInformation.size();
909 for (size_t i = 0; i < eventInformationLength; ++i) {
910 const EventListenerInfo& info = eventInformation[i];
911 const EventListenerVector& vector = info.eventListenerVector;
912 for (size_t j = 0; j < vector.size(); ++j) {
913 const RegisteredEventListener& listener = vector[j];
914 if (listener.useCapture) {
915 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
916 if (listenerObject)
917 listenersArray->addItem(listenerObject);
918 }
919 }
920 }
921
922 // Get Bubbling Listeners (reverse order)
923 for (size_t i = eventInformationLength; i; --i) {
924 const EventListenerInfo& info = eventInformation[i - 1];
925 const EventListenerVector& vector = info.eventListenerVector;
926 for (size_t j = 0; j < vector.size(); ++j) {
927 const RegisteredEventListener& listener = vector[j];
928 if (!listener.useCapture) {
929 RefPtr<TypeBuilder::DOM::EventListener> listenerObject = buildObjectForEventListener(listener, info.eventType, info.eventTarget->toNode(), objectGroup);
930 if (listenerObject)
931 listenersArray->addItem(listenerObject);
932 }
933 }
934 }
935 }
936
getEventListeners(EventTarget * target,Vector<EventListenerInfo> & eventInformation,bool includeAncestors)937 void InspectorDOMAgent::getEventListeners(EventTarget* target, Vector<EventListenerInfo>& eventInformation, bool includeAncestors)
938 {
939 // The Node's Ancestors including self.
940 Vector<EventTarget*> ancestors;
941 ancestors.append(target);
942 if (includeAncestors) {
943 Node* node = target->toNode();
944 for (ContainerNode* ancestor = node ? node->parentOrShadowHostNode() : 0; ancestor; ancestor = ancestor->parentOrShadowHostNode())
945 ancestors.append(ancestor);
946 }
947
948 // Nodes and their Listeners for the concerned event types (order is top to bottom)
949 for (size_t i = ancestors.size(); i; --i) {
950 EventTarget* ancestor = ancestors[i - 1];
951 Vector<AtomicString> eventTypes = ancestor->eventTypes();
952 for (size_t j = 0; j < eventTypes.size(); ++j) {
953 AtomicString& type = eventTypes[j];
954 const EventListenerVector& listeners = ancestor->getEventListeners(type);
955 EventListenerVector filteredListeners;
956 filteredListeners.reserveCapacity(listeners.size());
957 for (size_t k = 0; k < listeners.size(); ++k) {
958 if (listeners[k].listener->type() == EventListener::JSEventListenerType)
959 filteredListeners.append(listeners[k]);
960 }
961 if (!filteredListeners.isEmpty())
962 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners));
963 }
964 }
965 }
966
nextNodeWithShadowDOMInMind(const Node & current,const Node * stayWithin,bool includeUserAgentShadowDOM)967 static Node* nextNodeWithShadowDOMInMind(const Node& current, const Node* stayWithin, bool includeUserAgentShadowDOM)
968 {
969 // At first traverse the subtree.
970 if (current.isElementNode()) {
971 const Element& element = toElement(current);
972 ElementShadow* elementShadow = element.shadow();
973 if (elementShadow) {
974 ShadowRoot* shadowRoot = elementShadow->youngestShadowRoot();
975 if (shadowRoot) {
976 if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot || includeUserAgentShadowDOM)
977 return shadowRoot;
978 }
979 }
980 }
981 if (current.hasChildren())
982 return current.firstChild();
983
984 // Then traverse siblings of the node itself and its ancestors.
985 const Node* node = ¤t;
986 do {
987 if (node == stayWithin)
988 return 0;
989 if (node->isShadowRoot()) {
990 const ShadowRoot* shadowRoot = toShadowRoot(node);
991 if (shadowRoot->olderShadowRoot())
992 return shadowRoot->olderShadowRoot();
993 Node* host = shadowRoot->host();
994 if (host && host->hasChildren())
995 return host->firstChild();
996 }
997 if (node->nextSibling())
998 return node->nextSibling();
999 node = node->isShadowRoot() ? toShadowRoot(node)->host() : node->parentNode();
1000 } while (node);
1001
1002 return 0;
1003 }
1004
performSearch(ErrorString *,const String & whitespaceTrimmedQuery,const bool * optionalIncludeUserAgentShadowDOM,String * searchId,int * resultCount)1005 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, const bool* optionalIncludeUserAgentShadowDOM, String* searchId, int* resultCount)
1006 {
1007 // FIXME: Few things are missing here:
1008 // 1) Search works with node granularity - number of matches within node is not calculated.
1009 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
1010 // is sufficient.
1011
1012 bool includeUserAgentShadowDOM = optionalIncludeUserAgentShadowDOM ? *optionalIncludeUserAgentShadowDOM : false;
1013
1014 unsigned queryLength = whitespaceTrimmedQuery.length();
1015 bool startTagFound = !whitespaceTrimmedQuery.find('<');
1016 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
1017 bool startQuoteFound = !whitespaceTrimmedQuery.find('"');
1018 bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength;
1019 bool exactAttributeMatch = startQuoteFound && endQuoteFound;
1020
1021 String tagNameQuery = whitespaceTrimmedQuery;
1022 String attributeQuery = whitespaceTrimmedQuery;
1023 if (startTagFound)
1024 tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1);
1025 if (endTagFound)
1026 tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1);
1027 if (startQuoteFound)
1028 attributeQuery = attributeQuery.right(attributeQuery.length() - 1);
1029 if (endQuoteFound)
1030 attributeQuery = attributeQuery.left(attributeQuery.length() - 1);
1031
1032 WillBeHeapVector<RawPtrWillBeMember<Document> > docs = documents();
1033 WillBeHeapListHashSet<RawPtrWillBeMember<Node> > resultCollector;
1034
1035 for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1036 Document* document = *it;
1037 Node* documentElement = document->documentElement();
1038 Node* node = documentElement;
1039 if (!node)
1040 continue;
1041
1042 // Manual plain text search.
1043 for (node = nextNodeWithShadowDOMInMind(*node, documentElement, includeUserAgentShadowDOM); node; node = nextNodeWithShadowDOMInMind(*node, documentElement, includeUserAgentShadowDOM)) {
1044 switch (node->nodeType()) {
1045 case Node::TEXT_NODE:
1046 case Node::COMMENT_NODE:
1047 case Node::CDATA_SECTION_NODE: {
1048 String text = node->nodeValue();
1049 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound)
1050 resultCollector.add(node);
1051 break;
1052 }
1053 case Node::ELEMENT_NODE: {
1054 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound))
1055 || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery))
1056 || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false))
1057 || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) {
1058 resultCollector.add(node);
1059 break;
1060 }
1061 // Go through all attributes and serialize them.
1062 const Element* element = toElement(node);
1063 AttributeCollection attributes = element->attributes();
1064 AttributeCollection::iterator end = attributes.end();
1065 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
1066 // Add attribute pair
1067 if (it->localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) {
1068 resultCollector.add(node);
1069 break;
1070 }
1071 size_t foundPosition = it->value().find(attributeQuery, 0, false);
1072 if (foundPosition != kNotFound) {
1073 if (!exactAttributeMatch || (!foundPosition && it->value().length() == attributeQuery.length())) {
1074 resultCollector.add(node);
1075 break;
1076 }
1077 }
1078 }
1079 break;
1080 }
1081 default:
1082 break;
1083 }
1084 }
1085
1086 // XPath evaluation
1087 for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1088 Document* document = *it;
1089 ASSERT(document);
1090 TrackExceptionState exceptionState;
1091 RefPtrWillBeRawPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(*document, whitespaceTrimmedQuery, document, nullptr, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState);
1092 if (exceptionState.hadException() || !result)
1093 continue;
1094
1095 unsigned long size = result->snapshotLength(exceptionState);
1096 for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) {
1097 Node* node = result->snapshotItem(i, exceptionState);
1098 if (exceptionState.hadException())
1099 break;
1100
1101 if (node->nodeType() == Node::ATTRIBUTE_NODE)
1102 node = toAttr(node)->ownerElement();
1103 resultCollector.add(node);
1104 }
1105 }
1106
1107 // Selector evaluation
1108 for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = docs.begin(); it != docs.end(); ++it) {
1109 Document* document = *it;
1110 TrackExceptionState exceptionState;
1111 RefPtrWillBeRawPtr<StaticElementList> elementList = document->querySelectorAll(AtomicString(whitespaceTrimmedQuery), exceptionState);
1112 if (exceptionState.hadException() || !elementList)
1113 continue;
1114
1115 unsigned size = elementList->length();
1116 for (unsigned i = 0; i < size; ++i)
1117 resultCollector.add(elementList->item(i));
1118 }
1119 }
1120
1121 *searchId = IdentifiersFactory::createIdentifier();
1122 WillBeHeapVector<RefPtrWillBeMember<Node> >* resultsIt = &m_searchResults.add(*searchId, WillBeHeapVector<RefPtrWillBeMember<Node> >()).storedValue->value;
1123
1124 for (WillBeHeapListHashSet<RawPtrWillBeMember<Node> >::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it)
1125 resultsIt->append(*it);
1126
1127 *resultCount = resultsIt->size();
1128 }
1129
getSearchResults(ErrorString * errorString,const String & searchId,int fromIndex,int toIndex,RefPtr<TypeBuilder::Array<int>> & nodeIds)1130 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds)
1131 {
1132 SearchResults::iterator it = m_searchResults.find(searchId);
1133 if (it == m_searchResults.end()) {
1134 *errorString = "No search session with given id found";
1135 return;
1136 }
1137
1138 int size = it->value.size();
1139 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1140 *errorString = "Invalid search result range";
1141 return;
1142 }
1143
1144 nodeIds = TypeBuilder::Array<int>::create();
1145 for (int i = fromIndex; i < toIndex; ++i)
1146 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1147 }
1148
discardSearchResults(ErrorString *,const String & searchId)1149 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId)
1150 {
1151 m_searchResults.remove(searchId);
1152 }
1153
handleMousePress()1154 bool InspectorDOMAgent::handleMousePress()
1155 {
1156 if (m_searchingForNode == NotSearching)
1157 return false;
1158
1159 if (Node* node = m_overlay->highlightedNode()) {
1160 inspect(node);
1161 return true;
1162 }
1163 return false;
1164 }
1165
handleGestureEvent(LocalFrame * frame,const PlatformGestureEvent & event)1166 bool InspectorDOMAgent::handleGestureEvent(LocalFrame* frame, const PlatformGestureEvent& event)
1167 {
1168 if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap)
1169 return false;
1170 Node* node = hoveredNodeForEvent(frame, event, false);
1171 if (node && m_inspectModeHighlightConfig) {
1172 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1173 inspect(node);
1174 return true;
1175 }
1176 return false;
1177 }
1178
handleTouchEvent(LocalFrame * frame,const PlatformTouchEvent & event)1179 bool InspectorDOMAgent::handleTouchEvent(LocalFrame* frame, const PlatformTouchEvent& event)
1180 {
1181 if (m_searchingForNode == NotSearching)
1182 return false;
1183 Node* node = hoveredNodeForEvent(frame, event, false);
1184 if (node && m_inspectModeHighlightConfig) {
1185 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig, false);
1186 inspect(node);
1187 return true;
1188 }
1189 return false;
1190 }
1191
inspect(Node * inspectedNode)1192 void InspectorDOMAgent::inspect(Node* inspectedNode)
1193 {
1194 if (!m_frontend || !inspectedNode)
1195 return;
1196
1197 Node* node = inspectedNode;
1198 while (node && !node->isElementNode() && !node->isDocumentNode() && !node->isDocumentFragment())
1199 node = node->parentOrShadowHostNode();
1200
1201 if (!node)
1202 return;
1203
1204 int nodeId = pushNodePathToFrontend(node);
1205 if (nodeId)
1206 m_frontend->inspectNodeRequested(nodeId);
1207 }
1208
handleMouseMove(LocalFrame * frame,const PlatformMouseEvent & event)1209 bool InspectorDOMAgent::handleMouseMove(LocalFrame* frame, const PlatformMouseEvent& event)
1210 {
1211 if (m_searchingForNode == NotSearching)
1212 return false;
1213
1214 if (!frame->view() || !frame->contentRenderer())
1215 return true;
1216 Node* node = hoveredNodeForEvent(frame, event, event.shiftKey());
1217
1218 // Do not highlight within UA shadow root unless requested.
1219 if (m_searchingForNode != SearchingForUAShadow) {
1220 ShadowRoot* uaShadowRoot = userAgentShadowRoot(node);
1221 if (uaShadowRoot)
1222 node = uaShadowRoot->host();
1223 }
1224
1225 // Shadow roots don't have boxes - use host element instead.
1226 if (node && node->isShadowRoot())
1227 node = node->parentOrShadowHostNode();
1228
1229 if (!node)
1230 return true;
1231
1232 Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0;
1233 if (eventTarget == node)
1234 eventTarget = 0;
1235
1236 if (node && m_inspectModeHighlightConfig)
1237 m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig, event.ctrlKey() || event.metaKey());
1238 return true;
1239 }
1240
setSearchingForNode(ErrorString * errorString,SearchMode searchMode,JSONObject * highlightInspectorObject)1241 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject)
1242 {
1243 if (m_searchingForNode == searchMode)
1244 return;
1245
1246 m_searchingForNode = searchMode;
1247 m_overlay->setInspectModeEnabled(searchMode != NotSearching);
1248 if (searchMode != NotSearching) {
1249 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1250 if (!m_inspectModeHighlightConfig)
1251 return;
1252 } else
1253 hideHighlight(errorString);
1254 }
1255
highlightConfigFromInspectorObject(ErrorString * errorString,JSONObject * highlightInspectorObject)1256 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject)
1257 {
1258 if (!highlightInspectorObject) {
1259 *errorString = "Internal error: highlight configuration parameter is missing";
1260 return nullptr;
1261 }
1262
1263 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1264 bool showInfo = false; // Default: false (do not show a tooltip).
1265 highlightInspectorObject->getBoolean("showInfo", &showInfo);
1266 highlightConfig->showInfo = showInfo;
1267 bool showRulers = false; // Default: false (do not show rulers).
1268 highlightInspectorObject->getBoolean("showRulers", &showRulers);
1269 highlightConfig->showRulers = showRulers;
1270 bool showExtensionLines = false; // Default: false (do not show extension lines).
1271 highlightInspectorObject->getBoolean("showExtensionLines", &showExtensionLines);
1272 highlightConfig->showExtensionLines = showExtensionLines;
1273 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1274 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1275 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1276 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1277 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1278 highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject);
1279 highlightConfig->shape = parseConfigColor("shapeColor", highlightInspectorObject);
1280 highlightConfig->shapeMargin = parseConfigColor("shapeMarginColor", highlightInspectorObject);
1281
1282 return highlightConfig.release();
1283 }
1284
setInspectModeEnabled(ErrorString * errorString,bool enabled,const bool * inspectUAShadowDOM,const RefPtr<JSONObject> * highlightConfig)1285 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectUAShadowDOM, const RefPtr<JSONObject>* highlightConfig)
1286 {
1287 if (enabled && !pushDocumentUponHandlelessOperation(errorString))
1288 return;
1289 SearchMode searchMode = enabled ? (asBool(inspectUAShadowDOM) ? SearchingForUAShadow : SearchingForNormal) : NotSearching;
1290 setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0);
1291 }
1292
highlightRect(ErrorString *,int x,int y,int width,int height,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1293 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1294 {
1295 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height)));
1296 innerHighlightQuad(quad.release(), color, outlineColor);
1297 }
1298
highlightQuad(ErrorString * errorString,const RefPtr<JSONArray> & quadArray,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1299 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1300 {
1301 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad());
1302 if (!parseQuad(quadArray, quad.get())) {
1303 *errorString = "Invalid Quad format";
1304 return;
1305 }
1306 innerHighlightQuad(quad.release(), color, outlineColor);
1307 }
1308
innerHighlightQuad(PassOwnPtr<FloatQuad> quad,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1309 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor)
1310 {
1311 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1312 highlightConfig->content = parseColor(color);
1313 highlightConfig->contentOutline = parseColor(outlineColor);
1314 m_overlay->highlightQuad(quad, *highlightConfig);
1315 }
1316
highlightNode(ErrorString * errorString,const RefPtr<JSONObject> & highlightInspectorObject,const int * nodeId,const String * objectId)1317 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId)
1318 {
1319 Node* node = 0;
1320 if (nodeId) {
1321 node = assertNode(errorString, *nodeId);
1322 } else if (objectId) {
1323 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId);
1324 node = injectedScript.nodeForObjectId(*objectId);
1325 if (!node)
1326 *errorString = "Node for given objectId not found";
1327 } else
1328 *errorString = "Either nodeId or objectId must be specified";
1329
1330 if (!node)
1331 return;
1332
1333 OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get());
1334 if (!highlightConfig)
1335 return;
1336
1337 m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig, false);
1338 }
1339
highlightFrame(ErrorString *,const String & frameId,const RefPtr<JSONObject> * color,const RefPtr<JSONObject> * outlineColor)1340 void InspectorDOMAgent::highlightFrame(
1341 ErrorString*,
1342 const String& frameId,
1343 const RefPtr<JSONObject>* color,
1344 const RefPtr<JSONObject>* outlineColor)
1345 {
1346 LocalFrame* frame = m_pageAgent->frameForId(frameId);
1347 // FIXME: Inspector doesn't currently work cross process.
1348 if (frame && frame->deprecatedLocalOwner()) {
1349 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig());
1350 highlightConfig->showInfo = true; // Always show tooltips for frames.
1351 highlightConfig->content = parseColor(color);
1352 highlightConfig->contentOutline = parseColor(outlineColor);
1353 m_overlay->highlightNode(frame->deprecatedLocalOwner(), 0 /* eventTarget */, *highlightConfig, false);
1354 }
1355 }
1356
hideHighlight(ErrorString *)1357 void InspectorDOMAgent::hideHighlight(ErrorString*)
1358 {
1359 m_overlay->hideHighlight();
1360 }
1361
copyTo(ErrorString * errorString,int nodeId,int targetElementId,const int * const anchorNodeId,int * newNodeId)1362 void InspectorDOMAgent::copyTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1363 {
1364 Node* node = assertEditableNode(errorString, nodeId);
1365 if (!node)
1366 return;
1367
1368 Element* targetElement = assertEditableElement(errorString, targetElementId);
1369 if (!targetElement)
1370 return;
1371
1372 Node* anchorNode = 0;
1373 if (anchorNodeId && *anchorNodeId) {
1374 anchorNode = assertEditableChildNode(errorString, targetElement, *anchorNodeId);
1375 if (!anchorNode)
1376 return;
1377 }
1378
1379 // The clone is deep by default.
1380 RefPtrWillBeRawPtr<Node> clonedNode = node->cloneNode(true);
1381 if (!clonedNode) {
1382 *errorString = "Failed to clone node";
1383 return;
1384 }
1385 if (!m_domEditor->insertBefore(targetElement, clonedNode, anchorNode, errorString))
1386 return;
1387
1388 *newNodeId = pushNodePathToFrontend(clonedNode.get());
1389 }
1390
moveTo(ErrorString * errorString,int nodeId,int targetElementId,const int * const anchorNodeId,int * newNodeId)1391 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
1392 {
1393 Node* node = assertEditableNode(errorString, nodeId);
1394 if (!node)
1395 return;
1396
1397 Element* targetElement = assertEditableElement(errorString, targetElementId);
1398 if (!targetElement)
1399 return;
1400
1401 Node* current = targetElement;
1402 while (current) {
1403 if (current == node) {
1404 *errorString = "Unable to move node into self or descendant";
1405 return;
1406 }
1407 current = current->parentNode();
1408 }
1409
1410 Node* anchorNode = 0;
1411 if (anchorNodeId && *anchorNodeId) {
1412 anchorNode = assertEditableChildNode(errorString, targetElement, *anchorNodeId);
1413 if (!anchorNode)
1414 return;
1415 }
1416
1417 if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString))
1418 return;
1419
1420 *newNodeId = pushNodePathToFrontend(node);
1421 }
1422
undo(ErrorString * errorString)1423 void InspectorDOMAgent::undo(ErrorString* errorString)
1424 {
1425 TrackExceptionState exceptionState;
1426 m_history->undo(exceptionState);
1427 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1428 }
1429
redo(ErrorString * errorString)1430 void InspectorDOMAgent::redo(ErrorString* errorString)
1431 {
1432 TrackExceptionState exceptionState;
1433 m_history->redo(exceptionState);
1434 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1435 }
1436
markUndoableState(ErrorString *)1437 void InspectorDOMAgent::markUndoableState(ErrorString*)
1438 {
1439 m_history->markUndoableState();
1440 }
1441
focus(ErrorString * errorString,int nodeId)1442 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId)
1443 {
1444 Element* element = assertElement(errorString, nodeId);
1445 if (!element)
1446 return;
1447
1448 element->document().updateLayoutIgnorePendingStylesheets();
1449 if (!element->isFocusable()) {
1450 *errorString = "Element is not focusable";
1451 return;
1452 }
1453 element->focus();
1454 }
1455
setFileInputFiles(ErrorString * errorString,int nodeId,const RefPtr<JSONArray> & files)1456 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files)
1457 {
1458 Node* node = assertNode(errorString, nodeId);
1459 if (!node)
1460 return;
1461 if (!isHTMLInputElement(*node) || toHTMLInputElement(*node).type() != InputTypeNames::file) {
1462 *errorString = "Node is not a file input element";
1463 return;
1464 }
1465
1466 RefPtrWillBeRawPtr<FileList> fileList = FileList::create();
1467 for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) {
1468 String path;
1469 if (!(*iter)->asString(&path)) {
1470 *errorString = "Files must be strings";
1471 return;
1472 }
1473 fileList->append(File::create(path));
1474 }
1475 toHTMLInputElement(node)->setFiles(fileList);
1476 }
1477
getBoxModel(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::DOM::BoxModel> & model)1478 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model)
1479 {
1480 Node* node = assertNode(errorString, nodeId);
1481 if (!node)
1482 return;
1483
1484 bool result = m_overlay->getBoxModel(node, model);
1485 if (!result)
1486 *errorString = "Could not compute box model.";
1487
1488 }
1489
getNodeForLocation(ErrorString * errorString,int x,int y,int * nodeId)1490 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId)
1491 {
1492 if (!pushDocumentUponHandlelessOperation(errorString))
1493 return;
1494 HitTestRequest request(HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent);
1495 HitTestResult result(IntPoint(x, y));
1496 m_document->frame()->contentRenderer()->hitTest(request, result);
1497 Node* node = result.innerPossiblyPseudoNode();
1498 while (node && node->nodeType() == Node::TEXT_NODE)
1499 node = node->parentNode();
1500 if (!node) {
1501 *errorString = "No node found at given location";
1502 return;
1503 }
1504 *nodeId = pushNodePathToFrontend(node);
1505 }
1506
resolveNode(ErrorString * errorString,int nodeId,const String * const objectGroup,RefPtr<TypeBuilder::Runtime::RemoteObject> & result)1507 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
1508 {
1509 String objectGroupName = objectGroup ? *objectGroup : "";
1510 Node* node = nodeForId(nodeId);
1511 if (!node) {
1512 *errorString = "No node with given id found";
1513 return;
1514 }
1515 RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1516 if (!object) {
1517 *errorString = "Node with given id does not belong to the document";
1518 return;
1519 }
1520 result = object;
1521 }
1522
getAttributes(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::Array<String>> & result)1523 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result)
1524 {
1525 Element* element = assertElement(errorString, nodeId);
1526 if (!element)
1527 return;
1528
1529 result = buildArrayForElementAttributes(element);
1530 }
1531
requestNode(ErrorString *,const String & objectId,int * nodeId)1532 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId)
1533 {
1534 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
1535 Node* node = injectedScript.nodeForObjectId(objectId);
1536 if (node)
1537 *nodeId = pushNodePathToFrontend(node);
1538 else
1539 *nodeId = 0;
1540 }
1541
1542 // static
documentURLString(Document * document)1543 String InspectorDOMAgent::documentURLString(Document* document)
1544 {
1545 if (!document || document->url().isNull())
1546 return "";
1547 return document->url().string();
1548 }
1549
documentBaseURLString(Document * document)1550 static String documentBaseURLString(Document* document)
1551 {
1552 return document->completeURL("").string();
1553 }
1554
shadowRootType(ShadowRoot * shadowRoot)1555 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot)
1556 {
1557 switch (shadowRoot->type()) {
1558 case ShadowRoot::UserAgentShadowRoot:
1559 return TypeBuilder::DOM::ShadowRootType::User_agent;
1560 case ShadowRoot::AuthorShadowRoot:
1561 return TypeBuilder::DOM::ShadowRootType::Author;
1562 }
1563 ASSERT_NOT_REACHED();
1564 return TypeBuilder::DOM::ShadowRootType::User_agent;
1565 }
1566
buildObjectForNode(Node * node,int depth,NodeToIdMap * nodesMap)1567 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1568 {
1569 int id = bind(node, nodesMap);
1570 String nodeName;
1571 String localName;
1572 String nodeValue;
1573
1574 switch (node->nodeType()) {
1575 case Node::TEXT_NODE:
1576 case Node::COMMENT_NODE:
1577 case Node::CDATA_SECTION_NODE:
1578 nodeValue = node->nodeValue();
1579 if (nodeValue.length() > maxTextSize)
1580 nodeValue = nodeValue.left(maxTextSize) + ellipsisUChar;
1581 break;
1582 case Node::ATTRIBUTE_NODE:
1583 localName = node->localName();
1584 break;
1585 case Node::DOCUMENT_FRAGMENT_NODE:
1586 case Node::DOCUMENT_NODE:
1587 case Node::ELEMENT_NODE:
1588 default:
1589 nodeName = node->nodeName();
1590 localName = node->localName();
1591 break;
1592 }
1593
1594 RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create()
1595 .setNodeId(id)
1596 .setNodeType(static_cast<int>(node->nodeType()))
1597 .setNodeName(nodeName)
1598 .setLocalName(localName)
1599 .setNodeValue(nodeValue);
1600
1601 bool forcePushChildren = false;
1602 if (node->isElementNode()) {
1603 Element* element = toElement(node);
1604 value->setAttributes(buildArrayForElementAttributes(element));
1605
1606 if (node->isFrameOwnerElement()) {
1607 HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node);
1608 LocalFrame* frame = (frameOwner->contentFrame() && frameOwner->contentFrame()->isLocalFrame()) ? toLocalFrame(frameOwner->contentFrame()) : 0;
1609 if (frame)
1610 value->setFrameId(m_pageAgent->frameId(frame));
1611 if (Document* doc = frameOwner->contentDocument())
1612 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap));
1613 }
1614
1615 ElementShadow* shadow = element->shadow();
1616 if (shadow) {
1617 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1618 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot())
1619 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1620 value->setShadowRoots(shadowRoots);
1621 forcePushChildren = true;
1622 }
1623
1624 if (isHTMLLinkElement(*element)) {
1625 HTMLLinkElement& linkElement = toHTMLLinkElement(*element);
1626 if (linkElement.isImport() && linkElement.import() && innerParentNode(linkElement.import()) == linkElement)
1627 value->setImportedDocument(buildObjectForNode(linkElement.import(), 0, nodesMap));
1628 forcePushChildren = true;
1629 }
1630
1631 if (isHTMLTemplateElement(*element)) {
1632 value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(*element).content(), 0, nodesMap));
1633 forcePushChildren = true;
1634 }
1635
1636 switch (element->pseudoId()) {
1637 case BEFORE:
1638 value->setPseudoType(TypeBuilder::DOM::PseudoType::Before);
1639 break;
1640 case AFTER:
1641 value->setPseudoType(TypeBuilder::DOM::PseudoType::After);
1642 break;
1643 default: {
1644 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap);
1645 if (pseudoElements) {
1646 value->setPseudoElements(pseudoElements.release());
1647 forcePushChildren = true;
1648 }
1649 break;
1650 }
1651 }
1652 } else if (node->isDocumentNode()) {
1653 Document* document = toDocument(node);
1654 value->setDocumentURL(documentURLString(document));
1655 value->setBaseURL(documentBaseURLString(document));
1656 value->setXmlVersion(document->xmlVersion());
1657 } else if (node->isDocumentTypeNode()) {
1658 DocumentType* docType = toDocumentType(node);
1659 value->setPublicId(docType->publicId());
1660 value->setSystemId(docType->systemId());
1661 } else if (node->isAttributeNode()) {
1662 Attr* attribute = toAttr(node);
1663 value->setName(attribute->name());
1664 value->setValue(attribute->value());
1665 } else if (node->isShadowRoot()) {
1666 value->setShadowRootType(shadowRootType(toShadowRoot(node)));
1667 }
1668
1669 if (node->isContainerNode()) {
1670 int nodeCount = innerChildNodeCount(node);
1671 value->setChildNodeCount(nodeCount);
1672 if (nodesMap == m_documentNodeToIdMap)
1673 m_cachedChildCount.set(id, nodeCount);
1674 if (forcePushChildren && !depth)
1675 depth = 1;
1676 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap);
1677 if (children->length() > 0 || depth) // Push children along with shadow in any case.
1678 value->setChildren(children.release());
1679 }
1680
1681 return value.release();
1682 }
1683
buildArrayForElementAttributes(Element * element)1684 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1685 {
1686 RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create();
1687 // Go through all attributes and serialize them.
1688 AttributeCollection attributes = element->attributes();
1689 AttributeCollection::iterator end = attributes.end();
1690 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
1691 // Add attribute pair
1692 attributesValue->addItem(it->name().toString());
1693 attributesValue->addItem(it->value());
1694 }
1695 return attributesValue.release();
1696 }
1697
buildArrayForContainerChildren(Node * container,int depth,NodeToIdMap * nodesMap)1698 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1699 {
1700 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1701 if (depth == 0) {
1702 // Special-case the only text child - pretend that container's children have been requested.
1703 Node* firstChild = container->firstChild();
1704 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1705 children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1706 m_childrenRequested.add(bind(container, nodesMap));
1707 }
1708 return children.release();
1709 }
1710
1711 Node* child = innerFirstChild(container);
1712 depth--;
1713 m_childrenRequested.add(bind(container, nodesMap));
1714
1715 while (child) {
1716 children->addItem(buildObjectForNode(child, depth, nodesMap));
1717 child = innerNextSibling(child);
1718 }
1719 return children.release();
1720 }
1721
buildObjectForEventListener(const RegisteredEventListener & registeredEventListener,const AtomicString & eventType,Node * node,const String * objectGroupId)1722 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId)
1723 {
1724 RefPtr<EventListener> eventListener = registeredEventListener.listener;
1725 String sourceName;
1726 String scriptId;
1727 int lineNumber;
1728 int columnNumber;
1729 if (!eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber, columnNumber))
1730 return nullptr;
1731
1732 Document& document = node->document();
1733 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
1734 .setScriptId(scriptId)
1735 .setLineNumber(lineNumber);
1736 location->setColumnNumber(columnNumber);
1737 RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create()
1738 .setType(eventType)
1739 .setUseCapture(registeredEventListener.useCapture)
1740 .setIsAttribute(eventListener->isAttribute())
1741 .setNodeId(pushNodePathToFrontend(node))
1742 .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get()))
1743 .setLocation(location);
1744 if (objectGroupId) {
1745 ScriptValue functionValue = eventListenerHandler(&document, eventListener.get());
1746 if (!functionValue.isEmpty()) {
1747 LocalFrame* frame = document.frame();
1748 if (frame) {
1749 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get());
1750 if (scriptState) {
1751 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1752 if (!injectedScript.isEmpty()) {
1753 RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId);
1754 value->setHandler(valueJson);
1755 }
1756 }
1757 }
1758 }
1759 }
1760 if (!sourceName.isEmpty())
1761 value->setSourceName(sourceName);
1762 return value.release();
1763 }
1764
buildArrayForPseudoElements(Element * element,NodeToIdMap * nodesMap)1765 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap)
1766 {
1767 if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER))
1768 return nullptr;
1769
1770 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create();
1771 if (element->pseudoElement(BEFORE))
1772 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap));
1773 if (element->pseudoElement(AFTER))
1774 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap));
1775 return pseudoElements.release();
1776 }
1777
innerFirstChild(Node * node)1778 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1779 {
1780 node = node->firstChild();
1781 while (isWhitespace(node))
1782 node = node->nextSibling();
1783 return node;
1784 }
1785
innerNextSibling(Node * node)1786 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1787 {
1788 do {
1789 node = node->nextSibling();
1790 } while (isWhitespace(node));
1791 return node;
1792 }
1793
innerPreviousSibling(Node * node)1794 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1795 {
1796 do {
1797 node = node->previousSibling();
1798 } while (isWhitespace(node));
1799 return node;
1800 }
1801
innerChildNodeCount(Node * node)1802 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1803 {
1804 unsigned count = 0;
1805 Node* child = innerFirstChild(node);
1806 while (child) {
1807 count++;
1808 child = innerNextSibling(child);
1809 }
1810 return count;
1811 }
1812
innerParentNode(Node * node)1813 Node* InspectorDOMAgent::innerParentNode(Node* node)
1814 {
1815 if (node->isDocumentNode()) {
1816 Document* document = toDocument(node);
1817 if (HTMLImportLoader* loader = document->importLoader())
1818 return loader->firstImport()->link();
1819 return document->ownerElement();
1820 }
1821 return node->parentOrShadowHostNode();
1822 }
1823
isWhitespace(Node * node)1824 bool InspectorDOMAgent::isWhitespace(Node* node)
1825 {
1826 //TODO: pull ignoreWhitespace setting from the frontend and use here.
1827 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1828 }
1829
domContentLoadedEventFired(LocalFrame * frame)1830 void InspectorDOMAgent::domContentLoadedEventFired(LocalFrame* frame)
1831 {
1832 if (!frame->isMainFrame())
1833 return;
1834
1835 // Re-push document once it is loaded.
1836 discardFrontendBindings();
1837 if (enabled())
1838 m_frontend->documentUpdated();
1839 }
1840
invalidateFrameOwnerElement(LocalFrame * frame)1841 void InspectorDOMAgent::invalidateFrameOwnerElement(LocalFrame* frame)
1842 {
1843 HTMLFrameOwnerElement* frameOwner = frame->document()->ownerElement();
1844 if (!frameOwner)
1845 return;
1846
1847 int frameOwnerId = m_documentNodeToIdMap->get(frameOwner);
1848 if (!frameOwnerId)
1849 return;
1850
1851 // Re-add frame owner element together with its new children.
1852 int parentId = m_documentNodeToIdMap->get(innerParentNode(frameOwner));
1853 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1854 unbind(frameOwner, m_documentNodeToIdMap.get());
1855
1856 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, m_documentNodeToIdMap.get());
1857 Node* previousSibling = innerPreviousSibling(frameOwner);
1858 int prevId = previousSibling ? m_documentNodeToIdMap->get(previousSibling) : 0;
1859 m_frontend->childNodeInserted(parentId, prevId, value.release());
1860 }
1861
didCommitLoad(LocalFrame * frame,DocumentLoader * loader)1862 void InspectorDOMAgent::didCommitLoad(LocalFrame* frame, DocumentLoader* loader)
1863 {
1864 // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame()
1865 // then all we need to check here is loader->frame()->isMainFrame()
1866 // and we don't need "frame" at all.
1867 if (!frame->page()->mainFrame()->isLocalFrame())
1868 return;
1869 LocalFrame* mainFrame = frame->page()->deprecatedLocalMainFrame();
1870 if (loader->frame() != mainFrame) {
1871 invalidateFrameOwnerElement(loader->frame());
1872 return;
1873 }
1874
1875 setDocument(mainFrame->document());
1876 }
1877
didInsertDOMNode(Node * node)1878 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1879 {
1880 if (isWhitespace(node))
1881 return;
1882
1883 // We could be attaching existing subtree. Forget the bindings.
1884 unbind(node, m_documentNodeToIdMap.get());
1885
1886 ContainerNode* parent = node->parentNode();
1887 if (!parent)
1888 return;
1889 int parentId = m_documentNodeToIdMap->get(parent);
1890 // Return if parent is not mapped yet.
1891 if (!parentId)
1892 return;
1893
1894 if (!m_childrenRequested.contains(parentId)) {
1895 // No children are mapped yet -> only notify on changes of child count.
1896 int count = m_cachedChildCount.get(parentId) + 1;
1897 m_cachedChildCount.set(parentId, count);
1898 m_frontend->childNodeCountUpdated(parentId, count);
1899 } else {
1900 // Children have been requested -> return value of a new child.
1901 Node* prevSibling = innerPreviousSibling(node);
1902 int prevId = prevSibling ? m_documentNodeToIdMap->get(prevSibling) : 0;
1903 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, m_documentNodeToIdMap.get());
1904 m_frontend->childNodeInserted(parentId, prevId, value.release());
1905 }
1906 }
1907
willRemoveDOMNode(Node * node)1908 void InspectorDOMAgent::willRemoveDOMNode(Node* node)
1909 {
1910 if (isWhitespace(node))
1911 return;
1912
1913 ContainerNode* parent = node->parentNode();
1914
1915 // If parent is not mapped yet -> ignore the event.
1916 if (!m_documentNodeToIdMap->contains(parent))
1917 return;
1918
1919 int parentId = m_documentNodeToIdMap->get(parent);
1920
1921 if (!m_childrenRequested.contains(parentId)) {
1922 // No children are mapped yet -> only notify on changes of child count.
1923 int count = m_cachedChildCount.get(parentId) - 1;
1924 m_cachedChildCount.set(parentId, count);
1925 m_frontend->childNodeCountUpdated(parentId, count);
1926 } else {
1927 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap->get(node));
1928 }
1929 unbind(node, m_documentNodeToIdMap.get());
1930 }
1931
willModifyDOMAttr(Element *,const AtomicString & oldValue,const AtomicString & newValue)1932 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue)
1933 {
1934 m_suppressAttributeModifiedEvent = (oldValue == newValue);
1935 }
1936
didModifyDOMAttr(Element * element,const AtomicString & name,const AtomicString & value)1937 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value)
1938 {
1939 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
1940 m_suppressAttributeModifiedEvent = false;
1941 if (shouldSuppressEvent)
1942 return;
1943
1944 int id = boundNodeId(element);
1945 // If node is not mapped yet -> ignore the event.
1946 if (!id)
1947 return;
1948
1949 if (m_domListener)
1950 m_domListener->didModifyDOMAttr(element);
1951
1952 m_frontend->attributeModified(id, name, value);
1953 }
1954
didRemoveDOMAttr(Element * element,const AtomicString & name)1955 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name)
1956 {
1957 int id = boundNodeId(element);
1958 // If node is not mapped yet -> ignore the event.
1959 if (!id)
1960 return;
1961
1962 if (m_domListener)
1963 m_domListener->didModifyDOMAttr(element);
1964
1965 m_frontend->attributeRemoved(id, name);
1966 }
1967
styleAttributeInvalidated(const WillBeHeapVector<RawPtrWillBeMember<Element>> & elements)1968 void InspectorDOMAgent::styleAttributeInvalidated(const WillBeHeapVector<RawPtrWillBeMember<Element> >& elements)
1969 {
1970 RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create();
1971 for (unsigned i = 0, size = elements.size(); i < size; ++i) {
1972 Element* element = elements.at(i);
1973 int id = boundNodeId(element);
1974 // If node is not mapped yet -> ignore the event.
1975 if (!id)
1976 continue;
1977
1978 if (m_domListener)
1979 m_domListener->didModifyDOMAttr(element);
1980 nodeIds->addItem(id);
1981 }
1982 m_frontend->inlineStyleInvalidated(nodeIds.release());
1983 }
1984
characterDataModified(CharacterData * characterData)1985 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1986 {
1987 int id = m_documentNodeToIdMap->get(characterData);
1988 if (!id) {
1989 // Push text node if it is being created.
1990 didInsertDOMNode(characterData);
1991 return;
1992 }
1993 m_frontend->characterDataModified(id, characterData->data());
1994 }
1995
didInvalidateStyleAttr(Node * node)1996 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1997 {
1998 int id = m_documentNodeToIdMap->get(node);
1999 // If node is not mapped yet -> ignore the event.
2000 if (!id)
2001 return;
2002
2003 if (!m_revalidateStyleAttrTask)
2004 m_revalidateStyleAttrTask = adoptPtrWillBeNoop(new RevalidateStyleAttributeTask(this));
2005 m_revalidateStyleAttrTask->scheduleFor(toElement(node));
2006 }
2007
didPushShadowRoot(Element * host,ShadowRoot * root)2008 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root)
2009 {
2010 if (!host->ownerDocument())
2011 return;
2012
2013 int hostId = m_documentNodeToIdMap->get(host);
2014 if (!hostId)
2015 return;
2016
2017 pushChildNodesToFrontend(hostId, 1);
2018 m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, m_documentNodeToIdMap.get()));
2019 }
2020
willPopShadowRoot(Element * host,ShadowRoot * root)2021 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root)
2022 {
2023 if (!host->ownerDocument())
2024 return;
2025
2026 int hostId = m_documentNodeToIdMap->get(host);
2027 int rootId = m_documentNodeToIdMap->get(root);
2028 if (hostId && rootId)
2029 m_frontend->shadowRootPopped(hostId, rootId);
2030 }
2031
frameDocumentUpdated(LocalFrame * frame)2032 void InspectorDOMAgent::frameDocumentUpdated(LocalFrame* frame)
2033 {
2034 Document* document = frame->document();
2035 if (!document)
2036 return;
2037
2038 Page* page = frame->page();
2039 ASSERT(page);
2040 if (frame != page->mainFrame())
2041 return;
2042
2043 // Only update the main frame document, nested frame document updates are not required
2044 // (will be handled by invalidateFrameOwnerElement()).
2045 setDocument(document);
2046 }
2047
pseudoElementCreated(PseudoElement * pseudoElement)2048 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement)
2049 {
2050 Element* parent = pseudoElement->parentOrShadowHostElement();
2051 if (!parent)
2052 return;
2053 int parentId = m_documentNodeToIdMap->get(parent);
2054 if (!parentId)
2055 return;
2056
2057 pushChildNodesToFrontend(parentId, 1);
2058 m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, m_documentNodeToIdMap.get()));
2059 }
2060
pseudoElementDestroyed(PseudoElement * pseudoElement)2061 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement)
2062 {
2063 int pseudoElementId = m_documentNodeToIdMap->get(pseudoElement);
2064 if (!pseudoElementId)
2065 return;
2066
2067 // If a PseudoElement is bound, its parent element must be bound, too.
2068 Element* parent = pseudoElement->parentOrShadowHostElement();
2069 ASSERT(parent);
2070 int parentId = m_documentNodeToIdMap->get(parent);
2071 ASSERT(parentId);
2072
2073 unbind(pseudoElement, m_documentNodeToIdMap.get());
2074 m_frontend->pseudoElementRemoved(parentId, pseudoElementId);
2075 }
2076
shadowRootForNode(Node * node,const String & type)2077 static ShadowRoot* shadowRootForNode(Node* node, const String& type)
2078 {
2079 if (!node->isElementNode())
2080 return 0;
2081 if (type == "a")
2082 return toElement(node)->shadowRoot();
2083 if (type == "u")
2084 return toElement(node)->userAgentShadowRoot();
2085 return 0;
2086 }
2087
nodeForPath(const String & path)2088 Node* InspectorDOMAgent::nodeForPath(const String& path)
2089 {
2090 // The path is of form "1,HTML,2,BODY,1,DIV" (<index> and <nodeName> interleaved).
2091 // <index> may also be "a" (author shadow root) or "u" (user-agent shadow root),
2092 // in which case <nodeName> MUST be "#document-fragment".
2093 if (!m_document)
2094 return 0;
2095
2096 Node* node = m_document.get();
2097 Vector<String> pathTokens;
2098 path.split(',', pathTokens);
2099 if (!pathTokens.size())
2100 return 0;
2101 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
2102 bool success = true;
2103 String& indexValue = pathTokens[i];
2104 unsigned childNumber = indexValue.toUInt(&success);
2105 Node* child;
2106 if (!success) {
2107 child = shadowRootForNode(node, indexValue);
2108 } else {
2109 if (childNumber >= innerChildNodeCount(node))
2110 return 0;
2111
2112 child = innerFirstChild(node);
2113 }
2114 String childName = pathTokens[i + 1];
2115 for (size_t j = 0; child && j < childNumber; ++j)
2116 child = innerNextSibling(child);
2117
2118 if (!child || child->nodeName() != childName)
2119 return 0;
2120 node = child;
2121 }
2122 return node;
2123 }
2124
pushNodeByPathToFrontend(ErrorString * errorString,const String & path,int * nodeId)2125 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId)
2126 {
2127 if (Node* node = nodeForPath(path))
2128 *nodeId = pushNodePathToFrontend(node);
2129 else
2130 *errorString = "No node with given path found";
2131 }
2132
pushNodesByBackendIdsToFrontend(ErrorString * errorString,const RefPtr<JSONArray> & backendNodeIds,RefPtr<TypeBuilder::Array<int>> & result)2133 void InspectorDOMAgent::pushNodesByBackendIdsToFrontend(ErrorString* errorString, const RefPtr<JSONArray>& backendNodeIds, RefPtr<TypeBuilder::Array<int> >& result)
2134 {
2135 result = TypeBuilder::Array<int>::create();
2136 for (JSONArray::const_iterator it = backendNodeIds->begin(); it != backendNodeIds->end(); ++it) {
2137 int backendNodeId;
2138
2139 if (!(*it)->asNumber(&backendNodeId)) {
2140 *errorString = "Invalid argument type";
2141 return;
2142 }
2143
2144 Node* node = InspectorNodeIds::nodeForId(backendNodeId);
2145 if (node && node->document().page() == m_pageAgent->page())
2146 result->addItem(pushNodePathToFrontend(node));
2147 else
2148 result->addItem(0);
2149 }
2150 }
2151
getRelayoutBoundary(ErrorString * errorString,int nodeId,int * relayoutBoundaryNodeId)2152 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId)
2153 {
2154 Node* node = assertNode(errorString, nodeId);
2155 if (!node)
2156 return;
2157 RenderObject* renderer = node->renderer();
2158 if (!renderer) {
2159 *errorString = "No renderer for node, perhaps orphan or hidden node";
2160 return;
2161 }
2162 while (renderer && !renderer->isDocumentElement() && !renderer->isRelayoutBoundaryForInspector())
2163 renderer = renderer->container();
2164 Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument();
2165 *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode);
2166 }
2167
resolveNode(Node * node,const String & objectGroup)2168 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2169 {
2170 Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument();
2171 LocalFrame* frame = document ? document->frame() : 0;
2172 if (!frame)
2173 return nullptr;
2174
2175 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(ScriptState::forMainWorld(frame));
2176 if (injectedScript.isEmpty())
2177 return nullptr;
2178
2179 return injectedScript.wrapNode(node, objectGroup);
2180 }
2181
pushDocumentUponHandlelessOperation(ErrorString * errorString)2182 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString)
2183 {
2184 if (!m_documentNodeToIdMap->contains(m_document)) {
2185 RefPtr<TypeBuilder::DOM::Node> root;
2186 getDocument(errorString, root);
2187 return errorString->isEmpty();
2188 }
2189 return true;
2190 }
2191
trace(Visitor * visitor)2192 void InspectorDOMAgent::trace(Visitor* visitor)
2193 {
2194 visitor->trace(m_domListener);
2195 visitor->trace(m_pageAgent);
2196 visitor->trace(m_injectedScriptManager);
2197 #if ENABLE(OILPAN)
2198 visitor->trace(m_documentNodeToIdMap);
2199 visitor->trace(m_danglingNodeToIdMaps);
2200 visitor->trace(m_idToNode);
2201 visitor->trace(m_idToNodesMap);
2202 visitor->trace(m_document);
2203 visitor->trace(m_revalidateStyleAttrTask);
2204 visitor->trace(m_searchResults);
2205 #endif
2206 visitor->trace(m_history);
2207 visitor->trace(m_domEditor);
2208 visitor->trace(m_listener);
2209 InspectorBaseAgent::trace(visitor);
2210 }
2211
2212 } // namespace blink
2213
2214