browser_inspector-traversal.js (12444B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 Services.scriptloader.loadSubScript( 7 "chrome://mochitests/content/browser/devtools/server/tests/browser/inspector-helpers.js", 8 this 9 ); 10 11 const checkActorIDs = []; 12 13 add_task(async function loadNewChild() { 14 const { walker } = await initInspectorFront( 15 MAIN_DOMAIN + "inspector-traversal-data.html" 16 ); 17 18 // Make sure that refetching the root document of the walker returns the same 19 // actor as the getWalker returned. 20 const root = await walker.document(); 21 Assert.strictEqual( 22 root, 23 walker.rootNode, 24 "Re-fetching the document node should match the root document node." 25 ); 26 checkActorIDs.push(root.actorID); 27 await assertOwnershipTrees(walker); 28 }); 29 30 add_task(async function testInnerHTML() { 31 const { walker } = await initInspectorFront( 32 MAIN_DOMAIN + "inspector-traversal-data.html" 33 ); 34 35 const docElement = await walker.documentElement(); 36 const longstring = await walker.innerHTML(docElement); 37 const innerHTML = await longstring.string(); 38 const actualInnerHTML = await SpecialPowers.spawn( 39 gBrowser.selectedBrowser, 40 [], 41 function () { 42 return content.document.documentElement.innerHTML; 43 } 44 ); 45 Assert.strictEqual(innerHTML, actualInnerHTML, "innerHTML should match"); 46 }); 47 48 add_task(async function testOuterHTML() { 49 const { walker } = await initInspectorFront( 50 MAIN_DOMAIN + "inspector-traversal-data.html" 51 ); 52 53 const docElement = await walker.documentElement(); 54 const longstring = await walker.outerHTML(docElement); 55 const outerHTML = await longstring.string(); 56 const actualOuterHTML = await SpecialPowers.spawn( 57 gBrowser.selectedBrowser, 58 [], 59 function () { 60 return content.document.documentElement.outerHTML; 61 } 62 ); 63 Assert.strictEqual(outerHTML, actualOuterHTML, "outerHTML should match"); 64 }); 65 66 add_task(async function testSetOuterHTMLNode() { 67 const { walker } = await initInspectorFront( 68 MAIN_DOMAIN + "inspector-traversal-data.html" 69 ); 70 const newHTML = '<p id="edit-html-done">after edit</p>'; 71 let node = await walker.querySelector(walker.rootNode, "#edit-html"); 72 await walker.setOuterHTML(node, newHTML); 73 node = await walker.querySelector(walker.rootNode, "#edit-html-done"); 74 const longstring = await walker.outerHTML(node); 75 const outerHTML = await longstring.string(); 76 is(outerHTML, newHTML, "outerHTML has been updated"); 77 node = await walker.querySelector(walker.rootNode, "#edit-html"); 78 ok(!node, "The node with the old ID cannot be selected anymore"); 79 }); 80 81 add_task(async function testQuerySelector() { 82 const { walker } = await initInspectorFront( 83 MAIN_DOMAIN + "inspector-traversal-data.html" 84 ); 85 let node = await walker.querySelector(walker.rootNode, "#longlist"); 86 is( 87 node.getAttribute("data-test"), 88 "exists", 89 "should have found the right node" 90 ); 91 await assertOwnershipTrees(walker); 92 node = await walker.querySelector(walker.rootNode, "unknownqueryselector"); 93 ok(!node, "Should not find a node here."); 94 await assertOwnershipTrees(walker); 95 }); 96 97 add_task(async function testQuerySelectors() { 98 const { target, walker } = await initInspectorFront( 99 MAIN_DOMAIN + "inspector-traversal-data.html" 100 ); 101 const nodeList = await walker.querySelectorAll( 102 walker.rootNode, 103 "#longlist div" 104 ); 105 is(nodeList.length, 26, "Expect 26 div children."); 106 await assertOwnershipTrees(walker); 107 const firstNode = await nodeList.item(0); 108 checkActorIDs.push(firstNode.actorID); 109 is(firstNode.id, "a", "First child should be a"); 110 await assertOwnershipTrees(walker); 111 let nodes = await nodeList.items(); 112 is(nodes.length, 26, "Expect 26 nodes"); 113 is(nodes[0], firstNode, "First node should be reused."); 114 ok(nodes[0]._parent, "Parent node should be set."); 115 ok(nodes[0]._next || nodes[0]._prev, "Siblings should be set."); 116 ok( 117 nodes[25]._next || nodes[25]._prev, 118 "Siblings of " + nodes[25] + " should be set." 119 ); 120 await assertOwnershipTrees(walker); 121 nodes = await nodeList.items(-1); 122 is(nodes.length, 1, "Expect 1 node"); 123 is(nodes[0].id, "z", "Expect it to be the last node."); 124 checkActorIDs.push(nodes[0].actorID); 125 // Save the node list ID so we can ensure it was destroyed. 126 const nodeListID = nodeList.actorID; 127 await assertOwnershipTrees(walker); 128 await nodeList.release(); 129 ok(!nodeList.actorID, "Actor should have been destroyed."); 130 await assertOwnershipTrees(walker); 131 await checkMissing(target, nodeListID); 132 }); 133 134 // Helper to check the response of requests that return hasFirst/hasLast/nodes 135 // node lists (like `children` and `siblings`) 136 async function checkArray(walker, children, first, last, ids) { 137 is( 138 children.hasFirst, 139 first, 140 "Should " + (first ? "" : "not ") + " have the first node." 141 ); 142 is( 143 children.hasLast, 144 last, 145 "Should " + (last ? "" : "not ") + " have the last node." 146 ); 147 is( 148 children.nodes.length, 149 ids.length, 150 "Should have " + ids.length + " children listed." 151 ); 152 let responseIds = ""; 153 for (const node of children.nodes) { 154 responseIds += node.id; 155 } 156 is(responseIds, ids, "Correct nodes were returned."); 157 await assertOwnershipTrees(walker); 158 } 159 160 add_task(async function testNoChildren() { 161 const { walker } = await initInspectorFront( 162 MAIN_DOMAIN + "inspector-traversal-data.html" 163 ); 164 const empty = await walker.querySelector(walker.rootNode, "#empty"); 165 await assertOwnershipTrees(walker); 166 const children = await walker.children(empty); 167 await checkArray(walker, children, true, true, ""); 168 }); 169 170 add_task(async function testLongListTraversal() { 171 const { walker } = await initInspectorFront( 172 MAIN_DOMAIN + "inspector-traversal-data.html" 173 ); 174 const longList = await walker.querySelector(walker.rootNode, "#longlist"); 175 // First call with no options, expect all children. 176 await assertOwnershipTrees(walker); 177 let children = await walker.children(longList); 178 await checkArray(walker, children, true, true, "abcdefghijklmnopqrstuvwxyz"); 179 const allChildren = children.nodes; 180 await assertOwnershipTrees(walker); 181 // maxNodes should limit us to the first 5 nodes. 182 await assertOwnershipTrees(walker); 183 children = await walker.children(longList, { maxNodes: 5 }); 184 await checkArray(walker, children, true, false, "abcde"); 185 await assertOwnershipTrees(walker); 186 // maxNodes with the second item centered should still give us the first 5 nodes. 187 children = await walker.children(longList, { 188 maxNodes: 5, 189 center: allChildren[1], 190 }); 191 await checkArray(walker, children, true, false, "abcde"); 192 // maxNodes with a center in the middle of the list should put that item in the middle 193 const center = allChildren[13]; 194 is(center.id, "n", "Make sure I know how to count letters."); 195 children = await walker.children(longList, { maxNodes: 5, center }); 196 await checkArray(walker, children, false, false, "lmnop"); 197 // maxNodes with the second-to-last item centered should give us the last 5 nodes. 198 children = await walker.children(longList, { 199 maxNodes: 5, 200 center: allChildren[24], 201 }); 202 await checkArray(walker, children, false, true, "vwxyz"); 203 // maxNodes with a start in the middle should start at that node and fetch 5 204 const start = allChildren[13]; 205 is(start.id, "n", "Make sure I know how to count letters."); 206 children = await walker.children(longList, { maxNodes: 5, start }); 207 await checkArray(walker, children, false, false, "nopqr"); 208 // maxNodes near the end should only return what's left 209 children = await walker.children(longList, { 210 maxNodes: 5, 211 start: allChildren[24], 212 }); 213 await checkArray(walker, children, false, true, "yz"); 214 }); 215 216 add_task(async function testObjectNodeChildren() { 217 const { walker } = await initInspectorFront( 218 MAIN_DOMAIN + "inspector-traversal-data.html" 219 ); 220 const object = await walker.querySelector(walker.rootNode, "object"); 221 const children = await walker.children(object); 222 await checkArray(walker, children, true, true, "1"); 223 }); 224 225 add_task(async function testNextSibling() { 226 const { walker } = await initInspectorFront( 227 MAIN_DOMAIN + "inspector-traversal-data.html" 228 ); 229 const y = await walker.querySelector(walker.rootNode, "#y"); 230 is(y.id, "y", "Got the right node."); 231 const z = await walker.nextSibling(y); 232 is(z.id, "z", "nextSibling got the next node."); 233 const nothing = await walker.nextSibling(z); 234 is(nothing, null, "nextSibling on the last node returned null."); 235 }); 236 237 add_task(async function testPreviousSibling() { 238 const { walker } = await initInspectorFront( 239 MAIN_DOMAIN + "inspector-traversal-data.html" 240 ); 241 const b = await walker.querySelector(walker.rootNode, "#b"); 242 is(b.id, "b", "Got the right node."); 243 const a = await walker.previousSibling(b); 244 is(a.id, "a", "nextSibling got the next node."); 245 const nothing = await walker.previousSibling(a); 246 is(nothing, null, "previousSibling on the first node returned null."); 247 }); 248 249 add_task(async function testFrameTraversal() { 250 const { walker } = await initInspectorFront( 251 MAIN_DOMAIN + "inspector-traversal-data.html" 252 ); 253 const childFrame = await walker.querySelector(walker.rootNode, "#childFrame"); 254 const children = await walker.children(childFrame); 255 const nodes = children.nodes; 256 is(nodes.length, 1, "There should be only one child of the iframe"); 257 is( 258 nodes[0].nodeType, 259 Node.DOCUMENT_NODE, 260 "iframe child should be a document node" 261 ); 262 await walker.querySelector(nodes[0], "#z"); 263 }); 264 265 add_task(async function testLongValue() { 266 const { walker } = await initInspectorFront( 267 MAIN_DOMAIN + "inspector-traversal-data.html" 268 ); 269 270 SimpleTest.registerCleanupFunction(async function () { 271 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { 272 const { require } = ChromeUtils.importESModule( 273 "resource://devtools/shared/loader/Loader.sys.mjs" 274 ); 275 const WalkerActor = require("resource://devtools/server/actors/inspector/walker.js"); 276 WalkerActor.setValueSummaryLength( 277 WalkerActor.DEFAULT_VALUE_SUMMARY_LENGTH 278 ); 279 }); 280 }); 281 282 const longstringText = await SpecialPowers.spawn( 283 gBrowser.selectedBrowser, 284 [], 285 function () { 286 const { require } = ChromeUtils.importESModule( 287 "resource://devtools/shared/loader/Loader.sys.mjs" 288 ); 289 const testSummaryLength = 10; 290 const WalkerActor = require("resource://devtools/server/actors/inspector/walker.js"); 291 292 WalkerActor.setValueSummaryLength(testSummaryLength); 293 return content.document.getElementById("longstring").firstChild.nodeValue; 294 } 295 ); 296 297 const node = await walker.querySelector(walker.rootNode, "#longstring"); 298 ok(!node.inlineTextChild, "Text is too long to be inlined"); 299 // Now we need to get the text node child... 300 const children = await walker.children(node, { maxNodes: 1 }); 301 const textNode = children.nodes[0]; 302 is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); 303 const value = await textNode.getNodeValue(); 304 const valueStr = await value.string(); 305 is( 306 valueStr, 307 longstringText, 308 "Full node value should match the string from the document." 309 ); 310 }); 311 312 add_task(async function testShortValue() { 313 const { walker } = await initInspectorFront( 314 MAIN_DOMAIN + "inspector-traversal-data.html" 315 ); 316 const shortstringText = await SpecialPowers.spawn( 317 gBrowser.selectedBrowser, 318 [], 319 function () { 320 return content.document.getElementById("shortstring").firstChild 321 .nodeValue; 322 } 323 ); 324 325 const node = await walker.querySelector(walker.rootNode, "#shortstring"); 326 ok(!!node.inlineTextChild, "Text is short enough to be inlined"); 327 // Now we need to get the text node child... 328 const children = await walker.children(node, { maxNodes: 1 }); 329 const textNode = children.nodes[0]; 330 is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); 331 const value = await textNode.getNodeValue(); 332 const valueStr = await value.string(); 333 is( 334 valueStr, 335 shortstringText, 336 "Full node value should match the string from the document." 337 ); 338 }); 339 340 add_task(async function testReleaseWalker() { 341 const { target, walker } = await initInspectorFront( 342 MAIN_DOMAIN + "inspector-traversal-data.html" 343 ); 344 checkActorIDs.push(walker.actorID); 345 346 await walker.release(); 347 for (const id of checkActorIDs) { 348 await checkMissing(target, id); 349 } 350 });