Title: [278785] trunk
Revision
278785
Author
[email protected]
Date
2021-06-11 15:37:40 -0700 (Fri, 11 Jun 2021)

Log Message

Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent
https://bugs.webkit.org/show_bug.cgi?id=226624

Reviewed by Devin Rousso.

Source/_javascript_Core:

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:

Source/WebCore:

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:

Source/WebInspectorUI:

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):

LayoutTests:

* inspector/dom/willDestroyDOMNode-expected.txt: Added.
* inspector/dom/willDestroyDOMNode.html: Added.

Modified Paths

Added Paths

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();
     },
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to