WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch v2.3 - Add valid key check to InspectorDOMAgent::boundNodeId
bug-226624-20210611122744.patch (text/plain), 46.12 KB, created by
Patrick Angle
on 2021-06-11 12:27:45 PDT
(
hide
)
Description:
Patch v2.3 - Add valid key check to InspectorDOMAgent::boundNodeId
Filename:
MIME Type:
Creator:
Patrick Angle
Created:
2021-06-11 12:27:45 PDT
Size:
46.12 KB
patch
obsolete
>Subversion Revision: 278325 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index e0b545e8c6e205aa98c1784524909489d352896f..c353de78f97554c368856688b0a7535640fbf549 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,17 @@ >+2021-06-10 Patrick Angle <pangle@apple.com> >+ >+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent >+ https://bugs.webkit.org/show_bug.cgi?id=226624 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-01 Adrian Perez de Castro <aperez@igalia.com> > > [WPE][GTK] Support building against uClibc >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 960fb2dfcc2e24b52528fa11f18d6695579c5514..4f207742994065bfa2ca114867678710922a38e7 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,69 @@ >+2021-06-10 Patrick Angle <pangle@apple.com> >+ >+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent >+ https://bugs.webkit.org/show_bug.cgi?id=226624 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-01 Cameron McCormack <heycam@apple.com> > > Remove duplicate "behavior for fixed" entry from scrolling state tree logging >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index 3c6177312ef696fb62ba8928b1f4f4cf8134dcc0..81233c3b61ae4a97b23d44a7b78926c2abf8adc2 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,22 @@ >+2021-06-10 Patrick Angle <pangle@apple.com> >+ >+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent >+ https://bugs.webkit.org/show_bug.cgi?id=226624 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-05-15 Said Abou-Hallawa <said@apple.com> > > Implement CanvasRenderingContext2D.createConicGradient >diff --git a/Source/JavaScriptCore/inspector/protocol/DOM.json b/Source/JavaScriptCore/inspector/protocol/DOM.json >index 1620c003f076eee31ddc8be69a84e4995299892a..3a91ac6b57fad7f472daa2e2107b3b483de70165 100644 >--- a/Source/JavaScriptCore/inspector/protocol/DOM.json >+++ b/Source/JavaScriptCore/inspector/protocol/DOM.json >@@ -678,6 +678,13 @@ > { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." } > ] > }, >+ { >+ "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.", >diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp >index fdabcd3e7b892e501d879432ac46c3d713f8eb00..a410f9486b6a2b8cfae8f021c2c29abbeea3bb55 100644 >--- a/Source/WebCore/dom/Node.cpp >+++ b/Source/WebCore/dom/Node.cpp >@@ -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 @@ Node::~Node() > ASSERT(m_deletionHasBegun); > ASSERT(!m_adoptionIsRequired); > >+ InspectorInstrumentation::willDestroyDOMNode(*this); >+ > #ifndef NDEBUG > if (!ignoreSet().remove(*this)) > nodeCounter.decrement(); >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.cpp b/Source/WebCore/inspector/InspectorInstrumentation.cpp >index 218ce4b947c42547ec4f9cf9e8517a5dc352c4c4..a60d464344fd90140288a0acce5b39b11144147b 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.cpp >+++ b/Source/WebCore/inspector/InspectorInstrumentation.cpp >@@ -176,6 +176,14 @@ void InspectorInstrumentation::didRemoveDOMNodeImpl(InstrumentingAgents& instrum > 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()) >diff --git a/Source/WebCore/inspector/InspectorInstrumentation.h b/Source/WebCore/inspector/InspectorInstrumentation.h >index 8f040444bb183d162c60f6fd4752becd9ce6ab99..ce10119c861cab954eec37c48e9197c32340cfdc 100644 >--- a/Source/WebCore/inspector/InspectorInstrumentation.h >+++ b/Source/WebCore/inspector/InspectorInstrumentation.h >@@ -125,6 +125,7 @@ public: > 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 @@ private: > 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 @@ inline void InspectorInstrumentation::didRemoveDOMNode(Document& document, Node& > 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()); >diff --git a/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp b/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp >index 04c17572ff656f222a131e80a97313edb5321f4e..6afb2a1a22462c8d64502ecbfea3547a165dd2c7 100644 >--- a/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp >+++ b/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp >@@ -1160,6 +1160,7 @@ RefPtr<Protocol::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle > > 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); >diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >index d462efc306a299b58e78c5d94e6e8757e1734d82..d27cada2664f9969525d05468bdcdbbe50074149 100644 >--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp >@@ -289,6 +289,7 @@ InspectorDOMAgent::InspectorDOMAgent(PageAgentContext& context, InspectorOverlay > , 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 >@@ -354,6 +355,11 @@ void InspectorDOMAgent::reset() > 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) >@@ -373,26 +379,18 @@ void InspectorDOMAgent::setDocument(Document* document) > m_frontendDispatcher->documentUpdated(); > } > >-void InspectorDOMAgent::releaseDanglingNodes() >-{ >- m_danglingNodeToIdMaps.clear(); >-} >- >-Protocol::DOM::NodeId InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap) >+Protocol::DOM::NodeId InspectorDOMAgent::bind(Node* node) > { >- 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; > >@@ -401,28 +399,26 @@ void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) > 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 (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); > > 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); >+ unbind(child); > } > } > >@@ -500,7 +496,7 @@ Protocol::ErrorStringOr<Ref<Protocol::DOM::Node>> InspectorDOMAgent::getDocument > reset(); > m_document = document; > >- auto root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap); >+ auto root = buildObjectForNode(m_document.get(), 2); > > if (m_nodeToFocus) > focusNode(); >@@ -514,8 +510,6 @@ void InspectorDOMAgent::pushChildNodesToFrontend(Protocol::DOM::NodeId nodeId, i > 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; >@@ -523,7 +517,7 @@ void InspectorDOMAgent::pushChildNodesToFrontend(Protocol::DOM::NodeId nodeId, i > depth--; > > for (node = innerFirstChild(node); node; node = innerNextSibling(node)) { >- auto childNodeId = nodeMap->get(node); >+ auto childNodeId = boundNodeId(node); > ASSERT(childNodeId); > pushChildNodesToFrontend(childNodeId, depth); > } >@@ -531,17 +525,16 @@ void InspectorDOMAgent::pushChildNodesToFrontend(Protocol::DOM::NodeId nodeId, i > 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(); > } > >@@ -654,51 +647,48 @@ Protocol::DOM::NodeId InspectorDOMAgent::pushNodePathToFrontend(Protocol::ErrorS > } > > // 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) >@@ -1712,9 +1702,9 @@ static String computeContentSecurityPolicySHA256Hash(const Element& element) > 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; >@@ -1756,7 +1746,7 @@ Ref<Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int d > 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)); > } >@@ -1775,17 +1765,17 @@ Ref<Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int d > 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)); >@@ -1799,7 +1789,7 @@ Ref<Protocol::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int d > 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)) { >@@ -1839,31 +1829,31 @@ Ref<JSON::ArrayOf<String>> InspectorDOMAgent::buildArrayForElementAttributes(Ele > 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) { > // 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; > } > > 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(); >@@ -1872,9 +1862,9 @@ RefPtr<JSON::ArrayOf<Protocol::DOM::Node>> InspectorDOMAgent::buildArrayForPseud > > 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; > } > >@@ -2363,18 +2353,18 @@ void InspectorDOMAgent::didCommitLoad(Document* document) > 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.get()); > >- 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 = previousSibling ? boundNodeId(previousSibling) : 0; > m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value)); > } > >@@ -2429,13 +2419,13 @@ void InspectorDOMAgent::didInsertDOMNode(Node& node) > 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; >@@ -2446,8 +2436,8 @@ void InspectorDOMAgent::didInsertDOMNode(Node& node) > } 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 = prevSibling ? boundNodeId(prevSibling) : 0; >+ auto value = buildObjectForNode(&node, 0); > m_frontendDispatcher->childNodeInserted(parentId, prevId, WTFMove(value)); > } > } >@@ -2459,19 +2449,63 @@ void InspectorDOMAgent::didRemoveDOMNode(Node& node) > > 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) >@@ -2526,7 +2560,7 @@ void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elemen > > 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); >@@ -2537,7 +2571,7 @@ void InspectorDOMAgent::characterDataModified(CharacterData& characterData) > > void InspectorDOMAgent::didInvalidateStyleAttr(Element& element) > { >- auto id = m_documentNodeToIdMap.get(&element); >+ auto id = boundNodeId(&element); > if (!id) > return; > >@@ -2548,22 +2582,22 @@ void InspectorDOMAgent::didInvalidateStyleAttr(Element& element) > > 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); > } > > void InspectorDOMAgent::didChangeCustomElementState(Element& element) > { >- auto elementId = m_documentNodeToIdMap.get(&element); >+ auto elementId = boundNodeId(&element); > if (!elementId) > return; > >@@ -2590,27 +2624,27 @@ void InspectorDOMAgent::pseudoElementCreated(PseudoElement& pseudoElement) > 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; > > // 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); > } > >diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.h b/Source/WebCore/inspector/agents/InspectorDOMAgent.h >index 74a60f618092676d8bee87538c9d697f3c584304..fccd54234007c9562dda999548c5e95b315cf175 100644 >--- a/Source/WebCore/inspector/agents/InspectorDOMAgent.h >+++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.h >@@ -159,6 +159,7 @@ public: > 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 @@ public: > > // Callbacks that don't directly correspond to an instrumentation entry point. > void setDocument(Document*); >- void releaseDanglingNodes(); > > void styleAttributeInvalidated(const Vector<Element*>& elements); > >@@ -218,19 +218,18 @@ private: > 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); > > 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 @@ private: > > 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 @@ private: > 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 { >diff --git a/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp b/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp >index 02830fd0649aff1830555c4ca8fd8dc8fe136d3b..5ed5130e8af2d054e791f40113092213e6471469 100644 >--- a/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp >+++ b/Source/WebCore/inspector/agents/page/PageConsoleAgent.cpp >@@ -47,7 +47,6 @@ using namespace Inspector; > > PageConsoleAgent::PageConsoleAgent(PageAgentContext& context) > : WebConsoleAgent(context) >- , m_instrumentingAgents(context.instrumentingAgents) > , m_inspectedPage(context.inspectedPage) > { > } >@@ -56,9 +55,6 @@ PageConsoleAgent::~PageConsoleAgent() = default; > > Protocol::ErrorStringOr<void> PageConsoleAgent::clearMessages() > { >- if (auto* domAgent = m_instrumentingAgents.persistentDOMAgent()) >- domAgent->releaseDanglingNodes(); >- > return WebConsoleAgent::clearMessages(); > } > >diff --git a/Source/WebCore/inspector/agents/page/PageConsoleAgent.h b/Source/WebCore/inspector/agents/page/PageConsoleAgent.h >index 41fe76d032070e0d4103e40a2c144ce37981c833..fcdd24723fa26f60b1f56823446b423e6fd51431 100644 >--- a/Source/WebCore/inspector/agents/page/PageConsoleAgent.h >+++ b/Source/WebCore/inspector/agents/page/PageConsoleAgent.h >@@ -49,7 +49,6 @@ public: > Inspector::Protocol::ErrorStringOr<void> setLoggingChannelLevel(Inspector::Protocol::Console::ChannelSource, Inspector::Protocol::Console::ChannelLevel); > > private: >- InstrumentingAgents& m_instrumentingAgents; > Page& m_inspectedPage; > }; > >diff --git a/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp b/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp >index 6c2072c42994ce7ee245f708631a6cc83df71a5e..104ada8815d4915cbc29680c5b23e7a6e2d78c07 100644 >--- a/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp >+++ b/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.cpp >@@ -277,6 +277,13 @@ void PageDOMDebuggerAgent::didRemoveDOMNode(Node& node) > 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()) >diff --git a/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h b/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h >index d87282925ec69310e0bc0e7883cdca508d103522..b81b5b315d76cce62b3de41f73b5caf5382ccbb1 100644 >--- a/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h >+++ b/Source/WebCore/inspector/agents/page/PageDOMDebuggerAgent.h >@@ -53,6 +53,7 @@ public: > void willInsertDOMNode(Node& parent); > void willRemoveDOMNode(Node&); > void didRemoveDOMNode(Node&); >+ void willDestroyDOMNode(Node&); > void willModifyDOMAttr(Element&); > void willInvalidateStyleAttr(Element&); > void willFireAnimationFrame(); >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js b/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js >index 70b8fd89a00b8af902f1962b3c1dc3d47e65abc6..8009ce81ce8b1d277731c873cb33df15594d82dc 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js >@@ -185,6 +185,15 @@ WI.DOMManager = class DOMManager extends WI.Object > > // 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]; >diff --git a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >index 64f9a12c63cd1a40a80d23fc2776f98a4bb0346e..1ea9c24758b9a45cf3e294f3fd046324aff42fe1 100644 >--- a/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >+++ b/Source/WebInspectorUI/UserInterface/Protocol/DOMObserver.js >@@ -77,6 +77,11 @@ WI.DOMObserver = class DOMObserver extends InspectorBackend.Dispatcher > WI.domManager._childNodeRemoved(parentNodeId, nodeId); > } > >+ willDestroyDOMNode(nodeId) >+ { >+ WI.domManager.willDestroyDOMNode(nodeId); >+ } >+ > shadowRootPushed(hostId, root) > { > WI.domManager._childNodeInserted(hostId, 0, root); >diff --git a/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js b/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js >index 10f92290f1e16a589ce4a7c8be43e99d2e86d4d8..6c0ae8a16f9436ec6b2efbff2ab4828b321b81e8 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js >+++ b/Source/WebInspectorUI/UserInterface/Views/DOMTreeUpdater.js >@@ -103,7 +103,11 @@ WI.DOMTreeUpdater.prototype = { > > _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(); > }, >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 2a721e2b73755149f6b350130544afb9b1939285..8d57a62931b9228184be536764552167bd5e1dd2 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,13 @@ >+2021-06-10 Patrick Angle <pangle@apple.com> >+ >+ Web Inspector: Add instrumentation to node destruction for InspectorDOMAgent >+ https://bugs.webkit.org/show_bug.cgi?id=226624 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * inspector/dom/willDestroyDOMNode-expected.txt: Added. >+ * inspector/dom/willDestroyDOMNode.html: Added. >+ > 2021-06-01 Sam Weinig <weinig@apple.com> > > Support calc() on components inside relative color syntax colors >diff --git a/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt b/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..9128e6452964a3195de078bf600f416753870d4b >--- /dev/null >+++ b/LayoutTests/inspector/dom/willDestroyDOMNode-expected.txt >@@ -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. >+ >diff --git a/LayoutTests/inspector/dom/willDestroyDOMNode.html b/LayoutTests/inspector/dom/willDestroyDOMNode.html >new file mode 100644 >index 0000000000000000000000000000000000000000..168c9b0d0a0e3c77b2c0bbcb3b1e8848d58581a5 >--- /dev/null >+++ b/LayoutTests/inspector/dom/willDestroyDOMNode.html >@@ -0,0 +1,47 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/protocol-test.js"></script> >+<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
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 226624
:
430538
|
430586
|
430643
|
430922
|
431141
|
431155
|
431174
|
431219
|
431225