Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / devtools / server / startup / frame.js
blobaa9cb790b363cb983941345d17ed30b6b59e05f3
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* eslint-env mozilla/frame-script */
7 "use strict";
9 /* global addEventListener */
12 * Frame script that listens for requests to start a `DevToolsServer` for a frame in a
13 * content process. Loaded into content process frames by the main process during
14 * frame-connector.js' connectToFrame.
17 try {
18 var chromeGlobal = this;
20 // Encapsulate in its own scope to allows loading this frame script more than once.
21 (function () {
22 // In most cases, we are debugging a tab in content process, without chrome
23 // privileges. But in some tests, we are attaching to privileged document.
24 // Because the debugger can't be running in the same compartment than its debuggee,
25 // we have to load the server in a dedicated Loader, flagged with
26 // invisibleToDebugger, which will force it to be loaded in another compartment.
27 let loader,
28 customLoader = false;
29 if (content.document.nodePrincipal.isSystemPrincipal) {
30 const { useDistinctSystemPrincipalLoader } = ChromeUtils.importESModule(
31 "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
32 { global: "shared" }
34 loader = useDistinctSystemPrincipalLoader(chromeGlobal);
35 customLoader = true;
36 } else {
37 // Otherwise, use the shared loader.
38 loader = ChromeUtils.importESModule(
39 "resource://devtools/shared/loader/Loader.sys.mjs"
42 const { require } = loader;
44 const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
45 const {
46 DevToolsServer,
47 } = require("resource://devtools/server/devtools-server.js");
49 DevToolsServer.init();
50 // We want a special server without any root actor and only target-scoped actors.
51 // We are going to spawn a WindowGlobalTargetActor instance in the next few lines,
52 // it is going to act like a root actor without being one.
53 DevToolsServer.registerActors({ target: true });
55 const connections = new Map();
57 const onConnect = DevToolsUtils.makeInfallible(function (msg) {
58 const mm = msg.target;
59 const prefix = msg.data.prefix;
61 // If we try to create several frame targets simultaneously, the frame script will be loaded several times.
62 // In this case a single "debug:connect" message might be received by all the already loaded frame scripts.
63 // Check if the DevToolsServer already knows the provided connection prefix,
64 // because it means that another framescript instance already handled this message.
65 // Another "debug:connect" message is guaranteed to be emitted for another prefix,
66 // so we keep the message listener and wait for this next message.
67 if (DevToolsServer.hasConnectionForPrefix(prefix)) {
68 return;
70 removeMessageListener("debug:connect", onConnect);
72 const conn = DevToolsServer.connectToParent(prefix, mm);
73 connections.set(prefix, conn);
75 const {
76 WindowGlobalTargetActor,
77 } = require("resource://devtools/server/actors/targets/window-global.js");
78 const {
79 createBrowserElementSessionContext,
80 } = require("resource://devtools/server/actors/watcher/session-context.js");
82 const { docShell } = chromeGlobal;
83 // For a script loaded via loadFrameScript, the global is the content
84 // message manager.
85 // All WindowGlobalTarget actors created via the framescript are top-level
86 // targets. Non top-level WindowGlobalTarget actors are all created by the
87 // DevToolsFrameChild actor.
89 // createBrowserElementSessionContext only reads browserId attribute
90 const fakeBrowserElement = {
91 browserId: docShell.browsingContext.browserId,
93 const actor = new WindowGlobalTargetActor(conn, {
94 docShell,
95 isTopLevelTarget: true,
96 // This is only used when server target switching is off and we create
97 // the target from TabDescriptor. So all config attributes are false.
98 sessionContext: createBrowserElementSessionContext(
99 fakeBrowserElement,
103 actor.manage(actor);
105 sendAsyncMessage("debug:actor", { actor: actor.form(), prefix });
108 addMessageListener("debug:connect", onConnect);
110 const onDisconnect = DevToolsUtils.makeInfallible(function (msg) {
111 const prefix = msg.data.prefix;
112 const conn = connections.get(prefix);
113 if (!conn) {
114 // Several copies of this frame script can be running for a single frame since it
115 // is loaded once for each DevTools connection to the frame. If this disconnect
116 // request doesn't match a connection known here, ignore it.
117 return;
120 removeMessageListener("debug:disconnect", onDisconnect);
121 // Call DevToolsServerConnection.close to destroy all child actors. It should end up
122 // calling DevToolsServerConnection.onTransportClosed that would actually cleanup all actor
123 // pools.
124 conn.close();
125 connections.delete(prefix);
127 addMessageListener("debug:disconnect", onDisconnect);
129 // In non-e10s mode, the "debug:disconnect" message isn't always received before the
130 // messageManager connection goes away. Watching for "unload" here ensures we close
131 // any connections when the frame is unloaded.
132 addEventListener("unload", () => {
133 for (const conn of connections.values()) {
134 conn.close();
136 connections.clear();
139 // Destroy the server once its last connection closes. Note that multiple frame
140 // scripts may be running in parallel and reuse the same server.
141 function destroyLoader() {
142 // Only destroy the server if there is no more connections to it. It may be used
143 // to debug another tab running in the same process.
144 if (DevToolsServer.hasConnection() || DevToolsServer.keepAlive) {
145 return;
147 DevToolsServer.off("connectionchange", destroyLoader);
149 // When debugging chrome pages, we initialized a dedicated loader, also destroy it
150 if (customLoader) {
151 const { releaseDistinctSystemPrincipalLoader } =
152 ChromeUtils.importESModule(
153 "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
154 { global: "shared" }
156 releaseDistinctSystemPrincipalLoader(chromeGlobal);
159 DevToolsServer.on("connectionchange", destroyLoader);
160 })();
161 } catch (e) {
162 dump(`Exception in DevTools frame startup: ${e}\n`);