summaryrefslogtreecommitdiffstats
path: root/devtools/server/tests/browser/browser_inspector-retain.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/tests/browser/browser_inspector-retain.js')
-rw-r--r--devtools/server/tests/browser/browser_inspector-retain.js158
1 files changed, 158 insertions, 0 deletions
diff --git a/devtools/server/tests/browser/browser_inspector-retain.js b/devtools/server/tests/browser/browser_inspector-retain.js
new file mode 100644
index 0000000000..6216ec8c4c
--- /dev/null
+++ b/devtools/server/tests/browser/browser_inspector-retain.js
@@ -0,0 +1,158 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from inspector-helpers.js */
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/server/tests/browser/inspector-helpers.js",
+ this
+);
+
+// Retain a node, and a second-order child (in another document, for kicks)
+// Release the parent of the top item, which should cause one retained orphan.
+
+// Then unretain the top node, which should retain the orphan.
+
+// Then change the source of the iframe, which should kill that orphan.
+
+add_task(async function testRetain() {
+ // The test does not make sense when EFT is enabled, as different documents will have
+ // different walkers.
+ if (isEveryFrameTargetEnabled()) {
+ return;
+ }
+
+ const { walker } = await initInspectorFront(
+ MAIN_DOMAIN + "inspector-traversal-data.html"
+ );
+
+ // Get the toplevel body element and retain it.
+ const bodyFront = await walker.querySelector(walker.rootNode, "body");
+ await walker.retainNode(bodyFront);
+ // Get an element in the child frame and retain it.
+ const frame = await walker.querySelector(walker.rootNode, "#childFrame");
+ const children = await walker.children(frame, { maxNodes: 1 });
+ const childDoc = children.nodes[0];
+ const childListFront = await walker.querySelector(childDoc, "#longlist");
+ const originalOwnershipSize = await assertOwnershipTrees(walker);
+ // and retain it.
+ await walker.retainNode(childListFront);
+ // OK, try releasing the parent of the first retained.
+ await walker.releaseNode(bodyFront.parentNode());
+ const clientTree = clientOwnershipTree(walker);
+
+ // That request should have freed the parent of the first retained
+ // but moved the rest into the retained orphaned tree.
+ is(
+ ownershipTreeSize(clientTree.root) +
+ ownershipTreeSize(clientTree.retained[0]) +
+ 1,
+ originalOwnershipSize,
+ "Should have only lost one item overall."
+ );
+ is(walker._retainedOrphans.size, 1, "Should have retained one orphan");
+ ok(
+ walker._retainedOrphans.has(bodyFront),
+ "Should have retained the expected node."
+ );
+ // Unretain the body, which should promote the childListFront to a retained orphan.
+ await walker.unretainNode(bodyFront);
+ await assertOwnershipTrees(walker);
+
+ is(
+ walker._retainedOrphans.size,
+ 1,
+ "Should still only have one retained orphan."
+ );
+ ok(
+ !walker._retainedOrphans.has(bodyFront),
+ "Should have dropped the body node."
+ );
+ ok(
+ walker._retainedOrphans.has(childListFront),
+ "Should have retained the child node."
+ );
+
+ // Change the source of the iframe, which should kill the retained orphan.
+ const onMutations = waitForMutation(walker, isUnretained);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+ content.document.querySelector("#childFrame").src =
+ "data:text/html,<html>new child</html>";
+ });
+ await onMutations;
+
+ await assertOwnershipTrees(walker);
+ is(walker._retainedOrphans.size, 0, "Should have no more retained orphans.");
+});
+
+// Get a hold of a node, remove it from the doc and retain it at the same time.
+// We should always win that race (even though the mutation happens before the
+// retain request), because we haven't issued `getMutations` yet.
+add_task(async function testWinRace() {
+ const { walker } = await initInspectorFront(
+ MAIN_DOMAIN + "inspector-traversal-data.html"
+ );
+
+ const front = await walker.querySelector(walker.rootNode, "#a");
+ const onMutation = waitForMutation(walker, isChildList);
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+ const contentNode = content.document.querySelector("#a");
+ contentNode.remove();
+ });
+ // Now wait for that mutation and retain response to come in.
+ await walker.retainNode(front);
+ await onMutation;
+
+ await assertOwnershipTrees(walker);
+ is(walker._retainedOrphans.size, 1, "Should have a retained orphan.");
+ ok(
+ walker._retainedOrphans.has(front),
+ "Should have retained our expected node."
+ );
+ await walker.unretainNode(front);
+
+ // Make sure we're clear for the next test.
+ await assertOwnershipTrees(walker);
+ is(walker._retainedOrphans.size, 0, "Should have no more retained orphans.");
+});
+
+// Same as above, but issue the request right after the 'new-mutations' event, so that
+// we *lose* the race.
+add_task(async function testLoseRace() {
+ const { walker } = await initInspectorFront(
+ MAIN_DOMAIN + "inspector-traversal-data.html"
+ );
+
+ const front = await walker.querySelector(walker.rootNode, "#z");
+ const onMutation = walker.once("new-mutations");
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+ const contentNode = content.document.querySelector("#z");
+ contentNode.remove();
+ });
+ await onMutation;
+
+ // Verify that we have an outstanding request (no good way to tell that it's a
+ // getMutations request, but there's nothing else it would be).
+ is(walker._requests.length, 1, "Should have an outstanding request.");
+ try {
+ await walker.retainNode(front);
+ ok(false, "Request should not have succeeded!");
+ } catch (err) {
+ // XXX: Switched to from ok() to todo_is() in Bug 1467712. Follow up in
+ // 1500960
+ // This is throwing because of
+ // `gInspectee.querySelector("#z").parentNode = null;` two blocks above...
+ // Even if you fix that, the test is still failing because "#a" was removed
+ // by the previous test. I am switching this to "#z" because I think that
+ // was the original intent. Still not failing with the expected error message
+ // Needs more work.
+ // ok(err, "noSuchActor", "Should have lost the race.");
+ is(
+ walker._retainedOrphans.size,
+ 0,
+ "Should have no more retained orphans."
+ );
+ // Don't re-throw the error.
+ }
+});