/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/server/tests/browser/inspector-helpers.js", this ); const checkActorIDs = []; add_task(async function loadNewChild() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); // Make sure that refetching the root document of the walker returns the same // actor as the getWalker returned. const root = await walker.document(); ok( root === walker.rootNode, "Re-fetching the document node should match the root document node." ); checkActorIDs.push(root.actorID); await assertOwnershipTrees(walker); }); add_task(async function testInnerHTML() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const docElement = await walker.documentElement(); const longstring = await walker.innerHTML(docElement); const innerHTML = await longstring.string(); const actualInnerHTML = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], function () { return content.document.documentElement.innerHTML; } ); ok(innerHTML === actualInnerHTML, "innerHTML should match"); }); add_task(async function testOuterHTML() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const docElement = await walker.documentElement(); const longstring = await walker.outerHTML(docElement); const outerHTML = await longstring.string(); const actualOuterHTML = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], function () { return content.document.documentElement.outerHTML; } ); ok(outerHTML === actualOuterHTML, "outerHTML should match"); }); add_task(async function testSetOuterHTMLNode() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const newHTML = '
after edit
'; let node = await walker.querySelector(walker.rootNode, "#edit-html"); await walker.setOuterHTML(node, newHTML); node = await walker.querySelector(walker.rootNode, "#edit-html-done"); const longstring = await walker.outerHTML(node); const outerHTML = await longstring.string(); is(outerHTML, newHTML, "outerHTML has been updated"); node = await walker.querySelector(walker.rootNode, "#edit-html"); ok(!node, "The node with the old ID cannot be selected anymore"); }); add_task(async function testQuerySelector() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); let node = await walker.querySelector(walker.rootNode, "#longlist"); is( node.getAttribute("data-test"), "exists", "should have found the right node" ); await assertOwnershipTrees(walker); node = await walker.querySelector(walker.rootNode, "unknownqueryselector"); ok(!node, "Should not find a node here."); await assertOwnershipTrees(walker); }); add_task(async function testQuerySelectors() { const { target, walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const nodeList = await walker.querySelectorAll( walker.rootNode, "#longlist div" ); is(nodeList.length, 26, "Expect 26 div children."); await assertOwnershipTrees(walker); const firstNode = await nodeList.item(0); checkActorIDs.push(firstNode.actorID); is(firstNode.id, "a", "First child should be a"); await assertOwnershipTrees(walker); let nodes = await nodeList.items(); is(nodes.length, 26, "Expect 26 nodes"); is(nodes[0], firstNode, "First node should be reused."); ok(nodes[0]._parent, "Parent node should be set."); ok(nodes[0]._next || nodes[0]._prev, "Siblings should be set."); ok( nodes[25]._next || nodes[25]._prev, "Siblings of " + nodes[25] + " should be set." ); await assertOwnershipTrees(walker); nodes = await nodeList.items(-1); is(nodes.length, 1, "Expect 1 node"); is(nodes[0].id, "z", "Expect it to be the last node."); checkActorIDs.push(nodes[0].actorID); // Save the node list ID so we can ensure it was destroyed. const nodeListID = nodeList.actorID; await assertOwnershipTrees(walker); await nodeList.release(); ok(!nodeList.actorID, "Actor should have been destroyed."); await assertOwnershipTrees(walker); await checkMissing(target, nodeListID); }); // Helper to check the response of requests that return hasFirst/hasLast/nodes // node lists (like `children` and `siblings`) async function checkArray(walker, children, first, last, ids) { is( children.hasFirst, first, "Should " + (first ? "" : "not ") + " have the first node." ); is( children.hasLast, last, "Should " + (last ? "" : "not ") + " have the last node." ); is( children.nodes.length, ids.length, "Should have " + ids.length + " children listed." ); let responseIds = ""; for (const node of children.nodes) { responseIds += node.id; } is(responseIds, ids, "Correct nodes were returned."); await assertOwnershipTrees(walker); } add_task(async function testNoChildren() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const empty = await walker.querySelector(walker.rootNode, "#empty"); await assertOwnershipTrees(walker); const children = await walker.children(empty); await checkArray(walker, children, true, true, ""); }); add_task(async function testLongListTraversal() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const longList = await walker.querySelector(walker.rootNode, "#longlist"); // First call with no options, expect all children. await assertOwnershipTrees(walker); let children = await walker.children(longList); await checkArray(walker, children, true, true, "abcdefghijklmnopqrstuvwxyz"); const allChildren = children.nodes; await assertOwnershipTrees(walker); // maxNodes should limit us to the first 5 nodes. await assertOwnershipTrees(walker); children = await walker.children(longList, { maxNodes: 5 }); await checkArray(walker, children, true, false, "abcde"); await assertOwnershipTrees(walker); // maxNodes with the second item centered should still give us the first 5 nodes. children = await walker.children(longList, { maxNodes: 5, center: allChildren[1], }); await checkArray(walker, children, true, false, "abcde"); // maxNodes with a center in the middle of the list should put that item in the middle const center = allChildren[13]; is(center.id, "n", "Make sure I know how to count letters."); children = await walker.children(longList, { maxNodes: 5, center }); await checkArray(walker, children, false, false, "lmnop"); // maxNodes with the second-to-last item centered should give us the last 5 nodes. children = await walker.children(longList, { maxNodes: 5, center: allChildren[24], }); await checkArray(walker, children, false, true, "vwxyz"); // maxNodes with a start in the middle should start at that node and fetch 5 const start = allChildren[13]; is(start.id, "n", "Make sure I know how to count letters."); children = await walker.children(longList, { maxNodes: 5, start }); await checkArray(walker, children, false, false, "nopqr"); // maxNodes near the end should only return what's left children = await walker.children(longList, { maxNodes: 5, start: allChildren[24], }); await checkArray(walker, children, false, true, "yz"); }); add_task(async function testObjectNodeChildren() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const object = await walker.querySelector(walker.rootNode, "object"); const children = await walker.children(object); await checkArray(walker, children, true, true, "1"); }); add_task(async function testNextSibling() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const y = await walker.querySelector(walker.rootNode, "#y"); is(y.id, "y", "Got the right node."); const z = await walker.nextSibling(y); is(z.id, "z", "nextSibling got the next node."); const nothing = await walker.nextSibling(z); is(nothing, null, "nextSibling on the last node returned null."); }); add_task(async function testPreviousSibling() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const b = await walker.querySelector(walker.rootNode, "#b"); is(b.id, "b", "Got the right node."); const a = await walker.previousSibling(b); is(a.id, "a", "nextSibling got the next node."); const nothing = await walker.previousSibling(a); is(nothing, null, "previousSibling on the first node returned null."); }); add_task(async function testFrameTraversal() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const childFrame = await walker.querySelector(walker.rootNode, "#childFrame"); const children = await walker.children(childFrame); const nodes = children.nodes; is(nodes.length, 1, "There should be only one child of the iframe"); is( nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node" ); await walker.querySelector(nodes[0], "#z"); }); add_task(async function testLongValue() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); SimpleTest.registerCleanupFunction(async function () { await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { const { require } = ChromeUtils.importESModule( "resource://devtools/shared/loader/Loader.sys.mjs" ); const WalkerActor = require("resource://devtools/server/actors/inspector/walker.js"); WalkerActor.setValueSummaryLength( WalkerActor.DEFAULT_VALUE_SUMMARY_LENGTH ); }); }); const longstringText = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], function () { const { require } = ChromeUtils.importESModule( "resource://devtools/shared/loader/Loader.sys.mjs" ); const testSummaryLength = 10; const WalkerActor = require("resource://devtools/server/actors/inspector/walker.js"); WalkerActor.setValueSummaryLength(testSummaryLength); return content.document.getElementById("longstring").firstChild.nodeValue; } ); const node = await walker.querySelector(walker.rootNode, "#longstring"); ok(!node.inlineTextChild, "Text is too long to be inlined"); // Now we need to get the text node child... const children = await walker.children(node, { maxNodes: 1 }); const textNode = children.nodes[0]; is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); const value = await textNode.getNodeValue(); const valueStr = await value.string(); is( valueStr, longstringText, "Full node value should match the string from the document." ); }); add_task(async function testShortValue() { const { walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); const shortstringText = await SpecialPowers.spawn( gBrowser.selectedBrowser, [], function () { return content.document.getElementById("shortstring").firstChild .nodeValue; } ); const node = await walker.querySelector(walker.rootNode, "#shortstring"); ok(!!node.inlineTextChild, "Text is short enough to be inlined"); // Now we need to get the text node child... const children = await walker.children(node, { maxNodes: 1 }); const textNode = children.nodes[0]; is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); const value = await textNode.getNodeValue(); const valueStr = await value.string(); is( valueStr, shortstringText, "Full node value should match the string from the document." ); }); add_task(async function testReleaseWalker() { const { target, walker } = await initInspectorFront( MAIN_DOMAIN + "inspector-traversal-data.html" ); checkActorIDs.push(walker.actorID); await walker.release(); for (const id of checkActorIDs) { await checkMissing(target, id); } });