browser_toolbox_tool_remote_reopen.js (3282B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { 7 DevToolsServer, 8 } = require("resource://devtools/server/devtools-server.js"); 9 10 // Bug 1277805: Too slow for debug runs 11 requestLongerTimeout(2); 12 13 /** 14 * Bug 979536: Ensure fronts are destroyed after toolbox close. 15 * 16 * The fronts need to be destroyed manually to unbind their onPacket handlers. 17 * 18 * When you initialize a front and call |this.manage|, it adds a client actor 19 * pool that the DevToolsClient uses to route packet replies to that actor. 20 * 21 * Most (all?) tools create a new front when they are opened. When the destroy 22 * step is skipped and the tool is reopened, a second front is created and also 23 * added to the client actor pool. When a packet reply is received, is ends up 24 * being routed to the first (now unwanted) front that is still in the client 25 * actor pool. Since this is not the same front that was used to make the 26 * request, an error occurs. 27 * 28 * This problem does not occur with the toolbox for a local tab because the 29 * toolbox target creates its own DevToolsClient for the local tab, and the 30 * client is destroyed when the toolbox is closed, which removes the client 31 * actor pools, and avoids this issue. 32 * 33 * In remote debugging, we do not destroy the DevToolsClient on toolbox close 34 * because it can still used for other targets. 35 * Thus, the same client gets reused across multiple toolboxes, 36 * which leads to the tools failing if they don't destroy their fronts. 37 */ 38 39 function runTools(tab) { 40 return (async function () { 41 let toolbox; 42 const toolIds = await getSupportedToolIds(tab); 43 for (const toolId of toolIds) { 44 info("About to open " + toolId); 45 toolbox = await gDevTools.showToolboxForTab(tab, { 46 toolId, 47 hostType: "window", 48 }); 49 ok(toolbox, "toolbox exists for " + toolId); 50 is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId); 51 52 const panel = toolbox.getCurrentPanel(); 53 ok(panel, toolId + " panel has been registered in the toolbox"); 54 } 55 56 const client = toolbox.commands.client; 57 await toolbox.destroy(); 58 59 // We need to check the client after the toolbox destruction. 60 return client; 61 })(); 62 } 63 64 function test() { 65 (async function () { 66 toggleAllTools(true); 67 const tab = await addTab("about:blank"); 68 69 const client = await runTools(tab); 70 71 const rootFronts = [...client.mainRoot.fronts.values()]; 72 73 // Actor fronts should be destroyed now that the toolbox has closed, but 74 // look for any that remain. 75 for (const pool of client.__pools) { 76 if (!pool.__poolMap) { 77 continue; 78 } 79 80 // Ignore the root fronts, which are top-level pools and aren't released 81 // on toolbox destroy, but on client close. 82 if (rootFronts.includes(pool)) { 83 continue; 84 } 85 86 for (const actor of pool.__poolMap.keys()) { 87 // Ignore the root front as it is only release on client close 88 if (actor == "root") { 89 continue; 90 } 91 ok(false, "Front for " + actor + " still held in pool!"); 92 } 93 } 94 95 gBrowser.removeCurrentTab(); 96 DevToolsServer.destroy(); 97 toggleAllTools(false); 98 finish(); 99 })(); 100 }