Bug 1696118 - Fix and add a test for dom mutation breakpoints in shadow dom. r=jdescottes,devtools-reviewers

Turns out this never worked because of the document.contains() check.

Differential Revision: https://phabricator.services.mozilla.com/D175272
This commit is contained in:
Emilio Cobos Álvarez 2023-04-12 20:14:00 +00:00
parent 9404d81ceb
commit bf4eaad67a
6 changed files with 104 additions and 60 deletions

View File

@ -28,21 +28,45 @@ add_task(async function() {
const { inspector, toolbox } = await openInspectorForURL(DMB_TEST_URL);
info("Selecting the body node");
await selectNode("body", inspector);
{
info("Selecting the body node");
await selectNode("body", inspector);
info("Adding DOM mutation breakpoints to body");
const allMenuItems = openContextMenuAndGetAllItems(inspector);
info("Adding DOM mutation breakpoints to body");
const allMenuItems = openContextMenuAndGetAllItems(inspector);
const attributeMenuItem = allMenuItems.find(
item => item.id === "node-menu-mutation-breakpoint-attribute"
);
attributeMenuItem.click();
const attributeMenuItem = allMenuItems.find(
item => item.id === "node-menu-mutation-breakpoint-attribute"
);
attributeMenuItem.click();
const subtreeMenuItem = allMenuItems.find(
item => item.id === "node-menu-mutation-breakpoint-subtree"
);
subtreeMenuItem.click();
const subtreeMenuItem = allMenuItems.find(
item => item.id === "node-menu-mutation-breakpoint-subtree"
);
subtreeMenuItem.click();
}
{
info("Find and expand the shadow host.");
const hostFront = await getNodeFront("#host", inspector);
const hostContainer = inspector.markup.getContainer(hostFront);
await expandContainer(inspector, hostContainer);
info("Expand the shadow root");
const shadowRootContainer = hostContainer.getChildContainers()[0];
await expandContainer(inspector, shadowRootContainer);
info("Select the div under the shadow root");
const divContainer = shadowRootContainer.getChildContainers()[0];
await selectNode(divContainer.node, inspector);
const allMenuItems = openContextMenuAndGetAllItems(inspector);
info("Adding attribute breakpoint.");
const attributeMenuItem = allMenuItems.find(
item => item.id === "node-menu-mutation-breakpoint-attribute"
);
attributeMenuItem.click();
}
info("Switches over to the debugger pane");
await toolbox.selectTool("jsdebugger");
@ -84,6 +108,13 @@ add_task(async function() {
await waitForPaused(dbg);
await resume(dbg);
info("Changing attribute in shadow dom to trigger debugger pause");
SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
content.document.querySelector("#shadow-attribute").click();
});
await waitForPaused(dbg);
await resume(dbg);
info("Adding element in subtree to trigger debugger pause");
SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
content.document.querySelector("#add-in-subtree").click();
@ -94,7 +125,7 @@ add_task(async function() {
);
is(
whyPaused,
`Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyAdded:div`
`Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyAdded:div#dynamic`
);
await resume(dbg);
@ -109,7 +140,7 @@ add_task(async function() {
);
is(
whyPaused,
`Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyRemoved:div`
`Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyRemoved:div#dynamic`
);
await resume(dbg);
@ -136,5 +167,5 @@ add_task(async function() {
info("Removing breakpoints works");
dbg.win.document.querySelector(".dom-mutation-list .close-btn").click();
await waitForAllElements(dbg, "domMutationItem", 1, true);
await waitForAllElements(dbg, "domMutationItem", 2, true);
});

View File

@ -11,9 +11,11 @@
<body>
<button title="Hello" id="attribute" onclick="changeAttribute()">Click me!</button>
<button id="style-attribute" onclick="changeStyleAttribute()">Click me!</button>
<button id="shadow-attribute" onclick="changeAttributeInShadow()">remove attr in shadow</button>
<button id="add-in-subtree" onclick="addDivToBody()">add div to body</button>
<button id="remove-in-subtree" onclick="removeDivInBody()">remove div in body</button>
<button id="blackbox" onclick="changeAttribute();&#xA;debugger;&#xA;//# sourceURL=click.js">Click me!</button>
<div id="host"></div>
<script src="dom-mutation.js"></script>
</body>
</html>

View File

@ -8,11 +8,19 @@ function changeStyleAttribute() {
}
function addDivToBody() {
document.body.appendChild(document.createElement("div"));
let div = document.createElement("div");
div.id = "dynamic";
document.body.appendChild(div);
}
function removeDivInBody() {
document.body.querySelector("div").remove();
document.body.querySelector("#dynamic").remove();
}
function changeAttributeInShadow() {
document.getElementById("host").shadowRoot.querySelector("div").classList.toggle("red");
}
document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = `<div></div>`;
//# sourceMappingURL=dom-mutation.js.map

View File

@ -793,39 +793,6 @@ function getHighlighterTestHelpers(inspector) {
};
}
// The expand all operation of the markup-view calls itself recursively and
// there's not one event we can wait for to know when it's done so use this
// helper function to wait until all recursive children updates are done.
async function waitForMultipleChildrenUpdates(inspector) {
// As long as child updates are queued up while we wait for an update already
// wait again
if (
inspector.markup._queuedChildUpdates &&
inspector.markup._queuedChildUpdates.size
) {
await waitForChildrenUpdated(inspector);
return waitForMultipleChildrenUpdates(inspector);
}
return null;
}
/**
* Using the markupview's _waitForChildren function, wait for all queued
* children updates to be handled.
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return a promise that resolves when all queued children updates have been
* handled
*/
function waitForChildrenUpdated({ markup }) {
info("Waiting for queued children updates to be handled");
return new Promise(resolve => {
markup._waitForChildren().then(() => {
executeSoon(resolve);
});
});
}
/**
* Wait for the toolbox to emit the styleeditor-selected event and when done
* wait for the stylesheet identified by href to be loaded in the stylesheet
@ -1119,15 +1086,6 @@ async function toggleShapesHighlighter(
}
}
/**
* Expand the provided markup container programatically and wait for all children to
* update.
*/
async function expandContainer(inspector, container) {
await inspector.markup.expandNode(container.node);
await waitForMultipleChildrenUpdates(inspector);
}
/**
* Toggle the provided markup container by clicking on the expand arrow and waiting for
* children to update. Similar to expandContainer helper, but this method

View File

@ -260,6 +260,48 @@ var selectNode = async function(
await onSelectionCssSelectorsUpdated;
};
/**
* Using the markupview's _waitForChildren function, wait for all queued
* children updates to be handled.
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return a promise that resolves when all queued children updates have been
* handled
*/
function waitForChildrenUpdated({ markup }) {
info("Waiting for queued children updates to be handled");
return new Promise(resolve => {
markup._waitForChildren().then(() => {
executeSoon(resolve);
});
});
}
// The expand all operation of the markup-view calls itself recursively and
// there's not one event we can wait for to know when it's done, so use this
// helper function to wait until all recursive children updates are done.
async function waitForMultipleChildrenUpdates(inspector) {
// As long as child updates are queued up while we wait for an update already
// wait again
if (
inspector.markup._queuedChildUpdates &&
inspector.markup._queuedChildUpdates.size
) {
await waitForChildrenUpdated(inspector);
return waitForMultipleChildrenUpdates(inspector);
}
return null;
}
/**
* Expand the provided markup container programmatically and wait for all
* children to update.
*/
async function expandContainer(inspector, container) {
await inspector.markup.expandNode(container.node);
await waitForMultipleChildrenUpdates(inspector);
}
/**
* Get the NodeFront for a node that matches a given css selector inside a
* given iframe.

View File

@ -1779,7 +1779,10 @@ class WalkerActor extends Actor {
}
const rawNode = node.rawNode;
if (rawNode.ownerDocument && !rawNode.ownerDocument.contains(rawNode)) {
if (
rawNode.ownerDocument &&
rawNode.getRootNode({ composed: true }) != rawNode.ownerDocument
) {
// We only allow watching for mutations on nodes that are attached to
// documents. That allows us to clean up our mutation listeners when all
// of the watched nodes have been removed from the document.