1/*
2 * Copyright (C) 2009-2017 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 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 "InspectorDOMAgent.h"
33
34#include "AXObjectCache.h"
35#include "AccessibilityNodeObject.h"
36#include "Attr.h"
37#include "CSSComputedStyleDeclaration.h"
38#include "CSSPropertyNames.h"
39#include "CSSPropertySourceData.h"
40#include "CSSRule.h"
41#include "CSSRuleList.h"
42#include "CSSStyleRule.h"
43#include "CSSStyleSheet.h"
44#include "CharacterData.h"
45#include "CommandLineAPIHost.h"
46#include "ContainerNode.h"
47#include "Cookie.h"
48#include "CookieJar.h"
49#include "DOMEditor.h"
50#include "DOMException.h"
51#include "DOMPatchSupport.h"
52#include "DOMWindow.h"
53#include "Document.h"
54#include "DocumentType.h"
55#include "Editing.h"
56#include "Element.h"
57#include "Event.h"
58#include "EventListener.h"
59#include "EventNames.h"
60#include "Frame.h"
61#include "FrameTree.h"
62#include "FrameView.h"
63#include "FullscreenManager.h"
64#include "HTMLElement.h"
65#include "HTMLFrameOwnerElement.h"
66#include "HTMLMediaElement.h"
67#include "HTMLNames.h"
68#include "HTMLParserIdioms.h"
69#include "HTMLScriptElement.h"
70#include "HTMLStyleElement.h"
71#include "HTMLTemplateElement.h"
72#include "HTMLVideoElement.h"
73#include "HitTestResult.h"
74#include "InspectorCSSAgent.h"
75#include "InspectorClient.h"
76#include "InspectorController.h"
77#include "InspectorHistory.h"
78#include "InspectorNodeFinder.h"
79#include "InspectorOverlay.h"
80#include "InspectorPageAgent.h"
81#include "InstrumentingAgents.h"
82#include "IntRect.h"
83#include "JSDOMBindingSecurity.h"
84#include "JSEventListener.h"
85#include "JSNode.h"
86#include "MutationEvent.h"
87#include "Node.h"
88#include "NodeList.h"
89#include "Page.h"
90#include "Pasteboard.h"
91#include "PseudoElement.h"
92#include "RenderStyle.h"
93#include "RenderStyleConstants.h"
94#include "ScriptState.h"
95#include "ShadowRoot.h"
96#include "StaticNodeList.h"
97#include "StyleProperties.h"
98#include "StyleResolver.h"
99#include "StyleSheetList.h"
100#include "Text.h"
101#include "TextNodeTraversal.h"
102#include "Timer.h"
103#include "VideoPlaybackQuality.h"
104#include "WebInjectedScriptManager.h"
105#include "XPathResult.h"
106#include "markup.h"
107#include <JavaScriptCore/IdentifiersFactory.h>
108#include <JavaScriptCore/InjectedScript.h>
109#include <JavaScriptCore/InjectedScriptManager.h>
110#include <JavaScriptCore/JSCInlines.h>
111#include <pal/crypto/CryptoDigest.h>
112#include <wtf/text/Base64.h>
113#include <wtf/text/CString.h>
114#include <wtf/text/WTFString.h>
115
116namespace WebCore {
117
118using namespace Inspector;
119
120using namespace HTMLNames;
121
122static const size_t maxTextSize = 10000;
123static const UChar ellipsisUChar[] = { 0x2026, 0 };
124
125static Color parseColor(const JSON::Object* colorObject)
126{
127 if (!colorObject)
128 return Color::transparent;
129
130 int r = 0;
131 int g = 0;
132 int b = 0;
133 if (!colorObject->getInteger("r", r) || !colorObject->getInteger("g", g) || !colorObject->getInteger("b", b))
134 return Color::transparent;
135
136 double a = 1.0;
137 if (!colorObject->getDouble("a", a))
138 return Color(r, g, b);
139
140 // Clamp alpha to the [0..1] range.
141 if (a < 0)
142 a = 0;
143 else if (a > 1)
144 a = 1;
145
146 return Color(r, g, b, static_cast<int>(a * 255));
147}
148
149static Color parseConfigColor(const String& fieldName, const JSON::Object* configObject)
150{
151 RefPtr<JSON::Object> colorObject;
152 configObject->getObject(fieldName, colorObject);
153
154 return parseColor(colorObject.get());
155}
156
157static bool parseQuad(const JSON::Array& quadArray, FloatQuad* quad)
158{
159 const size_t coordinatesInQuad = 8;
160 double coordinates[coordinatesInQuad];
161 if (quadArray.length() != coordinatesInQuad)
162 return false;
163 for (size_t i = 0; i < coordinatesInQuad; ++i) {
164 if (!quadArray.get(i)->asDouble(*(coordinates + i)))
165 return false;
166 }
167 quad->setP1(FloatPoint(coordinates[0], coordinates[1]));
168 quad->setP2(FloatPoint(coordinates[2], coordinates[3]));
169 quad->setP3(FloatPoint(coordinates[4], coordinates[5]));
170 quad->setP4(FloatPoint(coordinates[6], coordinates[7]));
171
172 return true;
173}
174
175class RevalidateStyleAttributeTask {
176 WTF_MAKE_FAST_ALLOCATED;
177public:
178 RevalidateStyleAttributeTask(InspectorDOMAgent*);
179 void scheduleFor(Element*);
180 void reset() { m_timer.stop(); }
181 void timerFired();
182
183private:
184 InspectorDOMAgent* m_domAgent;
185 Timer m_timer;
186 HashSet<RefPtr<Element>> m_elements;
187};
188
189RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
190 : m_domAgent(domAgent)
191 , m_timer(*this, &RevalidateStyleAttributeTask::timerFired)
192{
193}
194
195void RevalidateStyleAttributeTask::scheduleFor(Element* element)
196{
197 m_elements.add(element);
198 if (!m_timer.isActive())
199 m_timer.startOneShot(0_s);
200}
201
202void RevalidateStyleAttributeTask::timerFired()
203{
204 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
205 Vector<Element*> elements;
206 for (auto& element : m_elements)
207 elements.append(element.get());
208 m_domAgent->styleAttributeInvalidated(elements);
209
210 m_elements.clear();
211}
212
213class InspectableNode final : public CommandLineAPIHost::InspectableObject {
214public:
215 explicit InspectableNode(Node* node)
216 : m_node(node)
217 {
218 }
219
220 JSC::JSValue get(JSC::ExecState& state) final
221 {
222 return InspectorDOMAgent::nodeAsScriptValue(state, m_node.get());
223 }
224private:
225 RefPtr<Node> m_node;
226};
227
228class EventFiredCallback final : public EventListener {
229public:
230 static Ref<EventFiredCallback> create(InspectorDOMAgent& domAgent)
231 {
232 return adoptRef(*new EventFiredCallback(domAgent));
233 }
234
235 bool operator==(const EventListener& other) const final
236 {
237 return this == &other;
238 }
239
240 void handleEvent(ScriptExecutionContext&, Event& event) final
241 {
242 if (!is<Node>(event.target()) || m_domAgent.m_dispatchedEvents.contains(&event))
243 return;
244
245 auto* node = downcast<Node>(event.target());
246 int nodeId = m_domAgent.pushNodePathToFrontend(node);
247 if (!nodeId)
248 return;
249
250 m_domAgent.m_dispatchedEvents.add(&event);
251
252 RefPtr<JSON::Object> data = JSON::Object::create();
253
254#if ENABLE(FULLSCREEN_API)
255 if (event.type() == eventNames().webkitfullscreenchangeEvent)
256 data->setBoolean("enabled"_s, !!node->document().fullscreenManager().fullscreenElement());
257#endif // ENABLE(FULLSCREEN_API)
258
259 auto timestamp = m_domAgent.m_environment.executionStopwatch()->elapsedTime().seconds();
260 m_domAgent.m_frontendDispatcher->didFireEvent(nodeId, event.type(), timestamp, data->size() ? WTFMove(data) : nullptr);
261 }
262
263private:
264 EventFiredCallback(InspectorDOMAgent& domAgent)
265 : EventListener(EventListener::CPPEventListenerType)
266 , m_domAgent(domAgent)
267 {
268 }
269
270 InspectorDOMAgent& m_domAgent;
271};
272
273String InspectorDOMAgent::toErrorString(ExceptionCode ec)
274{
275 return ec ? String(DOMException::name(ec)) : emptyString();
276}
277
278String InspectorDOMAgent::toErrorString(Exception&& exception)
279{
280 return DOMException::name(exception.code());
281}
282
283InspectorDOMAgent::InspectorDOMAgent(PageAgentContext& context, InspectorOverlay* overlay)
284 : InspectorAgentBase("DOM"_s, context)
285 , m_injectedScriptManager(context.injectedScriptManager)
286 , m_frontendDispatcher(std::make_unique<Inspector::DOMFrontendDispatcher>(context.frontendRouter))
287 , m_backendDispatcher(Inspector::DOMBackendDispatcher::create(context.backendDispatcher, this))
288 , m_inspectedPage(context.inspectedPage)
289 , m_overlay(overlay)
290#if ENABLE(VIDEO)
291 , m_mediaMetricsTimer(*this, &InspectorDOMAgent::mediaMetricsTimerFired)
292#endif
293{
294}
295
296InspectorDOMAgent::~InspectorDOMAgent()
297{
298 reset();
299 ASSERT(!m_searchingForNode);
300}
301
302void InspectorDOMAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
303{
304 m_history = std::make_unique<InspectorHistory>();
305 m_domEditor = std::make_unique<DOMEditor>(*m_history);
306
307 m_instrumentingAgents.setInspectorDOMAgent(this);
308 m_document = m_inspectedPage.mainFrame().document();
309
310#if ENABLE(VIDEO)
311 if (m_document)
312 addEventListenersToNode(*m_document);
313
314 for (auto* mediaElement : HTMLMediaElement::allMediaElements())
315 addEventListenersToNode(*mediaElement);
316#endif
317}
318
319void InspectorDOMAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
320{
321 m_history.reset();
322 m_domEditor.reset();
323 m_nodeToFocus = nullptr;
324 m_mousedOverNode = nullptr;
325 m_inspectedNode = nullptr;
326
327 ErrorString unused;
328 setSearchingForNode(unused, false, nullptr);
329 hideHighlight(unused);
330
331 m_instrumentingAgents.setInspectorDOMAgent(nullptr);
332 m_documentRequested = false;
333 reset();
334}
335
336Vector<Document*> InspectorDOMAgent::documents()
337{
338 Vector<Document*> result;
339 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
340 Document* document = frame->document();
341 if (!document)
342 continue;
343 result.append(document);
344 }
345 return result;
346}
347
348void InspectorDOMAgent::reset()
349{
350 if (m_history)
351 m_history->reset();
352 m_searchResults.clear();
353 discardBindings();
354 if (m_revalidateStyleAttrTask)
355 m_revalidateStyleAttrTask->reset();
356 m_document = nullptr;
357}
358
359void InspectorDOMAgent::setDocument(Document* document)
360{
361 if (document == m_document.get())
362 return;
363
364 reset();
365
366 m_document = document;
367
368 if (!m_documentRequested)
369 return;
370
371 // Immediately communicate null document or document that has finished loading.
372 if (!document || !document->parsing())
373 m_frontendDispatcher->documentUpdated();
374}
375
376void InspectorDOMAgent::releaseDanglingNodes()
377{
378 m_danglingNodeToIdMaps.clear();
379}
380
381int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
382{
383 int id = nodesMap->get(node);
384 if (id)
385 return id;
386 id = m_lastNodeId++;
387 nodesMap->set(node, id);
388 m_idToNode.set(id, node);
389 m_idToNodesMap.set(id, nodesMap);
390 return id;
391}
392
393void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
394{
395 int id = nodesMap->get(node);
396 if (!id)
397 return;
398
399 m_idToNode.remove(id);
400
401 if (node->isFrameOwnerElement()) {
402 const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
403 if (Document* contentDocument = frameOwner->contentDocument())
404 unbind(contentDocument, nodesMap);
405 }
406
407 if (is<Element>(*node)) {
408 Element& element = downcast<Element>(*node);
409 if (ShadowRoot* root = element.shadowRoot())
410 unbind(root, nodesMap);
411 if (PseudoElement* beforeElement = element.beforePseudoElement())
412 unbind(beforeElement, nodesMap);
413 if (PseudoElement* afterElement = element.afterPseudoElement())
414 unbind(afterElement, nodesMap);
415 }
416
417 nodesMap->remove(node);
418
419 if (auto* cssAgent = m_instrumentingAgents.inspectorCSSAgent())
420 cssAgent->didRemoveDOMNode(*node, id);
421
422 if (m_childrenRequested.remove(id)) {
423 // FIXME: Would be better to do this iteratively rather than recursively.
424 for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
425 unbind(child, nodesMap);
426 }
427}
428
429Node* InspectorDOMAgent::assertNode(ErrorString& errorString, int nodeId)
430{
431 Node* node = nodeForId(nodeId);
432 if (!node) {
433 errorString = "Could not find node with given id"_s;
434 return nullptr;
435 }
436 return node;
437}
438
439Document* InspectorDOMAgent::assertDocument(ErrorString& errorString, int nodeId)
440{
441 Node* node = assertNode(errorString, nodeId);
442 if (!node)
443 return nullptr;
444 if (!is<Document>(*node)) {
445 errorString = "Document is not available"_s;
446 return nullptr;
447 }
448 return downcast<Document>(node);
449}
450
451Element* InspectorDOMAgent::assertElement(ErrorString& errorString, int nodeId)
452{
453 Node* node = assertNode(errorString, nodeId);
454 if (!node)
455 return nullptr;
456 if (!is<Element>(*node)) {
457 errorString = "Node is not an Element"_s;
458 return nullptr;
459 }
460 return downcast<Element>(node);
461}
462
463Node* InspectorDOMAgent::assertEditableNode(ErrorString& errorString, int nodeId)
464{
465 Node* node = assertNode(errorString, nodeId);
466 if (!node)
467 return nullptr;
468 if (node->isInUserAgentShadowTree()) {
469 errorString = "Cannot edit nodes in user agent shadow trees"_s;
470 return nullptr;
471 }
472 if (node->isPseudoElement()) {
473 errorString = "Cannot edit pseudo elements"_s;
474 return nullptr;
475 }
476 return node;
477}
478
479Element* InspectorDOMAgent::assertEditableElement(ErrorString& errorString, int nodeId)
480{
481 Element* element = assertElement(errorString, nodeId);
482 if (!element)
483 return nullptr;
484 if (element->isInUserAgentShadowTree()) {
485 errorString = "Cannot edit elements in user agent shadow trees"_s;
486 return nullptr;
487 }
488 if (element->isPseudoElement()) {
489 errorString = "Cannot edit pseudo elements"_s;
490 return nullptr;
491 }
492 return element;
493}
494
495void InspectorDOMAgent::getDocument(ErrorString& errorString, RefPtr<Inspector::Protocol::DOM::Node>& root)
496{
497 m_documentRequested = true;
498
499 if (!m_document) {
500 errorString = "Document is not available"_s;
501 return;
502 }
503
504 // Reset backend state.
505 RefPtr<Document> document = m_document;
506 reset();
507 m_document = document;
508
509 root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
510
511 if (m_nodeToFocus)
512 focusNode();
513}
514
515void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth)
516{
517 Node* node = nodeForId(nodeId);
518 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
519 return;
520
521 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
522
523 if (m_childrenRequested.contains(nodeId)) {
524 if (depth <= 1)
525 return;
526
527 depth--;
528
529 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
530 int childNodeId = nodeMap->get(node);
531 ASSERT(childNodeId);
532 pushChildNodesToFrontend(childNodeId, depth);
533 }
534
535 return;
536 }
537
538 auto children = buildArrayForContainerChildren(node, depth, nodeMap);
539 m_frontendDispatcher->setChildNodes(nodeId, WTFMove(children));
540}
541
542void InspectorDOMAgent::discardBindings()
543{
544 m_documentNodeToIdMap.clear();
545 m_idToNode.clear();
546 m_dispatchedEvents.clear();
547 m_eventListenerEntries.clear();
548 releaseDanglingNodes();
549 m_childrenRequested.clear();
550}
551
552int InspectorDOMAgent::pushNodeToFrontend(ErrorString& errorString, int documentNodeId, Node* nodeToPush)
553{
554 Document* document = assertDocument(errorString, documentNodeId);
555 if (!document)
556 return 0;
557 if (&nodeToPush->document() != document) {
558 errorString = "Node is not part of the document with given id"_s;
559 return 0;
560 }
561
562 return pushNodePathToFrontend(nodeToPush);
563}
564
565Node* InspectorDOMAgent::nodeForId(int id)
566{
567 if (!m_idToNode.isValidKey(id))
568 return nullptr;
569
570 return m_idToNode.get(id);
571}
572
573void InspectorDOMAgent::requestChildNodes(ErrorString& errorString, int nodeId, const int* depth)
574{
575 int sanitizedDepth;
576
577 if (!depth)
578 sanitizedDepth = 1;
579 else if (*depth == -1)
580 sanitizedDepth = INT_MAX;
581 else if (*depth > 0)
582 sanitizedDepth = *depth;
583 else {
584 errorString = "Please provide a positive integer as a depth or -1 for entire subtree"_s;
585 return;
586 }
587
588 pushChildNodesToFrontend(nodeId, sanitizedDepth);
589}
590
591void InspectorDOMAgent::querySelector(ErrorString& errorString, int nodeId, const String& selectors, int* elementId)
592{
593 *elementId = 0;
594 Node* node = assertNode(errorString, nodeId);
595 if (!node)
596 return;
597 if (!is<ContainerNode>(*node)) {
598 assertElement(errorString, nodeId);
599 return;
600 }
601
602 auto queryResult = downcast<ContainerNode>(*node).querySelector(selectors);
603 if (queryResult.hasException()) {
604 errorString = "DOM Error while querying"_s;
605 return;
606 }
607
608 if (auto* element = queryResult.releaseReturnValue())
609 *elementId = pushNodePathToFrontend(element);
610}
611
612void InspectorDOMAgent::querySelectorAll(ErrorString& errorString, int nodeId, const String& selectors, RefPtr<JSON::ArrayOf<int>>& result)
613{
614 Node* node = assertNode(errorString, nodeId);
615 if (!node)
616 return;
617 if (!is<ContainerNode>(*node)) {
618 assertElement(errorString, nodeId);
619 return;
620 }
621
622 auto queryResult = downcast<ContainerNode>(*node).querySelectorAll(selectors);
623 if (queryResult.hasException()) {
624 errorString = "DOM Error while querying"_s;
625 return;
626 }
627
628 auto nodes = queryResult.releaseReturnValue();
629 result = JSON::ArrayOf<int>::create();
630 for (unsigned i = 0; i < nodes->length(); ++i)
631 result->addItem(pushNodePathToFrontend(nodes->item(i)));
632}
633
634int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
635{
636 ASSERT(nodeToPush); // Invalid input
637
638 if (!m_document)
639 return 0;
640 if (!m_documentNodeToIdMap.contains(m_document))
641 return 0;
642
643 // Return id in case the node is known.
644 int result = m_documentNodeToIdMap.get(nodeToPush);
645 if (result)
646 return result;
647
648 Node* node = nodeToPush;
649 Vector<Node*> path;
650 NodeToIdMap* danglingMap = 0;
651
652 while (true) {
653 Node* parent = innerParentNode(node);
654 if (!parent) {
655 // Node being pushed is detached -> push subtree root.
656 auto newMap = std::make_unique<NodeToIdMap>();
657 danglingMap = newMap.get();
658 m_danglingNodeToIdMaps.append(newMap.release());
659 auto children = JSON::ArrayOf<Inspector::Protocol::DOM::Node>::create();
660 children->addItem(buildObjectForNode(node, 0, danglingMap));
661 m_frontendDispatcher->setChildNodes(0, WTFMove(children));
662 break;
663 } else {
664 path.append(parent);
665 if (m_documentNodeToIdMap.get(parent))
666 break;
667 else
668 node = parent;
669 }
670 }
671
672 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
673 for (int i = path.size() - 1; i >= 0; --i) {
674 int nodeId = map->get(path.at(i));
675 ASSERT(nodeId);
676 pushChildNodesToFrontend(nodeId);
677 }
678 return map->get(nodeToPush);
679}
680
681int InspectorDOMAgent::boundNodeId(const Node* node)
682{
683 return m_documentNodeToIdMap.get(const_cast<Node*>(node));
684}
685
686void InspectorDOMAgent::setAttributeValue(ErrorString& errorString, int elementId, const String& name, const String& value)
687{
688 Element* element = assertEditableElement(errorString, elementId);
689 if (!element)
690 return;
691
692 m_domEditor->setAttribute(*element, name, value, errorString);
693}
694
695void InspectorDOMAgent::setAttributesAsText(ErrorString& errorString, int elementId, const String& text, const String* name)
696{
697 Element* element = assertEditableElement(errorString, elementId);
698 if (!element)
699 return;
700
701 auto parsedElement = createHTMLElement(element->document(), spanTag);
702 auto result = parsedElement.get().setInnerHTML("<span " + text + "></span>");
703 if (result.hasException()) {
704 errorString = toErrorString(result.releaseException());
705 return;
706 }
707
708 Node* child = parsedElement->firstChild();
709 if (!child) {
710 errorString = "Could not parse value as attributes"_s;
711 return;
712 }
713
714 Element* childElement = downcast<Element>(child);
715 if (!childElement->hasAttributes() && name) {
716 m_domEditor->removeAttribute(*element, *name, errorString);
717 return;
718 }
719
720 bool foundOriginalAttribute = false;
721 for (const Attribute& attribute : childElement->attributesIterator()) {
722 // Add attribute pair
723 foundOriginalAttribute = foundOriginalAttribute || (name && attribute.name().toString() == *name);
724 if (!m_domEditor->setAttribute(*element, attribute.name().toString(), attribute.value(), errorString))
725 return;
726 }
727
728 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
729 m_domEditor->removeAttribute(*element, *name, errorString);
730}
731
732void InspectorDOMAgent::removeAttribute(ErrorString& errorString, int elementId, const String& name)
733{
734 Element* element = assertEditableElement(errorString, elementId);
735 if (!element)
736 return;
737
738 m_domEditor->removeAttribute(*element, name, errorString);
739}
740
741void InspectorDOMAgent::removeNode(ErrorString& errorString, int nodeId)
742{
743 Node* node = assertEditableNode(errorString, nodeId);
744 if (!node)
745 return;
746
747 ContainerNode* parentNode = node->parentNode();
748 if (!parentNode) {
749 errorString = "Cannot remove detached node"_s;
750 return;
751 }
752
753 m_domEditor->removeChild(*parentNode, *node, errorString);
754}
755
756void InspectorDOMAgent::setNodeName(ErrorString& errorString, int nodeId, const String& tagName, int* newId)
757{
758 *newId = 0;
759
760 RefPtr<Node> oldNode = nodeForId(nodeId);
761 if (!is<Element>(oldNode))
762 return;
763
764 auto createElementResult = oldNode->document().createElementForBindings(tagName);
765 if (createElementResult.hasException())
766 return;
767 auto newElement = createElementResult.releaseReturnValue();
768
769 // Copy over the original node's attributes.
770 newElement->cloneAttributesFromElement(downcast<Element>(*oldNode));
771
772 // Copy over the original node's children.
773 RefPtr<Node> child;
774 while ((child = oldNode->firstChild())) {
775 if (!m_domEditor->insertBefore(newElement, *child, 0, errorString))
776 return;
777 }
778
779 // Replace the old node with the new node
780 RefPtr<ContainerNode> parent = oldNode->parentNode();
781 if (!m_domEditor->insertBefore(*parent, newElement.copyRef(), oldNode->nextSibling(), errorString))
782 return;
783 if (!m_domEditor->removeChild(*parent, *oldNode, errorString))
784 return;
785
786 *newId = pushNodePathToFrontend(newElement.ptr());
787 if (m_childrenRequested.contains(nodeId))
788 pushChildNodesToFrontend(*newId);
789}
790
791void InspectorDOMAgent::getOuterHTML(ErrorString& errorString, int nodeId, WTF::String* outerHTML)
792{
793 Node* node = assertNode(errorString, nodeId);
794 if (!node)
795 return;
796
797 *outerHTML = serializeFragment(*node, SerializedNodes::SubtreeIncludingNode);
798}
799
800void InspectorDOMAgent::setOuterHTML(ErrorString& errorString, int nodeId, const String& outerHTML)
801{
802 if (!nodeId) {
803 DOMPatchSupport { *m_domEditor, *m_document }.patchDocument(outerHTML);
804 return;
805 }
806
807 Node* node = assertEditableNode(errorString, nodeId);
808 if (!node)
809 return;
810
811 Document& document = node->document();
812 if (!document.isHTMLDocument() && !document.isXMLDocument()) {
813 errorString = "Not an HTML/XML document"_s;
814 return;
815 }
816
817 Node* newNode = nullptr;
818 if (!m_domEditor->setOuterHTML(*node, outerHTML, newNode, errorString))
819 return;
820
821 if (!newNode) {
822 // The only child node has been deleted.
823 return;
824 }
825
826 int newId = pushNodePathToFrontend(newNode);
827
828 bool childrenRequested = m_childrenRequested.contains(nodeId);
829 if (childrenRequested)
830 pushChildNodesToFrontend(newId);
831}
832
833void InspectorDOMAgent::insertAdjacentHTML(ErrorString& errorString, int nodeId, const String& position, const String& html)
834{
835 Node* node = assertEditableNode(errorString, nodeId);
836 if (!node)
837 return;
838
839 if (!is<Element>(node)) {
840 errorString = "Can only call insertAdjacentHTML on Elements."_s;
841 return;
842 }
843
844 m_domEditor->insertAdjacentHTML(downcast<Element>(*node), position, html, errorString);
845}
846
847void InspectorDOMAgent::setNodeValue(ErrorString& errorString, int nodeId, const String& value)
848{
849 Node* node = assertEditableNode(errorString, nodeId);
850 if (!node)
851 return;
852
853 if (!is<Text>(*node)) {
854 errorString = "Can only set value of text nodes"_s;
855 return;
856 }
857
858 m_domEditor->replaceWholeText(downcast<Text>(*node), value, errorString);
859}
860
861void InspectorDOMAgent::getSupportedEventNames(ErrorString&, RefPtr<JSON::ArrayOf<String>>& eventNames)
862{
863 eventNames = JSON::ArrayOf<String>::create();
864
865#define DOM_EVENT_NAMES_ADD(name) eventNames->addItem(#name);
866 DOM_EVENT_NAMES_FOR_EACH(DOM_EVENT_NAMES_ADD)
867#undef DOM_EVENT_NAMES_ADD
868}
869
870void InspectorDOMAgent::getDataBindingsForNode(ErrorString& errorString, int /* nodeId */, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::DataBinding>>& /* dataBindings */)
871{
872 errorString = "Not supported"_s;
873}
874
875void InspectorDOMAgent::getAssociatedDataForNode(ErrorString& errorString, int /* nodeId */, Optional<String>& /* associatedData */)
876{
877 errorString = "Not supported"_s;
878}
879
880void InspectorDOMAgent::getEventListenersForNode(ErrorString& errorString, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>>& listenersArray)
881{
882 listenersArray = JSON::ArrayOf<Inspector::Protocol::DOM::EventListener>::create();
883
884 auto* node = assertNode(errorString, nodeId);
885 if (!node)
886 return;
887
888 Vector<RefPtr<EventTarget>> ancestors;
889 ancestors.append(node);
890 for (auto* ancestor = node->parentOrShadowHostNode(); ancestor; ancestor = ancestor->parentOrShadowHostNode())
891 ancestors.append(ancestor);
892 if (auto* window = node->document().domWindow())
893 ancestors.append(window);
894
895 struct EventListenerInfo {
896 RefPtr<EventTarget> eventTarget;
897 const AtomicString eventType;
898 const EventListenerVector eventListeners;
899 };
900
901 Vector<EventListenerInfo> eventInformation;
902 for (size_t i = ancestors.size(); i; --i) {
903 auto& ancestor = ancestors[i - 1];
904 for (auto& eventType : ancestor->eventTypes()) {
905 EventListenerVector filteredListeners;
906 for (auto& listener : ancestor->eventListeners(eventType)) {
907 if (listener->callback().type() == EventListener::JSEventListenerType)
908 filteredListeners.append(listener);
909 }
910 if (!filteredListeners.isEmpty())
911 eventInformation.append({ ancestor, eventType, WTFMove(filteredListeners) });
912 }
913 }
914
915 auto addListener = [&] (RegisteredEventListener& listener, const EventListenerInfo& info) {
916 int identifier = 0;
917 bool disabled = false;
918 bool hasBreakpoint = false;
919
920 for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
921 if (inspectorEventListener.matches(*info.eventTarget, info.eventType, listener.callback(), listener.useCapture())) {
922 identifier = inspectorEventListener.identifier;
923 disabled = inspectorEventListener.disabled;
924 hasBreakpoint = inspectorEventListener.hasBreakpoint;
925 break;
926 }
927 }
928
929 if (!identifier) {
930 InspectorEventListener inspectorEventListener(m_lastEventListenerId++, *info.eventTarget, info.eventType, listener.callback(), listener.useCapture());
931
932 identifier = inspectorEventListener.identifier;
933 disabled = inspectorEventListener.disabled;
934 hasBreakpoint = inspectorEventListener.hasBreakpoint;
935
936 m_eventListenerEntries.add(identifier, inspectorEventListener);
937 }
938
939 listenersArray->addItem(buildObjectForEventListener(listener, identifier, *info.eventTarget, info.eventType, disabled, hasBreakpoint));
940 };
941
942 // Get Capturing Listeners (in this order)
943 size_t eventInformationLength = eventInformation.size();
944 for (auto& info : eventInformation) {
945 for (auto& listener : info.eventListeners) {
946 if (listener->useCapture())
947 addListener(*listener, info);
948 }
949 }
950
951 // Get Bubbling Listeners (reverse order)
952 for (size_t i = eventInformationLength; i; --i) {
953 const EventListenerInfo& info = eventInformation[i - 1];
954 for (auto& listener : info.eventListeners) {
955 if (!listener->useCapture())
956 addListener(*listener, info);
957 }
958 }
959
960 if (m_inspectedNode == node)
961 m_suppressEventListenerChangedEvent = false;
962}
963
964void InspectorDOMAgent::setEventListenerDisabled(ErrorString& errorString, int eventListenerId, bool disabled)
965{
966 auto it = m_eventListenerEntries.find(eventListenerId);
967 if (it == m_eventListenerEntries.end()) {
968 errorString = "No event listener for given identifier."_s;
969 return;
970 }
971
972 it->value.disabled = disabled;
973}
974
975void InspectorDOMAgent::setBreakpointForEventListener(ErrorString& errorString, int eventListenerId)
976{
977 auto it = m_eventListenerEntries.find(eventListenerId);
978 if (it == m_eventListenerEntries.end()) {
979 errorString = "No event listener for given identifier."_s;
980 return;
981 }
982
983 it->value.hasBreakpoint = true;
984}
985
986void InspectorDOMAgent::removeBreakpointForEventListener(ErrorString& errorString, int eventListenerId)
987{
988 auto it = m_eventListenerEntries.find(eventListenerId);
989 if (it == m_eventListenerEntries.end()) {
990 errorString = "No event listener for given identifier."_s;
991 return;
992 }
993
994 it->value.hasBreakpoint = false;
995}
996
997void InspectorDOMAgent::getAccessibilityPropertiesForNode(ErrorString& errorString, int nodeId, RefPtr<Inspector::Protocol::DOM::AccessibilityProperties>& axProperties)
998{
999 Node* node = assertNode(errorString, nodeId);
1000 if (!node)
1001 return;
1002
1003 axProperties = buildObjectForAccessibilityProperties(node);
1004}
1005
1006void InspectorDOMAgent::performSearch(ErrorString& errorString, const String& query, const JSON::Array* nodeIds, const bool* caseSensitive, String* searchId, int* resultCount)
1007{
1008 // FIXME: Search works with node granularity - number of matches within node is not calculated.
1009 InspectorNodeFinder finder(query, caseSensitive && *caseSensitive);
1010
1011 if (nodeIds) {
1012 for (auto& nodeValue : *nodeIds) {
1013 if (!nodeValue) {
1014 errorString = "Invalid nodeIds item."_s;
1015 return;
1016 }
1017 int nodeId = 0;
1018 if (!nodeValue->asInteger(nodeId)) {
1019 errorString = "Invalid nodeIds item type. Expecting integer types."_s;
1020 return;
1021 }
1022 Node* node = assertNode(errorString, nodeId);
1023 if (!node) {
1024 // assertNode should have filled the errorString for us.
1025 ASSERT(errorString.length());
1026 return;
1027 }
1028 finder.performSearch(node);
1029 }
1030 } else {
1031 // There's no need to iterate the frames tree because
1032 // the search helper will go inside the frame owner elements.
1033 finder.performSearch(m_document.get());
1034 }
1035
1036 *searchId = IdentifiersFactory::createIdentifier();
1037
1038 auto& resultsVector = m_searchResults.add(*searchId, Vector<RefPtr<Node>>()).iterator->value;
1039 for (auto& result : finder.results())
1040 resultsVector.append(result);
1041
1042 *resultCount = resultsVector.size();
1043}
1044
1045void InspectorDOMAgent::getSearchResults(ErrorString& errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<JSON::ArrayOf<int>>& nodeIds)
1046{
1047 SearchResults::iterator it = m_searchResults.find(searchId);
1048 if (it == m_searchResults.end()) {
1049 errorString = "No search session with given id found"_s;
1050 return;
1051 }
1052
1053 int size = it->value.size();
1054 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) {
1055 errorString = "Invalid search result range"_s;
1056 return;
1057 }
1058
1059 nodeIds = JSON::ArrayOf<int>::create();
1060 for (int i = fromIndex; i < toIndex; ++i)
1061 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get()));
1062}
1063
1064void InspectorDOMAgent::discardSearchResults(ErrorString&, const String& searchId)
1065{
1066 m_searchResults.remove(searchId);
1067}
1068
1069bool InspectorDOMAgent::handleMousePress()
1070{
1071 if (!m_searchingForNode)
1072 return false;
1073
1074 if (Node* node = m_overlay->highlightedNode()) {
1075 inspect(node);
1076 return true;
1077 }
1078 return false;
1079}
1080
1081bool InspectorDOMAgent::handleTouchEvent(Node& node)
1082{
1083 if (!m_searchingForNode)
1084 return false;
1085 if (m_inspectModeHighlightConfig) {
1086 m_overlay->highlightNode(&node, *m_inspectModeHighlightConfig);
1087 inspect(&node);
1088 return true;
1089 }
1090 return false;
1091}
1092
1093void InspectorDOMAgent::inspect(Node* inspectedNode)
1094{
1095 ErrorString unused;
1096 RefPtr<Node> node = inspectedNode;
1097 setSearchingForNode(unused, false, nullptr);
1098
1099 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
1100 node = node->parentNode();
1101 m_nodeToFocus = node;
1102
1103 if (!m_nodeToFocus)
1104 return;
1105
1106 focusNode();
1107}
1108
1109void InspectorDOMAgent::focusNode()
1110{
1111 if (!m_documentRequested)
1112 return;
1113
1114 ASSERT(m_nodeToFocus);
1115
1116 RefPtr<Node> node = m_nodeToFocus.get();
1117 m_nodeToFocus = nullptr;
1118
1119 Frame* frame = node->document().frame();
1120 if (!frame)
1121 return;
1122
1123 JSC::ExecState* scriptState = mainWorldExecState(frame);
1124 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(scriptState);
1125 if (injectedScript.hasNoValue())
1126 return;
1127
1128 injectedScript.inspectObject(nodeAsScriptValue(*scriptState, node.get()));
1129}
1130
1131void InspectorDOMAgent::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
1132{
1133 m_mousedOverNode = result.innerNode();
1134
1135 if (!m_searchingForNode)
1136 return;
1137
1138 highlightMousedOverNode();
1139}
1140
1141void InspectorDOMAgent::highlightMousedOverNode()
1142{
1143 Node* node = m_mousedOverNode.get();
1144 while (node && node->nodeType() == Node::TEXT_NODE)
1145 node = node->parentNode();
1146 if (node && m_inspectModeHighlightConfig)
1147 m_overlay->highlightNode(node, *m_inspectModeHighlightConfig);
1148}
1149
1150void InspectorDOMAgent::setSearchingForNode(ErrorString& errorString, bool enabled, const JSON::Object* highlightInspectorObject)
1151{
1152 if (m_searchingForNode == enabled)
1153 return;
1154
1155 m_searchingForNode = enabled;
1156
1157 if (enabled) {
1158 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject);
1159 if (!m_inspectModeHighlightConfig)
1160 return;
1161 highlightMousedOverNode();
1162 } else
1163 hideHighlight(errorString);
1164
1165 m_overlay->didSetSearchingForNode(m_searchingForNode);
1166
1167 if (InspectorClient* client = m_inspectedPage.inspectorController().inspectorClient())
1168 client->elementSelectionChanged(m_searchingForNode);
1169}
1170
1171std::unique_ptr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString& errorString, const JSON::Object* highlightInspectorObject)
1172{
1173 if (!highlightInspectorObject) {
1174 errorString = "Internal error: highlight configuration parameter is missing"_s;
1175 return nullptr;
1176 }
1177
1178 auto highlightConfig = std::make_unique<HighlightConfig>();
1179 bool showInfo = false; // Default: false (do not show a tooltip).
1180 highlightInspectorObject->getBoolean("showInfo", showInfo);
1181 highlightConfig->showInfo = showInfo;
1182 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject);
1183 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject);
1184 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject);
1185 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject);
1186 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject);
1187 return highlightConfig;
1188}
1189
1190void InspectorDOMAgent::setInspectModeEnabled(ErrorString& errorString, bool enabled, const JSON::Object* highlightConfig)
1191{
1192 setSearchingForNode(errorString, enabled, highlightConfig ? highlightConfig : nullptr);
1193}
1194
1195void InspectorDOMAgent::highlightRect(ErrorString&, int x, int y, int width, int height, const JSON::Object* color, const JSON::Object* outlineColor, const bool* usePageCoordinates)
1196{
1197 auto quad = std::make_unique<FloatQuad>(FloatRect(x, y, width, height));
1198 innerHighlightQuad(WTFMove(quad), color, outlineColor, usePageCoordinates);
1199}
1200
1201void InspectorDOMAgent::highlightQuad(ErrorString& errorString, const JSON::Array& quadArray, const JSON::Object* color, const JSON::Object* outlineColor, const bool* usePageCoordinates)
1202{
1203 auto quad = std::make_unique<FloatQuad>();
1204 if (!parseQuad(quadArray, quad.get())) {
1205 errorString = "Invalid Quad format"_s;
1206 return;
1207 }
1208 innerHighlightQuad(WTFMove(quad), color, outlineColor, usePageCoordinates);
1209}
1210
1211void InspectorDOMAgent::innerHighlightQuad(std::unique_ptr<FloatQuad> quad, const JSON::Object* color, const JSON::Object* outlineColor, const bool* usePageCoordinates)
1212{
1213 auto highlightConfig = std::make_unique<HighlightConfig>();
1214 highlightConfig->content = parseColor(color);
1215 highlightConfig->contentOutline = parseColor(outlineColor);
1216 highlightConfig->usePageCoordinates = usePageCoordinates ? *usePageCoordinates : false;
1217 m_overlay->highlightQuad(WTFMove(quad), *highlightConfig);
1218}
1219
1220void InspectorDOMAgent::highlightSelector(ErrorString& errorString, const JSON::Object& highlightInspectorObject, const String& selectorString, const String* frameId)
1221{
1222 RefPtr<Document> document;
1223
1224 if (frameId) {
1225 auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
1226 if (!pageAgent) {
1227 errorString = "Missing Page agent"_s;
1228 return;
1229 }
1230
1231 Frame* frame = pageAgent->frameForId(*frameId);
1232 if (!frame) {
1233 errorString = "No frame for given id found"_s;
1234 return;
1235 }
1236
1237 document = frame->document();
1238 } else
1239 document = m_document;
1240
1241 if (!document) {
1242 errorString = "Document could not be found"_s;
1243 return;
1244 }
1245
1246 auto queryResult = document->querySelectorAll(selectorString);
1247 // FIXME: <https://webkit.org/b/146161> Web Inspector: DOM.highlightSelector should work for "a:visited"
1248 if (queryResult.hasException()) {
1249 errorString = "DOM Error while querying"_s;
1250 return;
1251 }
1252
1253 auto highlightConfig = highlightConfigFromInspectorObject(errorString, &highlightInspectorObject);
1254 if (!highlightConfig)
1255 return;
1256
1257 m_overlay->highlightNodeList(queryResult.releaseReturnValue(), *highlightConfig);
1258}
1259
1260void InspectorDOMAgent::highlightNode(ErrorString& errorString, const JSON::Object& highlightInspectorObject, const int* nodeId, const String* objectId)
1261{
1262 Node* node = nullptr;
1263 if (nodeId)
1264 node = assertNode(errorString, *nodeId);
1265 else if (objectId) {
1266 node = nodeForObjectId(*objectId);
1267 if (!node)
1268 errorString = "Node for given objectId not found"_s;
1269 } else
1270 errorString = "Either nodeId or objectId must be specified"_s;
1271
1272 if (!node)
1273 return;
1274
1275 std::unique_ptr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, &highlightInspectorObject);
1276 if (!highlightConfig)
1277 return;
1278
1279 m_overlay->highlightNode(node, *highlightConfig);
1280}
1281
1282void InspectorDOMAgent::highlightNodeList(ErrorString& errorString, const JSON::Array& nodeIds, const JSON::Object& highlightInspectorObject)
1283{
1284 Vector<Ref<Node>> nodes;
1285 for (auto& nodeValue : nodeIds) {
1286 if (!nodeValue) {
1287 errorString = "Invalid nodeIds item."_s;
1288 return;
1289 }
1290
1291 int nodeId = 0;
1292 if (!nodeValue->asInteger(nodeId)) {
1293 errorString = "Invalid nodeIds item type. Expecting integer types."_s;
1294 return;
1295 }
1296
1297 // In the case that a node is removed in the time between when highlightNodeList is invoked
1298 // by the frontend and it is executed by the backend, we should still attempt to highlight
1299 // as many nodes as possible. As such, we should ignore any errors generated when attempting
1300 // to get a Node from a given nodeId.
1301 ErrorString ignored;
1302 Node* node = assertNode(ignored, nodeId);
1303 if (!node)
1304 continue;
1305
1306 nodes.append(*node);
1307 }
1308
1309 std::unique_ptr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, &highlightInspectorObject);
1310 if (!highlightConfig)
1311 return;
1312
1313 m_overlay->highlightNodeList(StaticNodeList::create(WTFMove(nodes)), *highlightConfig);
1314}
1315
1316void InspectorDOMAgent::highlightFrame(ErrorString& errorString, const String& frameId, const JSON::Object* color, const JSON::Object* outlineColor)
1317{
1318 auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
1319 if (!pageAgent) {
1320 errorString = "Missing Page agent"_s;
1321 return;
1322 }
1323
1324 Frame* frame = pageAgent->assertFrame(errorString, frameId);
1325 if (!frame)
1326 return;
1327
1328 if (frame->ownerElement()) {
1329 auto highlightConfig = std::make_unique<HighlightConfig>();
1330 highlightConfig->showInfo = true; // Always show tooltips for frames.
1331 highlightConfig->content = parseColor(color);
1332 highlightConfig->contentOutline = parseColor(outlineColor);
1333 m_overlay->highlightNode(frame->ownerElement(), *highlightConfig);
1334 }
1335}
1336
1337void InspectorDOMAgent::hideHighlight(ErrorString&)
1338{
1339 m_overlay->hideHighlight();
1340}
1341
1342void InspectorDOMAgent::moveTo(ErrorString& errorString, int nodeId, int targetElementId, const int* anchorNodeId, int* newNodeId)
1343{
1344 Node* node = assertEditableNode(errorString, nodeId);
1345 if (!node)
1346 return;
1347
1348 Element* targetElement = assertEditableElement(errorString, targetElementId);
1349 if (!targetElement)
1350 return;
1351
1352 Node* anchorNode = 0;
1353 if (anchorNodeId && *anchorNodeId) {
1354 anchorNode = assertEditableNode(errorString, *anchorNodeId);
1355 if (!anchorNode)
1356 return;
1357 if (anchorNode->parentNode() != targetElement) {
1358 errorString = "Anchor node must be child of the target element"_s;
1359 return;
1360 }
1361 }
1362
1363 if (!m_domEditor->insertBefore(*targetElement, *node, anchorNode, errorString))
1364 return;
1365
1366 *newNodeId = pushNodePathToFrontend(node);
1367}
1368
1369void InspectorDOMAgent::undo(ErrorString& errorString)
1370{
1371 auto result = m_history->undo();
1372 if (result.hasException())
1373 errorString = toErrorString(result.releaseException());
1374}
1375
1376void InspectorDOMAgent::redo(ErrorString& errorString)
1377{
1378 auto result = m_history->redo();
1379 if (result.hasException())
1380 errorString = toErrorString(result.releaseException());
1381}
1382
1383void InspectorDOMAgent::markUndoableState(ErrorString&)
1384{
1385 m_history->markUndoableState();
1386}
1387
1388void InspectorDOMAgent::focus(ErrorString& errorString, int nodeId)
1389{
1390 Element* element = assertElement(errorString, nodeId);
1391 if (!element)
1392 return;
1393 if (!element->isFocusable()) {
1394 errorString = "Element is not focusable"_s;
1395 return;
1396 }
1397 element->focus();
1398}
1399
1400void InspectorDOMAgent::setInspectedNode(ErrorString& errorString, int nodeId)
1401{
1402 Node* node = nodeForId(nodeId);
1403 if (!node || node->isInUserAgentShadowTree()) {
1404 errorString = "No node with given id found"_s;
1405 return;
1406 }
1407
1408 m_inspectedNode = node;
1409
1410 if (CommandLineAPIHost* commandLineAPIHost = static_cast<WebInjectedScriptManager&>(m_injectedScriptManager).commandLineAPIHost())
1411 commandLineAPIHost->addInspectedObject(std::make_unique<InspectableNode>(node));
1412
1413 m_suppressEventListenerChangedEvent = false;
1414}
1415
1416void InspectorDOMAgent::resolveNode(ErrorString& errorString, int nodeId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result)
1417{
1418 String objectGroupName = objectGroup ? *objectGroup : emptyString();
1419 Node* node = nodeForId(nodeId);
1420 if (!node) {
1421 errorString = "No node with given id found"_s;
1422 return;
1423 }
1424 RefPtr<Inspector::Protocol::Runtime::RemoteObject> object = resolveNode(node, objectGroupName);
1425 if (!object) {
1426 errorString = "Node with given id does not belong to the document"_s;
1427 return;
1428 }
1429 result = object;
1430}
1431
1432void InspectorDOMAgent::getAttributes(ErrorString& errorString, int nodeId, RefPtr<JSON::ArrayOf<String>>& result)
1433{
1434 Element* element = assertElement(errorString, nodeId);
1435 if (!element)
1436 return;
1437
1438 result = buildArrayForElementAttributes(element);
1439}
1440
1441void InspectorDOMAgent::requestNode(ErrorString&, const String& objectId, int* nodeId)
1442{
1443 Node* node = nodeForObjectId(objectId);
1444 if (node)
1445 *nodeId = pushNodePathToFrontend(node);
1446 else
1447 *nodeId = 0;
1448}
1449
1450String InspectorDOMAgent::documentURLString(Document* document)
1451{
1452 if (!document || document->url().isNull())
1453 return emptyString();
1454 return document->url().string();
1455}
1456
1457static String documentBaseURLString(Document* document)
1458{
1459 return document->completeURL(emptyString()).string();
1460}
1461
1462static bool pseudoElementType(PseudoId pseudoId, Inspector::Protocol::DOM::PseudoType* type)
1463{
1464 switch (pseudoId) {
1465 case PseudoId::Before:
1466 *type = Inspector::Protocol::DOM::PseudoType::Before;
1467 return true;
1468 case PseudoId::After:
1469 *type = Inspector::Protocol::DOM::PseudoType::After;
1470 return true;
1471 default:
1472 return false;
1473 }
1474}
1475
1476static Inspector::Protocol::DOM::ShadowRootType shadowRootType(ShadowRootMode mode)
1477{
1478 switch (mode) {
1479 case ShadowRootMode::UserAgent:
1480 return Inspector::Protocol::DOM::ShadowRootType::UserAgent;
1481 case ShadowRootMode::Closed:
1482 return Inspector::Protocol::DOM::ShadowRootType::Closed;
1483 case ShadowRootMode::Open:
1484 return Inspector::Protocol::DOM::ShadowRootType::Open;
1485 }
1486
1487 ASSERT_NOT_REACHED();
1488 return Inspector::Protocol::DOM::ShadowRootType::UserAgent;
1489}
1490
1491static Inspector::Protocol::DOM::CustomElementState customElementState(const Element& element)
1492{
1493 if (element.isDefinedCustomElement())
1494 return Inspector::Protocol::DOM::CustomElementState::Custom;
1495 if (element.isFailedCustomElement())
1496 return Inspector::Protocol::DOM::CustomElementState::Failed;
1497 if (element.isUndefinedCustomElement() || element.isCustomElementUpgradeCandidate())
1498 return Inspector::Protocol::DOM::CustomElementState::Waiting;
1499 return Inspector::Protocol::DOM::CustomElementState::Builtin;
1500}
1501
1502static String computeContentSecurityPolicySHA256Hash(const Element& element)
1503{
1504 // FIXME: Compute the digest with respect to the raw bytes received from the page.
1505 // See <https://bugs.webkit.org/show_bug.cgi?id=155184>.
1506 TextEncoding documentEncoding = element.document().textEncoding();
1507 const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding();
1508 auto content = encodingToUse.encode(TextNodeTraversal::contentsAsString(element), UnencodableHandling::Entities);
1509 auto cryptoDigest = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
1510 cryptoDigest->addBytes(content.data(), content.size());
1511 auto digest = cryptoDigest->computeHash();
1512 return makeString("sha256-", base64Encode(digest.data(), digest.size()));
1513}
1514
1515Ref<Inspector::Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1516{
1517 int id = bind(node, nodesMap);
1518 String nodeName;
1519 String localName;
1520 String nodeValue;
1521
1522 switch (node->nodeType()) {
1523 case Node::PROCESSING_INSTRUCTION_NODE:
1524 nodeName = node->nodeName();
1525 localName = node->localName();
1526 FALLTHROUGH;
1527 case Node::TEXT_NODE:
1528 case Node::COMMENT_NODE:
1529 case Node::CDATA_SECTION_NODE:
1530 nodeValue = node->nodeValue();
1531 if (nodeValue.length() > maxTextSize) {
1532 nodeValue = nodeValue.left(maxTextSize);
1533 nodeValue.append(ellipsisUChar);
1534 }
1535 break;
1536 case Node::ATTRIBUTE_NODE:
1537 localName = node->localName();
1538 break;
1539 case Node::DOCUMENT_FRAGMENT_NODE:
1540 case Node::DOCUMENT_NODE:
1541 case Node::ELEMENT_NODE:
1542 default:
1543 nodeName = node->nodeName();
1544 localName = node->localName();
1545 break;
1546 }
1547
1548 auto value = Inspector::Protocol::DOM::Node::create()
1549 .setNodeId(id)
1550 .setNodeType(static_cast<int>(node->nodeType()))
1551 .setNodeName(nodeName)
1552 .setLocalName(localName)
1553 .setNodeValue(nodeValue)
1554 .release();
1555
1556 if (node->isContainerNode()) {
1557 int nodeCount = innerChildNodeCount(node);
1558 value->setChildNodeCount(nodeCount);
1559 Ref<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> children = buildArrayForContainerChildren(node, depth, nodesMap);
1560 if (children->length() > 0)
1561 value->setChildren(WTFMove(children));
1562 }
1563
1564 auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
1565 if (pageAgent) {
1566 if (auto* frameView = node->document().view())
1567 value->setFrameId(pageAgent->frameId(&frameView->frame()));
1568 }
1569
1570 if (is<Element>(*node)) {
1571 Element& element = downcast<Element>(*node);
1572 value->setAttributes(buildArrayForElementAttributes(&element));
1573 if (is<HTMLFrameOwnerElement>(element)) {
1574 if (auto* document = downcast<HTMLFrameOwnerElement>(element).contentDocument())
1575 value->setContentDocument(buildObjectForNode(document, 0, nodesMap));
1576 }
1577
1578 if (ShadowRoot* root = element.shadowRoot()) {
1579 auto shadowRoots = JSON::ArrayOf<Inspector::Protocol::DOM::Node>::create();
1580 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
1581 value->setShadowRoots(WTFMove(shadowRoots));
1582 }
1583
1584 if (is<HTMLTemplateElement>(element))
1585 value->setTemplateContent(buildObjectForNode(&downcast<HTMLTemplateElement>(element).content(), 0, nodesMap));
1586
1587 if (is<HTMLStyleElement>(element) || (is<HTMLScriptElement>(element) && !element.hasAttributeWithoutSynchronization(HTMLNames::srcAttr)))
1588 value->setContentSecurityPolicyHash(computeContentSecurityPolicySHA256Hash(element));
1589
1590 auto state = customElementState(element);
1591 if (state != Inspector::Protocol::DOM::CustomElementState::Builtin)
1592 value->setCustomElementState(state);
1593
1594 if (element.pseudoId() != PseudoId::None) {
1595 Inspector::Protocol::DOM::PseudoType pseudoType;
1596 if (pseudoElementType(element.pseudoId(), &pseudoType))
1597 value->setPseudoType(pseudoType);
1598 } else {
1599 if (auto pseudoElements = buildArrayForPseudoElements(element, nodesMap))
1600 value->setPseudoElements(WTFMove(pseudoElements));
1601 }
1602 } else if (is<Document>(*node)) {
1603 Document& document = downcast<Document>(*node);
1604 if (pageAgent)
1605 value->setFrameId(pageAgent->frameId(document.frame()));
1606 value->setDocumentURL(documentURLString(&document));
1607 value->setBaseURL(documentBaseURLString(&document));
1608 value->setXmlVersion(document.xmlVersion());
1609 } else if (is<DocumentType>(*node)) {
1610 DocumentType& docType = downcast<DocumentType>(*node);
1611 value->setPublicId(docType.publicId());
1612 value->setSystemId(docType.systemId());
1613 } else if (is<Attr>(*node)) {
1614 Attr& attribute = downcast<Attr>(*node);
1615 value->setName(attribute.name());
1616 value->setValue(attribute.value());
1617 } else if (is<ShadowRoot>(*node)) {
1618 ShadowRoot& shadowRoot = downcast<ShadowRoot>(*node);
1619 value->setShadowRootType(shadowRootType(shadowRoot.mode()));
1620 }
1621
1622 return value;
1623}
1624
1625Ref<JSON::ArrayOf<String>> InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1626{
1627 auto attributesValue = JSON::ArrayOf<String>::create();
1628 // Go through all attributes and serialize them.
1629 if (!element->hasAttributes())
1630 return attributesValue;
1631 for (const Attribute& attribute : element->attributesIterator()) {
1632 // Add attribute pair
1633 attributesValue->addItem(attribute.name().toString());
1634 attributesValue->addItem(attribute.value());
1635 }
1636 return attributesValue;
1637}
1638
1639Ref<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1640{
1641 auto children = JSON::ArrayOf<Inspector::Protocol::DOM::Node>::create();
1642 if (depth == 0) {
1643 // Special-case the only text child - pretend that container's children have been requested.
1644 Node* firstChild = container->firstChild();
1645 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
1646 children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
1647 m_childrenRequested.add(bind(container, nodesMap));
1648 }
1649 return children;
1650 }
1651
1652 Node* child = innerFirstChild(container);
1653 depth--;
1654 m_childrenRequested.add(bind(container, nodesMap));
1655
1656 while (child) {
1657 children->addItem(buildObjectForNode(child, depth, nodesMap));
1658 child = innerNextSibling(child);
1659 }
1660 return children;
1661}
1662
1663RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForPseudoElements(const Element& element, NodeToIdMap* nodesMap)
1664{
1665 PseudoElement* beforeElement = element.beforePseudoElement();
1666 PseudoElement* afterElement = element.afterPseudoElement();
1667 if (!beforeElement && !afterElement)
1668 return nullptr;
1669
1670 auto pseudoElements = JSON::ArrayOf<Inspector::Protocol::DOM::Node>::create();
1671 if (beforeElement)
1672 pseudoElements->addItem(buildObjectForNode(beforeElement, 0, nodesMap));
1673 if (afterElement)
1674 pseudoElements->addItem(buildObjectForNode(afterElement, 0, nodesMap));
1675 return pseudoElements;
1676}
1677
1678Ref<Inspector::Protocol::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, int identifier, EventTarget& eventTarget, const AtomicString& eventType, bool disabled, bool hasBreakpoint)
1679{
1680 Ref<EventListener> eventListener = registeredEventListener.callback();
1681
1682 String handlerName;
1683 int lineNumber = 0;
1684 int columnNumber = 0;
1685 String scriptID;
1686 if (is<JSEventListener>(eventListener.get())) {
1687 auto& scriptListener = downcast<JSEventListener>(eventListener.get());
1688
1689 Document* document = nullptr;
1690 if (auto* scriptExecutionContext = eventTarget.scriptExecutionContext()) {
1691 if (is<Document>(scriptExecutionContext))
1692 document = downcast<Document>(scriptExecutionContext);
1693 } else if (is<Node>(eventTarget))
1694 document = &downcast<Node>(eventTarget).document();
1695
1696 JSC::JSObject* handlerObject = nullptr;
1697 JSC::ExecState* exec = nullptr;
1698
1699 JSC::JSLockHolder lock(scriptListener.isolatedWorld().vm());
1700
1701 if (document) {
1702 handlerObject = scriptListener.jsFunction(*document);
1703 exec = execStateFromNode(scriptListener.isolatedWorld(), document);
1704 }
1705
1706 if (handlerObject && exec) {
1707 JSC::JSFunction* handlerFunction = JSC::jsDynamicCast<JSC::JSFunction*>(exec->vm(), handlerObject);
1708
1709 if (!handlerFunction) {
1710 auto scope = DECLARE_CATCH_SCOPE(exec->vm());
1711
1712 // If the handler is not actually a function, see if it implements the EventListener interface and use that.
1713 auto handleEventValue = handlerObject->get(exec, JSC::Identifier::fromString(exec, "handleEvent"));
1714
1715 if (UNLIKELY(scope.exception()))
1716 scope.clearException();
1717
1718 if (handleEventValue)
1719 handlerFunction = JSC::jsDynamicCast<JSC::JSFunction*>(exec->vm(), handleEventValue);
1720 }
1721
1722 if (handlerFunction && !handlerFunction->isHostOrBuiltinFunction()) {
1723 // If the listener implements the EventListener interface, use the class name instead of
1724 // "handleEvent", unless it is a plain object.
1725 if (handlerFunction != handlerObject)
1726 handlerName = JSC::JSObject::calculatedClassName(handlerObject);
1727 if (handlerName.isEmpty() || handlerName == "Object"_s)
1728 handlerName = handlerFunction->calculatedDisplayName(exec->vm());
1729
1730 if (auto executable = handlerFunction->jsExecutable()) {
1731 lineNumber = executable->firstLine() - 1;
1732 columnNumber = executable->startColumn() - 1;
1733 scriptID = executable->sourceID() == JSC::SourceProvider::nullID ? emptyString() : String::number(executable->sourceID());
1734 }
1735 }
1736 }
1737 }
1738
1739 auto value = Inspector::Protocol::DOM::EventListener::create()
1740 .setEventListenerId(identifier)
1741 .setType(eventType)
1742 .setUseCapture(registeredEventListener.useCapture())
1743 .setIsAttribute(eventListener->isAttribute())
1744 .release();
1745 if (is<Node>(eventTarget))
1746 value->setNodeId(pushNodePathToFrontend(&downcast<Node>(eventTarget)));
1747 else if (is<DOMWindow>(eventTarget))
1748 value->setOnWindow(true);
1749 if (!scriptID.isNull()) {
1750 auto location = Inspector::Protocol::Debugger::Location::create()
1751 .setScriptId(scriptID)
1752 .setLineNumber(lineNumber)
1753 .release();
1754 location->setColumnNumber(columnNumber);
1755 value->setLocation(WTFMove(location));
1756 }
1757 if (!handlerName.isEmpty())
1758 value->setHandlerName(handlerName);
1759 if (registeredEventListener.isPassive())
1760 value->setPassive(true);
1761 if (registeredEventListener.isOnce())
1762 value->setOnce(true);
1763 if (disabled)
1764 value->setDisabled(disabled);
1765 if (hasBreakpoint)
1766 value->setHasBreakpoint(hasBreakpoint);
1767 return value;
1768}
1769
1770void InspectorDOMAgent::processAccessibilityChildren(AccessibilityObject& axObject, JSON::ArrayOf<int>& childNodeIds)
1771{
1772 const auto& children = axObject.children();
1773 if (!children.size())
1774 return;
1775
1776 for (const auto& childObject : children) {
1777 if (Node* childNode = childObject->node())
1778 childNodeIds.addItem(pushNodePathToFrontend(childNode));
1779 else
1780 processAccessibilityChildren(*childObject, childNodeIds);
1781 }
1782}
1783
1784RefPtr<Inspector::Protocol::DOM::AccessibilityProperties> InspectorDOMAgent::buildObjectForAccessibilityProperties(Node* node)
1785{
1786 ASSERT(node);
1787 if (!node)
1788 return nullptr;
1789
1790 if (!WebCore::AXObjectCache::accessibilityEnabled())
1791 WebCore::AXObjectCache::enableAccessibility();
1792
1793 Node* activeDescendantNode = nullptr;
1794 bool busy = false;
1795 auto checked = Inspector::Protocol::DOM::AccessibilityProperties::Checked::False;
1796 RefPtr<JSON::ArrayOf<int>> childNodeIds;
1797 RefPtr<JSON::ArrayOf<int>> controlledNodeIds;
1798 auto currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::False;
1799 bool exists = false;
1800 bool expanded = false;
1801 bool disabled = false;
1802 RefPtr<JSON::ArrayOf<int>> flowedNodeIds;
1803 bool focused = false;
1804 bool ignored = true;
1805 bool ignoredByDefault = false;
1806 auto invalid = Inspector::Protocol::DOM::AccessibilityProperties::Invalid::False;
1807 bool hidden = false;
1808 String label;
1809 bool liveRegionAtomic = false;
1810 RefPtr<JSON::ArrayOf<String>> liveRegionRelevant;
1811 auto liveRegionStatus = Inspector::Protocol::DOM::AccessibilityProperties::LiveRegionStatus::Off;
1812 Node* mouseEventNode = nullptr;
1813 RefPtr<JSON::ArrayOf<int>> ownedNodeIds;
1814 Node* parentNode = nullptr;
1815 bool pressed = false;
1816 bool readonly = false;
1817 bool required = false;
1818 String role;
1819 bool selected = false;
1820 RefPtr<JSON::ArrayOf<int>> selectedChildNodeIds;
1821 bool supportsChecked = false;
1822 bool supportsExpanded = false;
1823 bool supportsLiveRegion = false;
1824 bool supportsPressed = false;
1825 bool supportsRequired = false;
1826 bool supportsFocused = false;
1827 bool isPopupButton = false;
1828 int headingLevel = 0;
1829 unsigned hierarchicalLevel = 0;
1830 unsigned level = 0;
1831
1832 if (AXObjectCache* axObjectCache = node->document().axObjectCache()) {
1833 if (AccessibilityObject* axObject = axObjectCache->getOrCreate(node)) {
1834
1835 if (AccessibilityObject* activeDescendant = axObject->activeDescendant())
1836 activeDescendantNode = activeDescendant->node();
1837
1838 // An AX object is "busy" if it or any ancestor has aria-busy="true" set.
1839 AccessibilityObject* current = axObject;
1840 while (!busy && current) {
1841 busy = current->isBusy();
1842 current = current->parentObject();
1843 }
1844
1845 supportsChecked = axObject->supportsChecked();
1846 if (supportsChecked) {
1847 AccessibilityButtonState checkValue = axObject->checkboxOrRadioValue(); // Element using aria-checked.
1848 if (checkValue == AccessibilityButtonState::On)
1849 checked = Inspector::Protocol::DOM::AccessibilityProperties::Checked::True;
1850 else if (checkValue == AccessibilityButtonState::Mixed)
1851 checked = Inspector::Protocol::DOM::AccessibilityProperties::Checked::Mixed;
1852 else if (axObject->isChecked()) // Native checkbox.
1853 checked = Inspector::Protocol::DOM::AccessibilityProperties::Checked::True;
1854 }
1855
1856 if (!axObject->children().isEmpty()) {
1857 childNodeIds = JSON::ArrayOf<int>::create();
1858 processAccessibilityChildren(*axObject, *childNodeIds);
1859 }
1860
1861 Vector<Element*> controlledElements;
1862 axObject->elementsFromAttribute(controlledElements, aria_controlsAttr);
1863 if (controlledElements.size()) {
1864 controlledNodeIds = JSON::ArrayOf<int>::create();
1865 for (Element* controlledElement : controlledElements)
1866 controlledNodeIds->addItem(pushNodePathToFrontend(controlledElement));
1867 }
1868
1869 switch (axObject->currentState()) {
1870 case AccessibilityCurrentState::False:
1871 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::False;
1872 break;
1873 case AccessibilityCurrentState::Page:
1874 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::Page;
1875 break;
1876 case AccessibilityCurrentState::Step:
1877 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::Step;
1878 break;
1879 case AccessibilityCurrentState::Location:
1880 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::Location;
1881 break;
1882 case AccessibilityCurrentState::Date:
1883 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::Date;
1884 break;
1885 case AccessibilityCurrentState::Time:
1886 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::Time;
1887 break;
1888 case AccessibilityCurrentState::True:
1889 currentState = Inspector::Protocol::DOM::AccessibilityProperties::Current::True;
1890 break;
1891 }
1892
1893 disabled = !axObject->isEnabled();
1894 exists = true;
1895
1896 supportsExpanded = axObject->supportsExpanded();
1897 if (supportsExpanded)
1898 expanded = axObject->isExpanded();
1899
1900 Vector<Element*> flowedElements;
1901 axObject->elementsFromAttribute(flowedElements, aria_flowtoAttr);
1902 if (flowedElements.size()) {
1903 flowedNodeIds = JSON::ArrayOf<int>::create();
1904 for (Element* flowedElement : flowedElements)
1905 flowedNodeIds->addItem(pushNodePathToFrontend(flowedElement));
1906 }
1907
1908 if (is<Element>(*node)) {
1909 supportsFocused = axObject->canSetFocusAttribute();
1910 if (supportsFocused)
1911 focused = axObject->isFocused();
1912 }
1913
1914 ignored = axObject->accessibilityIsIgnored();
1915 ignoredByDefault = axObject->accessibilityIsIgnoredByDefault();
1916
1917 String invalidValue = axObject->invalidStatus();
1918 if (invalidValue == "false")
1919 invalid = Inspector::Protocol::DOM::AccessibilityProperties::Invalid::False;
1920 else if (invalidValue == "grammar")
1921 invalid = Inspector::Protocol::DOM::AccessibilityProperties::Invalid::Grammar;
1922 else if (invalidValue == "spelling")
1923 invalid = Inspector::Protocol::DOM::AccessibilityProperties::Invalid::Spelling;
1924 else // Future versions of ARIA may allow additional truthy values. Ex. format, order, or size.
1925 invalid = Inspector::Protocol::DOM::AccessibilityProperties::Invalid::True;
1926
1927 if (axObject->isAXHidden() || axObject->isDOMHidden())
1928 hidden = true;
1929
1930 label = axObject->computedLabel();
1931
1932 if (axObject->supportsLiveRegion()) {
1933 supportsLiveRegion = true;
1934 liveRegionAtomic = axObject->liveRegionAtomic();
1935
1936 String ariaRelevantAttrValue = axObject->liveRegionRelevant();
1937 if (!ariaRelevantAttrValue.isEmpty()) {
1938 // FIXME: Pass enum values rather than strings once unblocked. http://webkit.org/b/133711
1939 String ariaRelevantAdditions = Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::DOM::LiveRegionRelevant::Additions);
1940 String ariaRelevantRemovals = Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::DOM::LiveRegionRelevant::Removals);
1941 String ariaRelevantText = Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::DOM::LiveRegionRelevant::Text);
1942 liveRegionRelevant = JSON::ArrayOf<String>::create();
1943 const SpaceSplitString& values = SpaceSplitString(ariaRelevantAttrValue, true);
1944 // @aria-relevant="all" is exposed as ["additions","removals","text"], in order.
1945 // This order is controlled in WebCore and expected in WebInspectorUI.
1946 if (values.contains("all")) {
1947 liveRegionRelevant->addItem(ariaRelevantAdditions);
1948 liveRegionRelevant->addItem(ariaRelevantRemovals);
1949 liveRegionRelevant->addItem(ariaRelevantText);
1950 } else {
1951 if (values.contains(ariaRelevantAdditions))
1952 liveRegionRelevant->addItem(ariaRelevantAdditions);
1953 if (values.contains(ariaRelevantRemovals))
1954 liveRegionRelevant->addItem(ariaRelevantRemovals);
1955 if (values.contains(ariaRelevantText))
1956 liveRegionRelevant->addItem(ariaRelevantText);
1957 }
1958 }
1959
1960 String ariaLive = axObject->liveRegionStatus();
1961 if (ariaLive == "assertive")
1962 liveRegionStatus = Inspector::Protocol::DOM::AccessibilityProperties::LiveRegionStatus::Assertive;
1963 else if (ariaLive == "polite")
1964 liveRegionStatus = Inspector::Protocol::DOM::AccessibilityProperties::LiveRegionStatus::Polite;
1965 }
1966
1967 if (is<AccessibilityNodeObject>(*axObject))
1968 mouseEventNode = downcast<AccessibilityNodeObject>(*axObject).mouseButtonListener(MouseButtonListenerResultFilter::IncludeBodyElement);
1969
1970 if (axObject->supportsARIAOwns()) {
1971 Vector<Element*> ownedElements;
1972 axObject->elementsFromAttribute(ownedElements, aria_ownsAttr);
1973 if (ownedElements.size()) {
1974 ownedNodeIds = JSON::ArrayOf<int>::create();
1975 for (Element* ownedElement : ownedElements)
1976 ownedNodeIds->addItem(pushNodePathToFrontend(ownedElement));
1977 }
1978 }
1979
1980 if (AccessibilityObject* parentObject = axObject->parentObjectUnignored())
1981 parentNode = parentObject->node();
1982
1983 supportsPressed = axObject->pressedIsPresent();
1984 if (supportsPressed)
1985 pressed = axObject->isPressed();
1986
1987 if (axObject->isTextControl())
1988 readonly = !axObject->canSetValueAttribute();
1989
1990 supportsRequired = axObject->supportsRequiredAttribute();
1991 if (supportsRequired)
1992 required = axObject->isRequired();
1993
1994 role = axObject->computedRoleString();
1995 selected = axObject->isSelected();
1996
1997 AccessibilityObject::AccessibilityChildrenVector selectedChildren;
1998 axObject->selectedChildren(selectedChildren);
1999 if (selectedChildren.size()) {
2000 selectedChildNodeIds = JSON::ArrayOf<int>::create();
2001 for (auto& selectedChildObject : selectedChildren) {
2002 if (Node* selectedChildNode = selectedChildObject->node())
2003 selectedChildNodeIds->addItem(pushNodePathToFrontend(selectedChildNode));
2004 }
2005 }
2006
2007 headingLevel = axObject->headingLevel();
2008 hierarchicalLevel = axObject->hierarchicalLevel();
2009
2010 level = hierarchicalLevel ? hierarchicalLevel : headingLevel;
2011 isPopupButton = axObject->isPopUpButton() || axObject->hasPopup();
2012 }
2013 }
2014
2015 Ref<Inspector::Protocol::DOM::AccessibilityProperties> value = Inspector::Protocol::DOM::AccessibilityProperties::create()
2016 .setExists(exists)
2017 .setLabel(label)
2018 .setRole(role)
2019 .setNodeId(pushNodePathToFrontend(node))
2020 .release();
2021
2022 if (exists) {
2023 if (activeDescendantNode)
2024 value->setActiveDescendantNodeId(pushNodePathToFrontend(activeDescendantNode));
2025 if (busy)
2026 value->setBusy(busy);
2027 if (supportsChecked)
2028 value->setChecked(checked);
2029 if (childNodeIds)
2030 value->setChildNodeIds(childNodeIds);
2031 if (controlledNodeIds)
2032 value->setControlledNodeIds(controlledNodeIds);
2033 if (currentState != Inspector::Protocol::DOM::AccessibilityProperties::Current::False)
2034 value->setCurrent(currentState);
2035 if (disabled)
2036 value->setDisabled(disabled);
2037 if (supportsExpanded)
2038 value->setExpanded(expanded);
2039 if (flowedNodeIds)
2040 value->setFlowedNodeIds(flowedNodeIds);
2041 if (supportsFocused)
2042 value->setFocused(focused);
2043 if (ignored)
2044 value->setIgnored(ignored);
2045 if (ignoredByDefault)
2046 value->setIgnoredByDefault(ignoredByDefault);
2047 if (invalid != Inspector::Protocol::DOM::AccessibilityProperties::Invalid::False)
2048 value->setInvalid(invalid);
2049 if (hidden)
2050 value->setHidden(hidden);
2051 if (supportsLiveRegion) {
2052 value->setLiveRegionAtomic(liveRegionAtomic);
2053 if (liveRegionRelevant->length())
2054 value->setLiveRegionRelevant(liveRegionRelevant);
2055 value->setLiveRegionStatus(liveRegionStatus);
2056 }
2057 if (mouseEventNode)
2058 value->setMouseEventNodeId(pushNodePathToFrontend(mouseEventNode));
2059 if (ownedNodeIds)
2060 value->setOwnedNodeIds(ownedNodeIds);
2061 if (parentNode)
2062 value->setParentNodeId(pushNodePathToFrontend(parentNode));
2063 if (supportsPressed)
2064 value->setPressed(pressed);
2065 if (readonly)
2066 value->setReadonly(readonly);
2067 if (supportsRequired)
2068 value->setRequired(required);
2069 if (selected)
2070 value->setSelected(selected);
2071 if (selectedChildNodeIds)
2072 value->setSelectedChildNodeIds(selectedChildNodeIds);
2073
2074 // H1 -- H6 always have a headingLevel property that can be complimented by a hierarchicalLevel
2075 // property when aria-level is set on the element, in which case we want to remain calling
2076 // this value the "Heading Level" in the inspector.
2077 // Also, we do not want it to say Hierarchy Level: 0
2078 if (headingLevel)
2079 value->setHeadingLevel(level);
2080 else if (level)
2081 value->setHierarchyLevel(level);
2082 if (isPopupButton)
2083 value->setIsPopUpButton(isPopupButton);
2084 }
2085
2086 return value;
2087}
2088
2089static bool containsOnlyHTMLWhitespace(Node* node)
2090{
2091 // FIXME: Respect ignoreWhitespace setting from inspector front end?
2092 return is<Text>(node) && downcast<Text>(*node).data().isAllSpecialCharacters<isHTMLSpace>();
2093}
2094
2095Node* InspectorDOMAgent::innerFirstChild(Node* node)
2096{
2097 node = node->firstChild();
2098 while (containsOnlyHTMLWhitespace(node))
2099 node = node->nextSibling();
2100 return node;
2101}
2102
2103Node* InspectorDOMAgent::innerNextSibling(Node* node)
2104{
2105 do {
2106 node = node->nextSibling();
2107 } while (containsOnlyHTMLWhitespace(node));
2108 return node;
2109}
2110
2111Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
2112{
2113 do {
2114 node = node->previousSibling();
2115 } while (containsOnlyHTMLWhitespace(node));
2116 return node;
2117}
2118
2119unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
2120{
2121 unsigned count = 0;
2122 for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
2123 ++count;
2124 return count;
2125}
2126
2127Node* InspectorDOMAgent::innerParentNode(Node* node)
2128{
2129 ASSERT(node);
2130 if (is<Document>(*node))
2131 return downcast<Document>(*node).ownerElement();
2132 if (is<ShadowRoot>(*node))
2133 return downcast<ShadowRoot>(*node).host();
2134 return node->parentNode();
2135}
2136
2137void InspectorDOMAgent::didCommitLoad(Document* document)
2138{
2139 if (m_nodeToFocus && &m_nodeToFocus->document() == document)
2140 m_nodeToFocus = nullptr;
2141
2142 if (m_mousedOverNode && &m_mousedOverNode->document() == document)
2143 m_mousedOverNode = nullptr;
2144
2145 if (m_inspectedNode && &m_inspectedNode->document() == document)
2146 m_inspectedNode = nullptr;
2147
2148 RefPtr<Element> frameOwner = document->ownerElement();
2149 if (!frameOwner)
2150 return;
2151
2152 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
2153 if (!frameOwnerId)
2154 return;
2155
2156 // Re-add frame owner element together with its new children.
2157 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner.get()));
2158 m_frontendDispatcher->childNodeRemoved(parentId, frameOwnerId);
2159 unbind(frameOwner.get(), &m_documentNodeToIdMap);
2160
2161 Ref<Inspector::Protocol::DOM::Node> value = buildObjectForNode(frameOwner.get(), 0, &m_documentNodeToIdMap);
2162 Node* previousSibling = innerPreviousSibling(frameOwner.get());
2163 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
2164 m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value));
2165}
2166
2167int InspectorDOMAgent::identifierForNode(Node& node)
2168{
2169 return pushNodePathToFrontend(&node);
2170}
2171
2172void InspectorDOMAgent::addEventListenersToNode(Node& node)
2173{
2174#if ENABLE(VIDEO)
2175 auto callback = EventFiredCallback::create(*this);
2176
2177 auto createEventListener = [&] (const AtomicString& eventName) {
2178 node.addEventListener(eventName, callback.copyRef(), false);
2179 };
2180
2181#if ENABLE(FULLSCREEN_API)
2182 if (is<Document>(node) || is<HTMLMediaElement>(node))
2183 createEventListener(eventNames().webkitfullscreenchangeEvent);
2184#endif // ENABLE(FULLSCREEN_API)
2185
2186 if (is<HTMLMediaElement>(node)) {
2187 createEventListener(eventNames().abortEvent);
2188 createEventListener(eventNames().canplayEvent);
2189 createEventListener(eventNames().canplaythroughEvent);
2190 createEventListener(eventNames().emptiedEvent);
2191 createEventListener(eventNames().endedEvent);
2192 createEventListener(eventNames().loadeddataEvent);
2193 createEventListener(eventNames().loadedmetadataEvent);
2194 createEventListener(eventNames().loadstartEvent);
2195 createEventListener(eventNames().pauseEvent);
2196 createEventListener(eventNames().playEvent);
2197 createEventListener(eventNames().playingEvent);
2198 createEventListener(eventNames().seekedEvent);
2199 createEventListener(eventNames().seekingEvent);
2200 createEventListener(eventNames().stalledEvent);
2201 createEventListener(eventNames().suspendEvent);
2202 createEventListener(eventNames().waitingEvent);
2203
2204 if (!m_mediaMetricsTimer.isActive())
2205 m_mediaMetricsTimer.start(0_s, 1_s / 15.);
2206 }
2207#else
2208 UNUSED_PARAM(node);
2209#endif // ENABLE(VIDEO)
2210}
2211
2212void InspectorDOMAgent::didInsertDOMNode(Node& node)
2213{
2214 if (containsOnlyHTMLWhitespace(&node))
2215 return;
2216
2217 // We could be attaching existing subtree. Forget the bindings.
2218 unbind(&node, &m_documentNodeToIdMap);
2219
2220 ContainerNode* parent = node.parentNode();
2221 if (!parent)
2222 return;
2223
2224 int parentId = m_documentNodeToIdMap.get(parent);
2225 // Return if parent is not mapped yet.
2226 if (!parentId)
2227 return;
2228
2229 if (!m_childrenRequested.contains(parentId)) {
2230 // No children are mapped yet -> only notify on changes of hasChildren.
2231 m_frontendDispatcher->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
2232 } else {
2233 // Children have been requested -> return value of a new child.
2234 Node* prevSibling = innerPreviousSibling(&node);
2235 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
2236 Ref<Inspector::Protocol::DOM::Node> value = buildObjectForNode(&node, 0, &m_documentNodeToIdMap);
2237 m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value));
2238 }
2239}
2240
2241void InspectorDOMAgent::didRemoveDOMNode(Node& node)
2242{
2243 if (containsOnlyHTMLWhitespace(&node))
2244 return;
2245
2246 ContainerNode* parent = node.parentNode();
2247
2248 // If parent is not mapped yet -> ignore the event.
2249 if (!m_documentNodeToIdMap.contains(parent))
2250 return;
2251
2252 int parentId = m_documentNodeToIdMap.get(parent);
2253
2254 if (!m_childrenRequested.contains(parentId)) {
2255 // No children are mapped yet -> only notify on changes of hasChildren.
2256 if (innerChildNodeCount(parent) == 1)
2257 m_frontendDispatcher->childNodeCountUpdated(parentId, 0);
2258 } else
2259 m_frontendDispatcher->childNodeRemoved(parentId, m_documentNodeToIdMap.get(&node));
2260 unbind(&node, &m_documentNodeToIdMap);
2261}
2262
2263void InspectorDOMAgent::willModifyDOMAttr(Element&, const AtomicString& oldValue, const AtomicString& newValue)
2264{
2265 m_suppressAttributeModifiedEvent = (oldValue == newValue);
2266}
2267
2268void InspectorDOMAgent::didModifyDOMAttr(Element& element, const AtomicString& name, const AtomicString& value)
2269{
2270 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent;
2271 m_suppressAttributeModifiedEvent = false;
2272 if (shouldSuppressEvent)
2273 return;
2274
2275 int id = boundNodeId(&element);
2276 if (!id)
2277 return;
2278
2279 if (auto* cssAgent = m_instrumentingAgents.inspectorCSSAgent())
2280 cssAgent->didModifyDOMAttr(element);
2281
2282 m_frontendDispatcher->attributeModified(id, name, value);
2283}
2284
2285void InspectorDOMAgent::didRemoveDOMAttr(Element& element, const AtomicString& name)
2286{
2287 int id = boundNodeId(&element);
2288 if (!id)
2289 return;
2290
2291 if (auto* cssAgent = m_instrumentingAgents.inspectorCSSAgent())
2292 cssAgent->didModifyDOMAttr(element);
2293
2294 m_frontendDispatcher->attributeRemoved(id, name);
2295}
2296
2297void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elements)
2298{
2299 auto nodeIds = JSON::ArrayOf<int>::create();
2300 for (auto& element : elements) {
2301 int id = boundNodeId(element);
2302 if (!id)
2303 continue;
2304
2305 if (auto* cssAgent = m_instrumentingAgents.inspectorCSSAgent())
2306 cssAgent->didModifyDOMAttr(*element);
2307
2308 nodeIds->addItem(id);
2309 }
2310 m_frontendDispatcher->inlineStyleInvalidated(WTFMove(nodeIds));
2311}
2312
2313void InspectorDOMAgent::characterDataModified(CharacterData& characterData)
2314{
2315 int id = m_documentNodeToIdMap.get(&characterData);
2316 if (!id) {
2317 // Push text node if it is being created.
2318 didInsertDOMNode(characterData);
2319 return;
2320 }
2321 m_frontendDispatcher->characterDataModified(id, characterData.data());
2322}
2323
2324void InspectorDOMAgent::didInvalidateStyleAttr(Element& element)
2325{
2326 int id = m_documentNodeToIdMap.get(&element);
2327 if (!id)
2328 return;
2329
2330 if (!m_revalidateStyleAttrTask)
2331 m_revalidateStyleAttrTask = std::make_unique<RevalidateStyleAttributeTask>(this);
2332 m_revalidateStyleAttrTask->scheduleFor(&element);
2333}
2334
2335void InspectorDOMAgent::didPushShadowRoot(Element& host, ShadowRoot& root)
2336{
2337 int hostId = m_documentNodeToIdMap.get(&host);
2338 if (hostId)
2339 m_frontendDispatcher->shadowRootPushed(hostId, buildObjectForNode(&root, 0, &m_documentNodeToIdMap));
2340}
2341
2342void InspectorDOMAgent::willPopShadowRoot(Element& host, ShadowRoot& root)
2343{
2344 int hostId = m_documentNodeToIdMap.get(&host);
2345 int rootId = m_documentNodeToIdMap.get(&root);
2346 if (hostId && rootId)
2347 m_frontendDispatcher->shadowRootPopped(hostId, rootId);
2348}
2349
2350void InspectorDOMAgent::didChangeCustomElementState(Element& element)
2351{
2352 int elementId = m_documentNodeToIdMap.get(&element);
2353 if (!elementId)
2354 return;
2355
2356 m_frontendDispatcher->customElementStateChanged(elementId, customElementState(element));
2357}
2358
2359void InspectorDOMAgent::frameDocumentUpdated(Frame& frame)
2360{
2361 Document* document = frame.document();
2362 if (!document)
2363 return;
2364
2365 if (!frame.isMainFrame())
2366 return;
2367
2368 // Only update the main frame document, nested frame document updates are not required
2369 // (will be handled by didCommitLoad()).
2370 setDocument(document);
2371}
2372
2373void InspectorDOMAgent::pseudoElementCreated(PseudoElement& pseudoElement)
2374{
2375 Element* parent = pseudoElement.hostElement();
2376 if (!parent)
2377 return;
2378
2379 int parentId = m_documentNodeToIdMap.get(parent);
2380 if (!parentId)
2381 return;
2382
2383 pushChildNodesToFrontend(parentId, 1);
2384 m_frontendDispatcher->pseudoElementAdded(parentId, buildObjectForNode(&pseudoElement, 0, &m_documentNodeToIdMap));
2385}
2386
2387void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement& pseudoElement)
2388{
2389 int pseudoElementId = m_documentNodeToIdMap.get(&pseudoElement);
2390 if (!pseudoElementId)
2391 return;
2392
2393 // If a PseudoElement is bound, its parent element must have been bound.
2394 Element* parent = pseudoElement.hostElement();
2395 ASSERT(parent);
2396 int parentId = m_documentNodeToIdMap.get(parent);
2397 ASSERT(parentId);
2398
2399 unbind(&pseudoElement, &m_documentNodeToIdMap);
2400 m_frontendDispatcher->pseudoElementRemoved(parentId, pseudoElementId);
2401}
2402
2403void InspectorDOMAgent::didAddEventListener(EventTarget& target)
2404{
2405 if (!is<Node>(target))
2406 return;
2407
2408 auto& node = downcast<Node>(target);
2409 if (!node.contains(m_inspectedNode.get()))
2410 return;
2411
2412 int nodeId = boundNodeId(&node);
2413 if (!nodeId)
2414 return;
2415
2416 if (m_suppressEventListenerChangedEvent)
2417 return;
2418
2419 m_suppressEventListenerChangedEvent = true;
2420
2421 m_frontendDispatcher->didAddEventListener(nodeId);
2422}
2423
2424void InspectorDOMAgent::willRemoveEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
2425{
2426 if (!is<Node>(target))
2427 return;
2428
2429 auto& node = downcast<Node>(target);
2430 if (!node.contains(m_inspectedNode.get()))
2431 return;
2432
2433 int nodeId = boundNodeId(&node);
2434 if (!nodeId)
2435 return;
2436
2437 bool listenerExists = false;
2438 for (auto& item : node.eventListeners(eventType)) {
2439 if (item->callback() == listener && item->useCapture() == capture) {
2440 listenerExists = true;
2441 break;
2442 }
2443 }
2444
2445 if (!listenerExists)
2446 return;
2447
2448 m_eventListenerEntries.removeIf([&] (auto& entry) {
2449 return entry.value.matches(target, eventType, listener, capture);
2450 });
2451
2452 if (m_suppressEventListenerChangedEvent)
2453 return;
2454
2455 m_suppressEventListenerChangedEvent = true;
2456
2457 m_frontendDispatcher->willRemoveEventListener(nodeId);
2458}
2459
2460bool InspectorDOMAgent::isEventListenerDisabled(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
2461{
2462 for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
2463 if (inspectorEventListener.matches(target, eventType, listener, capture))
2464 return inspectorEventListener.disabled;
2465 }
2466 return false;
2467}
2468
2469void InspectorDOMAgent::eventDidResetAfterDispatch(const Event& event)
2470{
2471 m_dispatchedEvents.remove(&event);
2472}
2473
2474bool InspectorDOMAgent::hasBreakpointForEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
2475{
2476 for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
2477 if (inspectorEventListener.matches(target, eventType, listener, capture))
2478 return inspectorEventListener.hasBreakpoint;
2479 }
2480 return false;
2481}
2482
2483int InspectorDOMAgent::idForEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
2484{
2485 for (auto& inspectorEventListener : m_eventListenerEntries.values()) {
2486 if (inspectorEventListener.matches(target, eventType, listener, capture))
2487 return inspectorEventListener.identifier;
2488 }
2489 return 0;
2490}
2491
2492#if ENABLE(VIDEO)
2493void InspectorDOMAgent::mediaMetricsTimerFired()
2494{
2495 // FIXME: remove metrics information for any media element when it's destroyed
2496
2497 if (HTMLMediaElement::allMediaElements().isEmpty()) {
2498 if (m_mediaMetricsTimer.isActive())
2499 m_mediaMetricsTimer.stop();
2500 m_mediaMetrics.clear();
2501 return;
2502 }
2503
2504 for (auto* mediaElement : HTMLMediaElement::allMediaElements()) {
2505 if (!is<HTMLVideoElement>(mediaElement) || !mediaElement->isPlaying())
2506 continue;
2507
2508 auto videoPlaybackQuality = mediaElement->getVideoPlaybackQuality();
2509 unsigned displayCompositedVideoFrames = videoPlaybackQuality->displayCompositedVideoFrames();
2510
2511 auto iterator = m_mediaMetrics.find(mediaElement);
2512 if (iterator == m_mediaMetrics.end()) {
2513 m_mediaMetrics.set(mediaElement, MediaMetrics(displayCompositedVideoFrames));
2514 continue;
2515 }
2516
2517 bool isPowerEfficient = (displayCompositedVideoFrames - iterator->value.displayCompositedFrames) > 0;
2518 if (iterator->value.isPowerEfficient != isPowerEfficient) {
2519 iterator->value.isPowerEfficient = isPowerEfficient;
2520
2521 int nodeId = pushNodePathToFrontend(mediaElement);
2522 if (nodeId) {
2523 auto timestamp = m_environment.executionStopwatch()->elapsedTime().seconds();
2524 m_frontendDispatcher->powerEfficientPlaybackStateChanged(nodeId, timestamp, iterator->value.isPowerEfficient);
2525 }
2526 }
2527
2528 iterator->value.displayCompositedFrames = displayCompositedVideoFrames;
2529 }
2530
2531 m_mediaMetrics.removeIf([&] (auto& entry) {
2532 return !HTMLMediaElement::allMediaElements().contains(entry.key);
2533 });
2534}
2535#endif
2536
2537Node* InspectorDOMAgent::nodeForPath(const String& path)
2538{
2539 // The path is of form "1,HTML,2,BODY,1,DIV"
2540 if (!m_document)
2541 return nullptr;
2542
2543 Node* node = m_document.get();
2544 Vector<String> pathTokens = path.split(',');
2545 if (!pathTokens.size())
2546 return nullptr;
2547
2548 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
2549 bool success = true;
2550 unsigned childNumber = pathTokens[i].toUInt(&success);
2551 if (!success)
2552 return nullptr;
2553
2554 Node* child;
2555 if (is<HTMLFrameOwnerElement>(*node)) {
2556 ASSERT(!childNumber);
2557 auto& frameOwner = downcast<HTMLFrameOwnerElement>(*node);
2558 child = frameOwner.contentDocument();
2559 } else {
2560 if (childNumber >= innerChildNodeCount(node))
2561 return nullptr;
2562
2563 child = innerFirstChild(node);
2564 for (size_t j = 0; child && j < childNumber; ++j)
2565 child = innerNextSibling(child);
2566 }
2567
2568 const auto& childName = pathTokens[i + 1];
2569 if (!child || child->nodeName() != childName)
2570 return nullptr;
2571 node = child;
2572 }
2573 return node;
2574}
2575
2576Node* InspectorDOMAgent::nodeForObjectId(const String& objectId)
2577{
2578 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
2579 if (injectedScript.hasNoValue())
2580 return nullptr;
2581
2582 return scriptValueAsNode(injectedScript.findObjectById(objectId));
2583}
2584
2585void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString& errorString, const String& path, int* nodeId)
2586{
2587 if (Node* node = nodeForPath(path))
2588 *nodeId = pushNodePathToFrontend(node);
2589 else
2590 errorString = "No node with given path found"_s;
2591}
2592
2593RefPtr<Inspector::Protocol::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup)
2594{
2595 auto* frame = node->document().frame();
2596 if (!frame)
2597 return nullptr;
2598
2599 auto& state = *mainWorldExecState(frame);
2600 auto injectedScript = m_injectedScriptManager.injectedScriptFor(&state);
2601 if (injectedScript.hasNoValue())
2602 return nullptr;
2603
2604 return injectedScript.wrapObject(nodeAsScriptValue(state, node), objectGroup);
2605}
2606
2607Node* InspectorDOMAgent::scriptValueAsNode(JSC::JSValue value)
2608{
2609 if (!value || !value.isObject())
2610 return nullptr;
2611 return JSNode::toWrapped(*value.getObject()->vm(), value.getObject());
2612}
2613
2614JSC::JSValue InspectorDOMAgent::nodeAsScriptValue(JSC::ExecState& state, Node* node)
2615{
2616 JSC::JSLockHolder lock(&state);
2617 return toJS(&state, deprecatedGlobalObjectForPrototype(&state), BindingSecurity::checkSecurityForNode(state, node));
2618}
2619
2620} // namespace WebCore
2621