diff options
Diffstat (limited to '')
-rw-r--r-- | devtools/server/startup/frame.js | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/devtools/server/startup/frame.js b/devtools/server/startup/frame.js new file mode 100644 index 0000000000..ced47824bd --- /dev/null +++ b/devtools/server/startup/frame.js @@ -0,0 +1,141 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* global content, addEventListener, addMessageListener, removeMessageListener, + sendAsyncMessage */ + +/* + * Frame script that listens for requests to start a `DevToolsServer` for a frame in a + * content process. Loaded into content process frames by the main process during + * frame-connector.js' connectToFrame. + */ + +try { + var chromeGlobal = this; + + // Encapsulate in its own scope to allows loading this frame script more than once. + (function() { + // In most cases, we are debugging a tab in content process, without chrome + // privileges. But in some tests, we are attaching to privileged document. + // Because the debugger can't be running in the same compartment than its debuggee, + // we have to load the server in a dedicated Loader, flagged with + // invisibleToDebugger, which will force it to be loaded in another compartment. + let loader, + customLoader = false; + if (content.document.nodePrincipal.isSystemPrincipal) { + const { DevToolsLoader } = ChromeUtils.import( + "resource://devtools/shared/Loader.jsm" + ); + loader = new DevToolsLoader({ + invisibleToDebugger: true, + }); + customLoader = true; + } else { + // Otherwise, use the shared loader. + loader = ChromeUtils.import("resource://devtools/shared/Loader.jsm"); + } + const { require } = loader; + + const DevToolsUtils = require("devtools/shared/DevToolsUtils"); + const { DevToolsServer } = require("devtools/server/devtools-server"); + + DevToolsServer.init(); + // We want a special server without any root actor and only target-scoped actors. + // We are going to spawn a FrameTargetActor instance in the next few lines, + // it is going to act like a root actor without being one. + DevToolsServer.registerActors({ target: true }); + + const connections = new Map(); + + const onConnect = DevToolsUtils.makeInfallible(function(msg) { + removeMessageListener("debug:connect", onConnect); + + const mm = msg.target; + const prefix = msg.data.prefix; + const addonId = msg.data.addonId; + + const conn = DevToolsServer.connectToParent(prefix, mm); + conn.parentMessageManager = mm; + connections.set(prefix, conn); + + let actor; + + if (addonId) { + const { + WebExtensionTargetActor, + } = require("devtools/server/actors/targets/webextension"); + actor = new WebExtensionTargetActor( + conn, + chromeGlobal, + prefix, + addonId + ); + } else { + const { + FrameTargetActor, + } = require("devtools/server/actors/targets/frame"); + const { docShell } = chromeGlobal; + // For a script loaded via loadFrameScript, the global is the content + // message manager. + actor = new FrameTargetActor(conn, docShell); + } + actor.manage(actor); + + sendAsyncMessage("debug:actor", { actor: actor.form(), prefix: prefix }); + }); + + addMessageListener("debug:connect", onConnect); + + const onDisconnect = DevToolsUtils.makeInfallible(function(msg) { + const prefix = msg.data.prefix; + const conn = connections.get(prefix); + if (!conn) { + // Several copies of this frame script can be running for a single frame since it + // is loaded once for each DevTools connection to the frame. If this disconnect + // request doesn't match a connection known here, ignore it. + return; + } + + removeMessageListener("debug:disconnect", onDisconnect); + // Call DevToolsServerConnection.close to destroy all child actors. It should end up + // calling DevToolsServerConnection.onClosed that would actually cleanup all actor + // pools. + conn.close(); + connections.delete(prefix); + }); + addMessageListener("debug:disconnect", onDisconnect); + + // In non-e10s mode, the "debug:disconnect" message isn't always received before the + // messageManager connection goes away. Watching for "unload" here ensures we close + // any connections when the frame is unloaded. + addEventListener("unload", () => { + for (const conn of connections.values()) { + conn.close(); + } + connections.clear(); + }); + + // Destroy the server once its last connection closes. Note that multiple frame + // scripts may be running in parallel and reuse the same server. + function destroyServer() { + // Only destroy the server if there is no more connections to it. It may be used + // to debug another tab running in the same process. + if (DevToolsServer.hasConnection() || DevToolsServer.keepAlive) { + return; + } + DevToolsServer.off("connectionchange", destroyServer); + DevToolsServer.destroy(); + + // When debugging chrome pages, we initialized a dedicated loader, also destroy it + if (customLoader) { + loader.destroy(); + } + } + DevToolsServer.on("connectionchange", destroyServer); + })(); +} catch (e) { + dump(`Exception in DevTools frame startup: ${e}\n`); +} |