Diff
Modified: trunk/LayoutTests/ChangeLog (278784 => 278785)
--- trunk/LayoutTests/ChangeLog 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/LayoutTests/ChangeLog 2021-06-11 22:37:40 UTC (rev 278785)
@@ -1,3 +1,13 @@
+2021-06-11 Patrick Angle <[email protected]>
+
+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent
+ https://bugs.webkit.org/show_bug.cgi?id=226624
+
+ Reviewed by Devin Rousso.
+
+ * inspector/dom/willDestroyDOMNode-expected.txt: Added.
+ * inspector/dom/willDestroyDOMNode.html: Added.
+
2021-06-11 Wenson Hsieh <[email protected]>
[Live Text] Text selection inside image elements should not be cleared upon resize
Added: trunk/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt (0 => 278785)
--- trunk/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt 2021-06-11 22:37:40 UTC (rev 278785)
@@ -0,0 +1,10 @@
+Test for DOM.willDestroyDOMNode.
+
+
+== Running test suite: DOM.willDestroyDOMNode
+-- Running test case: DOM.willDestroyDOMNode.CreateDeleteAndGCDetachedNode
+Creating new detached DOM node.
+Requesting DOM node in order to receive future events.
+Releasing node and then triggering garbage collection while awaiting `DOM.willDestroyDOMNode` event.
+Received event `DOM.willDestoryDOMNode` after garbage collection.
+
Added: trunk/LayoutTests/inspector/dom/willDestroyDOMNode.html (0 => 278785)
--- trunk/LayoutTests/inspector/dom/willDestroyDOMNode.html (rev 0)
+++ trunk/LayoutTests/inspector/dom/willDestroyDOMNode.html 2021-06-11 22:37:40 UTC (rev 278785)
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+ let suite = ProtocolTest.createAsyncSuite("DOM.willDestroyDOMNode");
+
+ suite.addTestCase({
+ name: "DOM.willDestroyDOMNode.CreateDeleteAndGCDetachedNode",
+ description: "Create a new DOM node that is not attached the DOM tree, and then allow it to be garbage collected.",
+ async test() {
+ ProtocolTest.log("Creating new detached DOM node.");
+ let domNodeResponse = await InspectorProtocol.awaitCommand({ method: "Runtime.evaluate", params: { _expression_: `document.createElement("a")` } });
+ let objectId = domNodeResponse.result.objectId;
+ ProtocolTest.assert(!domNodeResponse.wasThrown);
+
+ ProtocolTest.log("Requesting DOM node in order to receive future events.");
+ // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
+ await InspectorProtocol.awaitCommand({ method: "DOM.getDocument"});
+ let node = await InspectorProtocol.awaitCommand({ method: "DOM.requestNode", params: { objectId } });
+
+ // ProtocolTest.log("Releasing reference to node held by remote object.")
+ // await InspectorProtocol.awaitCommand({ method: "Console.clearMessages" });
+
+ ProtocolTest.log("Releasing node and then triggering garbage collection while awaiting `DOM.willDestroyDOMNode` event.");
+ let deleteNodePromise = InspectorProtocol.awaitCommand({ method: "Runtime.releaseObject", params: { objectId } });
+ let gcPromise = ProtocolTest.evaluateInPage(`GCController.collect()`);
+
+ await Promise.all([
+ InspectorProtocol.awaitEvent({event:"DOM.willDestroyDOMNode"}),
+ deleteNodePromise.then((resolve) => gcPromise),
+ ]);
+
+ ProtocolTest.log("Received event `DOM.willDestoryDOMNode` after garbage collection.");
+ },
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest();">
+<p>Test for DOM.willDestroyDOMNode.</p>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/_javascript_Core/ChangeLog (278784 => 278785)
--- trunk/Source/_javascript_Core/ChangeLog 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-06-11 22:37:40 UTC (rev 278785)
@@ -1,3 +1,17 @@
+2021-06-11 Patrick Angle <[email protected]>
+
+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent
+ https://bugs.webkit.org/show_bug.cgi?id=226624
+
+ Reviewed by Devin Rousso.
+
+ Add new `DOM.willDestroyDOMNode` event to inform the frontend of DOM nodes that no longer exist, even if they
+ weren't in the DOM tree. This work serves as a prelude to <https://webkit.org/b/189687> (Web Inspector: preserve
+ DOM.NodeId if a node is removed and re-added) to eventually only forget about nodes upon destruction, instead of
+ removal from the DOM tree.
+
+ * inspector/protocol/DOM.json:
+
2021-06-11 Yijia Huang <[email protected]>
Air ARM64 sub32 opcode should indicate that it zero-extends its result
Modified: trunk/Source/_javascript_Core/inspector/protocol/DOM.json (278784 => 278785)
--- trunk/Source/_javascript_Core/inspector/protocol/DOM.json 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/_javascript_Core/inspector/protocol/DOM.json 2021-06-11 22:37:40 UTC (rev 278785)
@@ -679,6 +679,13 @@
]
},
{
+ "name": "willDestroyDOMNode",
+ "description": "Fired when a detached DOM node is about to be destroyed. Currently, this event will only be fired when a DOM node that is detached is about to be destructed.",
+ "parameters": [
+ { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that will be destroyed." }
+ ]
+ },
+ {
"name": "shadowRootPushed",
"description": "Called when shadow root is pushed into the element.",
"targetTypes": ["page"],
Modified: trunk/Source/WebCore/ChangeLog (278784 => 278785)
--- trunk/Source/WebCore/ChangeLog 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/ChangeLog 2021-06-11 22:37:40 UTC (rev 278785)
@@ -1,3 +1,69 @@
+2021-06-11 Patrick Angle <[email protected]>
+
+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent
+ https://bugs.webkit.org/show_bug.cgi?id=226624
+
+ Reviewed by Devin Rousso.
+
+ Test: inspector/dom/willDestroyDOMNode.html
+
+ Add instrumentation for destruction of nodes in order to cease instrumenting nodes and inform the frontend that
+ the node no longer exists. This work serves as a prelude to <https://webkit.org/b/189687> (Web Inspector:
+ preserve DOM.NodeId if a node is removed and re-added) to eventually only forget about nodes upon destruction,
+ instead of removal from the DOM tree. Additionally, the storage of nodes is simplified down to two inverse maps,
+ one that maps `Node` to `NodeId`, and another that maps `NodeId` to `Node`. These are kept in sync throughout,
+ and both attached and detached nodes are now handled as part of these two maps of Nodes.
+
+ * dom/Node.cpp:
+ (WebCore::Node::~Node):
+ * inspector/InspectorInstrumentation.cpp:
+ (WebCore::InspectorInstrumentation::willDestroyDOMNodeImpl):
+ * inspector/InspectorInstrumentation.h:
+ (WebCore::InspectorInstrumentation::didRemoveDOMNode):
+ (WebCore::InspectorInstrumentation::willDestroyDOMNode):
+ * inspector/agents/InspectorCSSAgent.cpp:
+ (WebCore::InspectorCSSAgent::didRemoveDOMNode):
+ * inspector/agents/InspectorDOMAgent.cpp:
+ (WebCore::InspectorDOMAgent::InspectorDOMAgent):
+ (WebCore::InspectorDOMAgent::reset):
+ (WebCore::InspectorDOMAgent::bind):
+ (WebCore::InspectorDOMAgent::unbind):
+ (WebCore::InspectorDOMAgent::getDocument):
+ (WebCore::InspectorDOMAgent::pushChildNodesToFrontend):
+ (WebCore::InspectorDOMAgent::discardBindings):
+ (WebCore::InspectorDOMAgent::pushNodePathToFrontend):
+ (WebCore::InspectorDOMAgent::boundNodeId):
+ - Add a check that the `Node*` is a valid key (not `nullptr`) before getting its id.
+ (WebCore::InspectorDOMAgent::buildObjectForNode):
+ (WebCore::InspectorDOMAgent::buildArrayForContainerChildren):
+ (WebCore::InspectorDOMAgent::buildArrayForPseudoElements):
+ (WebCore::InspectorDOMAgent::didCommitLoad):
+ (WebCore::InspectorDOMAgent::didInsertDOMNode):
+ (WebCore::InspectorDOMAgent::didRemoveDOMNode):
+ (WebCore::InspectorDOMAgent::willDestroyDOMNode):
+ (WebCore::InspectorDOMAgent::destroyedNodesTimerFired):
+ - Added instrumentation point for DOM nodes being destroyed so they can be removed from the agent, and the
+ frontend can also be informed of their ceasing to exist.
+ (WebCore::InspectorDOMAgent::characterDataModified):
+ (WebCore::InspectorDOMAgent::didInvalidateStyleAttr):
+ (WebCore::InspectorDOMAgent::didPushShadowRoot):
+ (WebCore::InspectorDOMAgent::willPopShadowRoot):
+ (WebCore::InspectorDOMAgent::didChangeCustomElementState):
+ (WebCore::InspectorDOMAgent::pseudoElementCreated):
+ (WebCore::InspectorDOMAgent::pseudoElementDestroyed):
+ (WebCore::InspectorDOMAgent::releaseDanglingNodes): Deleted.
+ - Removed usage of NodeToIdMap and nested maps of nodes throughout in favor of two inverse maps for relating
+ `Node`s and `NodeId`s. Because there is now a single set of canonical node maps, we no longer to to pass a
+ NodeToIdMap throughout the agent.
+ * inspector/agents/InspectorDOMAgent.h:
+ * inspector/agents/page/PageConsoleAgent.cpp:
+ (WebCore::PageConsoleAgent::PageConsoleAgent):
+ (WebCore::PageConsoleAgent::clearMessages):
+ * inspector/agents/page/PageConsoleAgent.h:
+ * inspector/agents/page/PageDOMDebuggerAgent.cpp:
+ (WebCore::PageDOMDebuggerAgent::willDestroyDOMNode):
+ * inspector/agents/page/PageDOMDebuggerAgent.h:
+
2021-06-11 Yusuke Suzuki <[email protected]>
Add fast-path for binding security check of DOMWindow
Modified: trunk/Source/WebCore/dom/Node.cpp (278784 => 278785)
--- trunk/Source/WebCore/dom/Node.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/dom/Node.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -50,6 +50,7 @@
#include "HTMLStyleElement.h"
#include "InputEvent.h"
#include "InspectorController.h"
+#include "InspectorInstrumentation.h"
#include "KeyboardEvent.h"
#include "Logging.h"
#include "MutationEvent.h"
@@ -349,6 +350,8 @@
ASSERT(m_deletionHasBegun);
ASSERT(!m_adoptionIsRequired);
+ InspectorInstrumentation::willDestroyDOMNode(*this);
+
#ifndef NDEBUG
if (!ignoreSet().remove(*this))
nodeCounter.decrement();
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp (278784 => 278785)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -176,6 +176,14 @@
domAgent->didRemoveDOMNode(node);
}
+void InspectorInstrumentation::willDestroyDOMNodeImpl(InstrumentingAgents& instrumentingAgents, Node& node)
+{
+ if (auto* pageDOMDebuggerAgent = instrumentingAgents.enabledPageDOMDebuggerAgent())
+ pageDOMDebuggerAgent->willDestroyDOMNode(node);
+ if (auto* domAgent = instrumentingAgents.persistentDOMAgent())
+ domAgent->willDestroyDOMNode(node);
+}
+
void InspectorInstrumentation::nodeLayoutContextChangedImpl(InstrumentingAgents& instrumentingAgents, Node& node, RenderObject* newRenderer)
{
if (auto* cssAgent = instrumentingAgents.enabledCSSAgent())
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (278784 => 278785)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2021-06-11 22:37:40 UTC (rev 278785)
@@ -125,6 +125,7 @@
static void didInsertDOMNode(Document&, Node&);
static void willRemoveDOMNode(Document&, Node&);
static void didRemoveDOMNode(Document&, Node&);
+ static void willDestroyDOMNode(Node&);
static void nodeLayoutContextChanged(Node&, RenderObject*);
static void willModifyDOMAttr(Document&, Element&, const AtomString& oldValue, const AtomString& newValue);
static void didModifyDOMAttr(Document&, Element&, const AtomString& name, const AtomString& value);
@@ -350,6 +351,7 @@
static void didInsertDOMNodeImpl(InstrumentingAgents&, Node&);
static void willRemoveDOMNodeImpl(InstrumentingAgents&, Node&);
static void didRemoveDOMNodeImpl(InstrumentingAgents&, Node&);
+ static void willDestroyDOMNodeImpl(InstrumentingAgents&, Node&);
static void nodeLayoutContextChangedImpl(InstrumentingAgents&, Node&, RenderObject*);
static void willModifyDOMAttrImpl(InstrumentingAgents&, Element&, const AtomString& oldValue, const AtomString& newValue);
static void didModifyDOMAttrImpl(InstrumentingAgents&, Element&, const AtomString& name, const AtomString& value);
@@ -601,6 +603,13 @@
didRemoveDOMNodeImpl(*agents, node);
}
+inline void InspectorInstrumentation::willDestroyDOMNode(Node& node)
+{
+ FAST_RETURN_IF_NO_FRONTENDS(void());
+ if (auto* agents = instrumentingAgents(node.document()))
+ willDestroyDOMNodeImpl(*agents, node);
+}
+
inline void InspectorInstrumentation::nodeLayoutContextChanged(Node& node, RenderObject* newRenderer)
{
FAST_RETURN_IF_NO_FRONTENDS(void());
Modified: trunk/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -1159,6 +1159,7 @@
void InspectorCSSAgent::didRemoveDOMNode(Node& node, Protocol::DOM::NodeId nodeId)
{
+ // This can be called in response to GC.
m_nodeIdToForcedPseudoState.remove(nodeId);
auto sheet = m_nodeToInspectorStyleSheet.take(&node);
Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -288,6 +288,7 @@
, m_backendDispatcher(Inspector::DOMBackendDispatcher::create(context.backendDispatcher, this))
, m_inspectedPage(context.inspectedPage)
, m_overlay(overlay)
+ , m_destroyedNodesTimer(*this, &InspectorDOMAgent::destroyedNodesTimerFired)
#if ENABLE(VIDEO)
, m_mediaMetricsTimer(*this, &InspectorDOMAgent::mediaMetricsTimerFired)
#endif
@@ -353,6 +354,11 @@
if (m_revalidateStyleAttrTask)
m_revalidateStyleAttrTask->reset();
m_document = nullptr;
+
+ m_destroyedDetachedNodeIdentifiers.clear();
+ m_destroyedAttachedNodeIdentifiers.clear();
+ if (m_destroyedNodesTimer.isActive())
+ m_destroyedNodesTimer.stop();
}
void InspectorDOMAgent::setDocument(Document* document)
@@ -372,56 +378,46 @@
m_frontendDispatcher->documentUpdated();
}
-void InspectorDOMAgent::releaseDanglingNodes()
+Protocol::DOM::NodeId InspectorDOMAgent::bind(Node& node)
{
- m_danglingNodeToIdMaps.clear();
-}
-
-Protocol::DOM::NodeId InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
-{
- auto id = nodesMap->get(node);
- if (id)
+ return m_nodeToId.ensure(&node, [&] {
+ auto id = m_lastNodeId++;
+ m_idToNode.set(id, &node);
return id;
- id = m_lastNodeId++;
- nodesMap->set(node, id);
- m_idToNode.set(id, node);
- m_idToNodesMap.set(id, nodesMap);
- return id;
+ }).iterator->value;
}
-void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
+void InspectorDOMAgent::unbind(Node& node)
{
- auto id = nodesMap->get(node);
+ auto id = m_nodeToId.take(&node);
if (!id)
return;
m_idToNode.remove(id);
- if (node->isFrameOwnerElement()) {
- const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
+ if (node.isFrameOwnerElement()) {
+ const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(&node);
if (Document* contentDocument = frameOwner->contentDocument())
- unbind(contentDocument, nodesMap);
+ unbind(*contentDocument);
}
- if (is<Element>(*node)) {
- Element& element = downcast<Element>(*node);
+ if (is<Element>(node)) {
+ Element& element = downcast<Element>(node);
if (ShadowRoot* root = element.shadowRoot())
- unbind(root, nodesMap);
+ unbind(*root);
if (PseudoElement* beforeElement = element.beforePseudoElement())
- unbind(beforeElement, nodesMap);
+ unbind(*beforeElement);
if (PseudoElement* afterElement = element.afterPseudoElement())
- unbind(afterElement, nodesMap);
+ unbind(*afterElement);
}
- nodesMap->remove(node);
-
if (auto* cssAgent = m_instrumentingAgents.enabledCSSAgent())
- cssAgent->didRemoveDOMNode(*node, id);
+ cssAgent->didRemoveDOMNode(node, id);
if (m_childrenRequested.remove(id)) {
// FIXME: Would be better to do this iteratively rather than recursively.
- for (Node* child = innerFirstChild(node); child; child = innerNextSibling(child))
- unbind(child, nodesMap);
+ for (Node* child = innerFirstChild(&node); child; child = innerNextSibling(child))
+ unbind(*child);
}
}
@@ -499,7 +495,7 @@
reset();
m_document = document;
- auto root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
+ auto root = buildObjectForNode(m_document.get(), 2);
if (m_nodeToFocus)
focusNode();
@@ -513,8 +509,6 @@
if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
return;
- NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
-
if (m_childrenRequested.contains(nodeId)) {
if (depth <= 1)
return;
@@ -522,7 +516,7 @@
depth--;
for (node = innerFirstChild(node); node; node = innerNextSibling(node)) {
- auto childNodeId = nodeMap->get(node);
+ auto childNodeId = boundNodeId(node);
ASSERT(childNodeId);
pushChildNodesToFrontend(childNodeId, depth);
}
@@ -530,17 +524,16 @@
return;
}
- auto children = buildArrayForContainerChildren(node, depth, nodeMap);
+ auto children = buildArrayForContainerChildren(node, depth);
m_frontendDispatcher->setChildNodes(nodeId, WTFMove(children));
}
void InspectorDOMAgent::discardBindings()
{
- m_documentNodeToIdMap.clear();
+ m_nodeToId.clear();
m_idToNode.clear();
m_dispatchedEvents.clear();
m_eventListenerEntries.clear();
- releaseDanglingNodes();
m_childrenRequested.clear();
}
@@ -653,51 +646,48 @@
}
// FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
- if (!m_documentNodeToIdMap.contains(m_document)) {
+ if (!m_nodeToId.contains(m_document.get())) {
errorString = "Document must have been requested"_s;
return 0;
}
// Return id in case the node is known.
- if (auto result = m_documentNodeToIdMap.get(nodeToPush))
+ if (auto result = boundNodeId(nodeToPush))
return result;
Node* node = nodeToPush;
Vector<Node*> path;
- NodeToIdMap* danglingMap = 0;
while (true) {
Node* parent = innerParentNode(node);
if (!parent) {
// Node being pushed is detached -> push subtree root.
- auto newMap = makeUnique<NodeToIdMap>();
- danglingMap = newMap.get();
- m_danglingNodeToIdMaps.append(newMap.release());
auto children = JSON::ArrayOf<Protocol::DOM::Node>::create();
- children->addItem(buildObjectForNode(node, 0, danglingMap));
+ children->addItem(buildObjectForNode(node, 0));
m_frontendDispatcher->setChildNodes(0, WTFMove(children));
break;
} else {
path.append(parent);
- if (m_documentNodeToIdMap.get(parent))
+ if (boundNodeId(parent))
break;
- else
- node = parent;
+ node = parent;
}
}
- NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
for (int i = path.size() - 1; i >= 0; --i) {
- auto nodeId = map->get(path.at(i));
+ auto nodeId = boundNodeId(path.at(i));
ASSERT(nodeId);
pushChildNodesToFrontend(nodeId);
}
- return map->get(nodeToPush);
+ return boundNodeId(nodeToPush);
}
Protocol::DOM::NodeId InspectorDOMAgent::boundNodeId(const Node* node)
{
- return m_documentNodeToIdMap.get(const_cast<Node*>(node));
+ if (!m_nodeToId.isValidKey(node))
+ return 0;
+
+ return m_nodeToId.get(node);
}
Protocol::ErrorStringOr<void> InspectorDOMAgent::setAttributeValue(Protocol::DOM::NodeId nodeId, const String& name, const String& value)
@@ -1711,9 +1701,9 @@
return makeString("sha256-", base64Encoded(digest.data(), digest.size()));
}
-Ref<Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
+Ref<Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth)
{
- auto id = bind(node, nodesMap);
+ auto id = bind(*node);
String nodeName;
String localName;
String nodeValue;
@@ -1755,7 +1745,7 @@
if (node->isContainerNode()) {
int nodeCount = innerChildNodeCount(node);
value->setChildNodeCount(nodeCount);
- auto children = buildArrayForContainerChildren(node, depth, nodesMap);
+ auto children = buildArrayForContainerChildren(node, depth);
if (children->length() > 0)
value->setChildren(WTFMove(children));
}
@@ -1774,17 +1764,17 @@
value->setAttributes(buildArrayForElementAttributes(&element));
if (is<HTMLFrameOwnerElement>(element)) {
if (auto* document = downcast<HTMLFrameOwnerElement>(element).contentDocument())
- value->setContentDocument(buildObjectForNode(document, 0, nodesMap));
+ value->setContentDocument(buildObjectForNode(document, 0));
}
if (ShadowRoot* root = element.shadowRoot()) {
auto shadowRoots = JSON::ArrayOf<Protocol::DOM::Node>::create();
- shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap));
+ shadowRoots->addItem(buildObjectForNode(root, 0));
value->setShadowRoots(WTFMove(shadowRoots));
}
if (is<HTMLTemplateElement>(element))
- value->setTemplateContent(buildObjectForNode(&downcast<HTMLTemplateElement>(element).content(), 0, nodesMap));
+ value->setTemplateContent(buildObjectForNode(&downcast<HTMLTemplateElement>(element).content(), 0));
if (is<HTMLStyleElement>(element) || (is<HTMLScriptElement>(element) && !element.hasAttributeWithoutSynchronization(HTMLNames::srcAttr)))
value->setContentSecurityPolicyHash(computeContentSecurityPolicySHA256Hash(element));
@@ -1798,7 +1788,7 @@
if (pseudoElementType(element.pseudoId(), &pseudoType))
value->setPseudoType(pseudoType);
} else {
- if (auto pseudoElements = buildArrayForPseudoElements(element, nodesMap))
+ if (auto pseudoElements = buildArrayForPseudoElements(element))
value->setPseudoElements(pseudoElements.releaseNonNull());
}
} else if (is<Document>(*node)) {
@@ -1838,7 +1828,7 @@
return attributesValue;
}
-Ref<JSON::ArrayOf<Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
+Ref<JSON::ArrayOf<Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth)
{
auto children = JSON::ArrayOf<Protocol::DOM::Node>::create();
if (depth == 0) {
@@ -1845,8 +1835,8 @@
// Special-case the only text child - pretend that container's children have been requested.
Node* firstChild = container->firstChild();
if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) {
- children->addItem(buildObjectForNode(firstChild, 0, nodesMap));
- m_childrenRequested.add(bind(container, nodesMap));
+ children->addItem(buildObjectForNode(firstChild, 0));
+ m_childrenRequested.add(bind(*container));
}
return children;
}
@@ -1853,16 +1843,16 @@
Node* child = innerFirstChild(container);
depth--;
- m_childrenRequested.add(bind(container, nodesMap));
+ m_childrenRequested.add(bind(*container));
while (child) {
- children->addItem(buildObjectForNode(child, depth, nodesMap));
+ children->addItem(buildObjectForNode(child, depth));
child = innerNextSibling(child);
}
return children;
}
-RefPtr<JSON::ArrayOf<Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForPseudoElements(const Element& element, NodeToIdMap* nodesMap)
+RefPtr<JSON::ArrayOf<Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForPseudoElements(const Element& element)
{
PseudoElement* beforeElement = element.beforePseudoElement();
PseudoElement* afterElement = element.afterPseudoElement();
@@ -1871,9 +1861,9 @@
auto pseudoElements = JSON::ArrayOf<Protocol::DOM::Node>::create();
if (beforeElement)
- pseudoElements->addItem(buildObjectForNode(beforeElement, 0, nodesMap));
+ pseudoElements->addItem(buildObjectForNode(beforeElement, 0));
if (afterElement)
- pseudoElements->addItem(buildObjectForNode(afterElement, 0, nodesMap));
+ pseudoElements->addItem(buildObjectForNode(afterElement, 0));
return pseudoElements;
}
@@ -2362,18 +2352,18 @@
if (!frameOwner)
return;
- auto frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
+ auto frameOwnerId = boundNodeId(frameOwner.get());
if (!frameOwnerId)
return;
// Re-add frame owner element together with its new children.
- auto parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner.get()));
+ auto parentId = boundNodeId(innerParentNode(frameOwner.get()));
m_frontendDispatcher->childNodeRemoved(parentId, frameOwnerId);
- unbind(frameOwner.get(), &m_documentNodeToIdMap);
+ unbind(*frameOwner);
- auto value = buildObjectForNode(frameOwner.get(), 0, &m_documentNodeToIdMap);
+ auto value = buildObjectForNode(frameOwner.get(), 0);
Node* previousSibling = innerPreviousSibling(frameOwner.get());
- auto prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
+ auto prevId = boundNodeId(previousSibling);
m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value));
}
@@ -2428,13 +2418,11 @@
return;
// We could be attaching existing subtree. Forget the bindings.
- unbind(&node, &m_documentNodeToIdMap);
+ unbind(node);
ContainerNode* parent = node.parentNode();
- if (!parent)
- return;
- auto parentId = m_documentNodeToIdMap.get(parent);
+ auto parentId = boundNodeId(parent);
// Return if parent is not mapped yet.
if (!parentId)
return;
@@ -2445,8 +2433,8 @@
} else {
// Children have been requested -> return value of a new child.
Node* prevSibling = innerPreviousSibling(&node);
- auto prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
- auto value = buildObjectForNode(&node, 0, &m_documentNodeToIdMap);
+ auto prevId = boundNodeId(prevSibling);
+ auto value = buildObjectForNode(&node, 0);
m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value));
}
}
@@ -2458,21 +2446,65 @@
ContainerNode* parent = node.parentNode();
+ auto parentId = boundNodeId(parent);
// If parent is not mapped yet -> ignore the event.
- if (!m_documentNodeToIdMap.contains(parent))
+ if (!parentId)
return;
- auto parentId = m_documentNodeToIdMap.get(parent);
-
+ // FIXME: <webkit.org/b/189687> Preserve DOM.NodeId if a node is removed and re-added
if (!m_childrenRequested.contains(parentId)) {
// No children are mapped yet -> only notify on changes of hasChildren.
if (innerChildNodeCount(parent) == 1)
m_frontendDispatcher->childNodeCountUpdated(parentId, 0);
} else
- m_frontendDispatcher->childNodeRemoved(parentId, m_documentNodeToIdMap.get(&node));
- unbind(&node, &m_documentNodeToIdMap);
+ m_frontendDispatcher->childNodeRemoved(parentId, boundNodeId(&node));
+ unbind(node);
}
+void InspectorDOMAgent::willDestroyDOMNode(Node& node)
+{
+ if (containsOnlyHTMLWhitespace(&node))
+ return;
+
+ auto nodeId = m_nodeToId.take(&node);
+ if (!nodeId)
+ return;
+
+ m_idToNode.remove(nodeId);
+ m_childrenRequested.remove(nodeId);
+
+ if (auto* cssAgent = m_instrumentingAgents.enabledCSSAgent())
+ cssAgent->didRemoveDOMNode(node, nodeId);
+
+ // This can be called in response to GC. Due to the single-process model used in WebKit1, the
+ // event must be dispatched from a timer to prevent the frontend from making JS allocations
+ // while the GC is still active.
+
+ // FIXME: <webkit.org/b/189687> Unify m_destroyedAttachedNodeIdentifiers and m_destroyedDetachedNodeIdentifiers.
+ if (auto parentId = boundNodeId(node.parentNode()))
+ m_destroyedAttachedNodeIdentifiers.append({ parentId, nodeId });
+ else
+ m_destroyedDetachedNodeIdentifiers.append(nodeId);
+
+ if (!m_destroyedNodesTimer.isActive())
+ m_destroyedNodesTimer.startOneShot(0_s);
+}
+
+void InspectorDOMAgent::destroyedNodesTimerFired()
+{
+ for (auto& [parentId, nodeId] : std::exchange(m_destroyedAttachedNodeIdentifiers, { })) {
+ if (!m_childrenRequested.contains(parentId)) {
+ auto* parent = nodeForId(parentId);
+ if (parent && innerChildNodeCount(parent) == 1)
+ m_frontendDispatcher->childNodeCountUpdated(parentId, 0);
+ } else
+ m_frontendDispatcher->childNodeRemoved(parentId, nodeId);
+ }
+
+ for (auto nodeId : std::exchange(m_destroyedDetachedNodeIdentifiers, { }))
+ m_frontendDispatcher->willDestroyDOMNode(nodeId);
+}
+
void InspectorDOMAgent::willModifyDOMAttr(Element&, const AtomString& oldValue, const AtomString& newValue)
{
m_suppressAttributeModifiedEvent = (oldValue == newValue);
@@ -2525,7 +2557,7 @@
void InspectorDOMAgent::characterDataModified(CharacterData& characterData)
{
- auto id = m_documentNodeToIdMap.get(&characterData);
+ auto id = boundNodeId(&characterData);
if (!id) {
// Push text node if it is being created.
didInsertDOMNode(characterData);
@@ -2536,7 +2568,7 @@
void InspectorDOMAgent::didInvalidateStyleAttr(Element& element)
{
- auto id = m_documentNodeToIdMap.get(&element);
+ auto id = boundNodeId(&element);
if (!id)
return;
@@ -2547,15 +2579,15 @@
void InspectorDOMAgent::didPushShadowRoot(Element& host, ShadowRoot& root)
{
- auto hostId = m_documentNodeToIdMap.get(&host);
+ auto hostId = boundNodeId(&host);
if (hostId)
- m_frontendDispatcher->shadowRootPushed(hostId, buildObjectForNode(&root, 0, &m_documentNodeToIdMap));
+ m_frontendDispatcher->shadowRootPushed(hostId, buildObjectForNode(&root, 0));
}
void InspectorDOMAgent::willPopShadowRoot(Element& host, ShadowRoot& root)
{
- auto hostId = m_documentNodeToIdMap.get(&host);
- auto rootId = m_documentNodeToIdMap.get(&root);
+ auto hostId = boundNodeId(&host);
+ auto rootId = boundNodeId(&root);
if (hostId && rootId)
m_frontendDispatcher->shadowRootPopped(hostId, rootId);
}
@@ -2562,7 +2594,7 @@
void InspectorDOMAgent::didChangeCustomElementState(Element& element)
{
- auto elementId = m_documentNodeToIdMap.get(&element);
+ auto elementId = boundNodeId(&element);
if (!elementId)
return;
@@ -2589,17 +2621,17 @@
if (!parent)
return;
- auto parentId = m_documentNodeToIdMap.get(parent);
+ auto parentId = boundNodeId(parent);
if (!parentId)
return;
pushChildNodesToFrontend(parentId, 1);
- m_frontendDispatcher->pseudoElementAdded(parentId, buildObjectForNode(&pseudoElement, 0, &m_documentNodeToIdMap));
+ m_frontendDispatcher->pseudoElementAdded(parentId, buildObjectForNode(&pseudoElement, 0));
}
void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement& pseudoElement)
{
- auto pseudoElementId = m_documentNodeToIdMap.get(&pseudoElement);
+ auto pseudoElementId = boundNodeId(&pseudoElement);
if (!pseudoElementId)
return;
@@ -2606,10 +2638,10 @@
// If a PseudoElement is bound, its parent element must have been bound.
Element* parent = pseudoElement.hostElement();
ASSERT(parent);
- auto parentId = m_documentNodeToIdMap.get(parent);
+ auto parentId = boundNodeId(parent);
ASSERT(parentId);
- unbind(&pseudoElement, &m_documentNodeToIdMap);
+ unbind(pseudoElement);
m_frontendDispatcher->pseudoElementRemoved(parentId, pseudoElementId);
}
Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h 2021-06-11 22:37:40 UTC (rev 278785)
@@ -159,6 +159,7 @@
void addEventListenersToNode(Node&);
void didInsertDOMNode(Node&);
void didRemoveDOMNode(Node&);
+ void willDestroyDOMNode(Node&);
void willModifyDOMAttr(Element&, const AtomString& oldValue, const AtomString& newValue);
void didModifyDOMAttr(Element&, const AtomString& name, const AtomString& value);
void didRemoveDOMAttr(Element&, const AtomString& name);
@@ -179,7 +180,6 @@
// Callbacks that don't directly correspond to an instrumentation entry point.
void setDocument(Document*);
- void releaseDanglingNodes();
void styleAttributeInvalidated(const Vector<Element*>& elements);
@@ -218,9 +218,8 @@
std::unique_ptr<InspectorOverlay::Grid::Config> gridOverlayConfigFromInspectorObject(Inspector::Protocol::ErrorString&, RefPtr<JSON::Object>&& gridOverlayInspectorObject);
// Node-related methods.
- typedef HashMap<RefPtr<Node>, Inspector::Protocol::DOM::NodeId> NodeToIdMap;
- Inspector::Protocol::DOM::NodeId bind(Node*, NodeToIdMap*);
- void unbind(Node*, NodeToIdMap*);
+ Inspector::Protocol::DOM::NodeId bind(Node&);
+ void unbind(Node&);
Node* assertEditableNode(Inspector::Protocol::ErrorString&, Inspector::Protocol::DOM::NodeId);
Element* assertEditableElement(Inspector::Protocol::ErrorString&, Inspector::Protocol::DOM::NodeId);
@@ -227,10 +226,10 @@
void pushChildNodesToFrontend(Inspector::Protocol::DOM::NodeId, int depth = 1);
- Ref<Inspector::Protocol::DOM::Node> buildObjectForNode(Node*, int depth, NodeToIdMap*);
+ Ref<Inspector::Protocol::DOM::Node> buildObjectForNode(Node*, int depth);
Ref<JSON::ArrayOf<String>> buildArrayForElementAttributes(Element*);
- Ref<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap);
- RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForPseudoElements(const Element&, NodeToIdMap* nodesMap);
+ Ref<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForContainerChildren(Node* container, int depth);
+ RefPtr<JSON::ArrayOf<Inspector::Protocol::DOM::Node>> buildArrayForPseudoElements(const Element&);
Ref<Inspector::Protocol::DOM::EventListener> buildObjectForEventListener(const RegisteredEventListener&, Inspector::Protocol::DOM::EventListenerId identifier, EventTarget&, const AtomString& eventType, bool disabled, const RefPtr<JSC::Breakpoint>&);
Ref<Inspector::Protocol::DOM::AccessibilityProperties> buildObjectForAccessibilityProperties(Node&);
void processAccessibilityChildren(AXCoreObject&, JSON::ArrayOf<Inspector::Protocol::DOM::NodeId>&);
@@ -242,16 +241,15 @@
void innerHighlightQuad(std::unique_ptr<FloatQuad>, RefPtr<JSON::Object>&& color, RefPtr<JSON::Object>&& outlineColor, std::optional<bool>&& usePageCoordinates);
+ void destroyedNodesTimerFired();
+
Inspector::InjectedScriptManager& m_injectedScriptManager;
std::unique_ptr<Inspector::DOMFrontendDispatcher> m_frontendDispatcher;
RefPtr<Inspector::DOMBackendDispatcher> m_backendDispatcher;
Page& m_inspectedPage;
InspectorOverlay* m_overlay { nullptr };
- NodeToIdMap m_documentNodeToIdMap;
- // Owns node mappings for dangling nodes.
- Vector<std::unique_ptr<NodeToIdMap>> m_danglingNodeToIdMaps;
+ HashMap<const Node*, Inspector::Protocol::DOM::NodeId> m_nodeToId;
HashMap<Inspector::Protocol::DOM::NodeId, Node*> m_idToNode;
- HashMap<Inspector::Protocol::DOM::NodeId, NodeToIdMap*> m_idToNodesMap;
HashSet<Inspector::Protocol::DOM::NodeId> m_childrenRequested;
Inspector::Protocol::DOM::NodeId m_lastNodeId { 1 };
RefPtr<Document> m_document;
@@ -265,6 +263,10 @@
std::unique_ptr<InspectorHistory> m_history;
std::unique_ptr<DOMEditor> m_domEditor;
+ Vector<Inspector::Protocol::DOM::NodeId> m_destroyedDetachedNodeIdentifiers;
+ Vector<std::pair<Inspector::Protocol::DOM::NodeId, Inspector::Protocol::DOM::NodeId>> m_destroyedAttachedNodeIdentifiers;
+ Timer m_destroyedNodesTimer;
+
#if ENABLE(VIDEO)
Timer m_mediaMetricsTimer;
struct MediaMetrics {
Modified: trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -47,7 +47,6 @@
PageConsoleAgent::PageConsoleAgent(PageAgentContext& context)
: WebConsoleAgent(context)
- , m_instrumentingAgents(context.instrumentingAgents)
, m_inspectedPage(context.inspectedPage)
{
}
@@ -54,14 +53,6 @@
PageConsoleAgent::~PageConsoleAgent() = default;
-Protocol::ErrorStringOr<void> PageConsoleAgent::clearMessages()
-{
- if (auto* domAgent = m_instrumentingAgents.persistentDOMAgent())
- domAgent->releaseDanglingNodes();
-
- return WebConsoleAgent::clearMessages();
-}
-
Protocol::ErrorStringOr<Ref<JSON::ArrayOf<Protocol::Console::Channel>>> PageConsoleAgent::getLoggingChannels()
{
auto channels = JSON::ArrayOf<Protocol::Console::Channel>::create();
Modified: trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.h (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.h 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/page/PageConsoleAgent.h 2021-06-11 22:37:40 UTC (rev 278785)
@@ -44,12 +44,10 @@
~PageConsoleAgent();
// ConsoleBackendDispatcherHandler
- Inspector::Protocol::ErrorStringOr<void> clearMessages();
Inspector::Protocol::ErrorStringOr<Ref<JSON::ArrayOf<Inspector::Protocol::Console::Channel>>> getLoggingChannels();
Inspector::Protocol::ErrorStringOr<void> setLoggingChannelLevel(Inspector::Protocol::Console::ChannelSource, Inspector::Protocol::Console::ChannelLevel);
private:
- InstrumentingAgents& m_instrumentingAgents;
Page& m_inspectedPage;
};
Modified: trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp 2021-06-11 22:37:40 UTC (rev 278785)
@@ -276,6 +276,13 @@
m_domNodeRemovedBreakpoints.removeIf(nodeContainsBreakpointOwner);
}
+void PageDOMDebuggerAgent::willDestroyDOMNode(Node& node)
+{
+ // This can be called in response to GC.
+ // DOM Node destruction should be treated as if the node was removed from the DOM tree.
+ didRemoveDOMNode(node);
+}
+
void PageDOMDebuggerAgent::willModifyDOMAttr(Element& element)
{
if (!m_debuggerAgent->breakpointsActive())
Modified: trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h (278784 => 278785)
--- trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h 2021-06-11 22:37:40 UTC (rev 278785)
@@ -53,6 +53,7 @@
void willInsertDOMNode(Node& parent);
void willRemoveDOMNode(Node&);
void didRemoveDOMNode(Node&);
+ void willDestroyDOMNode(Node&);
void willModifyDOMAttr(Element&);
void willInvalidateStyleAttr(Element&);
void willFireAnimationFrame();
Modified: trunk/Source/WebInspectorUI/ChangeLog (278784 => 278785)
--- trunk/Source/WebInspectorUI/ChangeLog 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebInspectorUI/ChangeLog 2021-06-11 22:37:40 UTC (rev 278785)
@@ -1,3 +1,22 @@
+2021-06-11 Patrick Angle <[email protected]>
+
+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent
+ https://bugs.webkit.org/show_bug.cgi?id=226624
+
+ Reviewed by Devin Rousso.
+
+ Listen for the new `DOM.willDestroyDOMNode` event in order to cleanup and remaining references to that Node.
+ This work serves as a prelude to <https://webkit.org/b/189687> (Web Inspector: preserve DOM.NodeId if a node is
+ removed and re-added) to eventually only forget about nodes upon destruction, instead of removal from the DOM
+ tree.
+
+ * UserInterface/Controllers/DOMManager.js:
+ (WI.DOMManager.prototype.willDestroyDOMNode):
+ * UserInterface/Protocol/DOMObserver.js:
+ (WI.DOMObserver.prototype.willDestroyDOMNode):
+ * UserInterface/Views/DOMTreeUpdater.js:
+ (WI.DOMTreeUpdater.prototype._nodeRemoved):
+
2021-06-08 Razvan Caliman <[email protected]>
Web Inspector: Styles panel slow to render when inspecting node with many inherited CSS variables
Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js (278784 => 278785)
--- trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js 2021-06-11 22:37:40 UTC (rev 278785)
@@ -185,6 +185,15 @@
// DOMObserver
+ willDestroyDOMNode(nodeId)
+ {
+ let node = this._idToDOMNode[nodeId];
+ node.markDestroyed();
+ delete this._idToDOMNode[nodeId];
+
+ this.dispatchEventToListeners(WI.DOMManager.Event.NodeRemoved, {node});
+ }
+
didAddEventListener(nodeId)
{
let node = this._idToDOMNode[nodeId];
Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js (278784 => 278785)
--- trunk/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js 2021-06-11 22:37:40 UTC (rev 278785)
@@ -77,6 +77,11 @@
WI.domManager._childNodeRemoved(parentNodeId, nodeId);
}
+ willDestroyDOMNode(nodeId)
+ {
+ WI.domManager.willDestroyDOMNode(nodeId);
+ }
+
shadowRootPushed(hostId, root)
{
WI.domManager._childNodeInserted(hostId, 0, root);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js (278784 => 278785)
--- trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js 2021-06-11 22:18:23 UTC (rev 278784)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js 2021-06-11 22:37:40 UTC (rev 278785)
@@ -103,7 +103,11 @@
_nodeRemoved: function(event)
{
- this._recentlyDeletedNodes.set(event.data.node, {parent: event.data.parent});
+ let parent = event.data.parent;
+ if (!parent)
+ return;
+
+ this._recentlyDeletedNodes.set(event.data.node, {parent});
if (this._treeOutline._visible)
this._updateModifiedNodesDebouncer.delayForFrame();
},