summaryrefslogtreecommitdiffstats
path: root/devtools/server/startup/content-process.jsm
blob: c54e8d2048d4755017f39621d9840e27f4267a8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 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";

/*
 * Module that listens for requests to start a `DevToolsServer` for an entire content
 * process.  Loaded into content processes by the main process during
 * content-process-connector.js' `connectToContentProcess` via the process
 * script `content-process.js`.
 *
 * The actual server startup itself is in this JSM so that code can be cached.
 */

/* exported initContentProcessTarget */
const EXPORTED_SYMBOLS = ["initContentProcessTarget"];

let gLoader;

function setupServer(mm) {
  // Prevent spawning multiple server per process, even if the caller call us
  // multiple times
  if (gLoader) {
    return gLoader;
  }

  // Lazy load Loader.jsm to prevent loading any devtools dependency too early.
  const { DevToolsLoader } = ChromeUtils.import(
    "resource://devtools/shared/Loader.jsm"
  );

  // Init a custom, invisible DevToolsServer, in order to not pollute the
  // debugger with all devtools modules, nor break the debugger itself with
  // using it in the same process.
  gLoader = new DevToolsLoader({
    invisibleToDebugger: true,
  });
  const { DevToolsServer } = gLoader.require("devtools/server/devtools-server");

  DevToolsServer.init();
  // For browser content toolbox, we do need a regular root actor and all tab
  // actors, but don't need all the "browser actors" that are only useful when
  // debugging the parent process via the browser toolbox.
  DevToolsServer.registerActors({ root: true, target: true });

  // 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 the same process from another client.
    if (DevToolsServer.hasConnection()) {
      return;
    }
    DevToolsServer.off("connectionchange", destroyServer);

    DevToolsServer.destroy();
    gLoader.destroy();
    gLoader = null;
  }
  DevToolsServer.on("connectionchange", destroyServer);

  return gLoader;
}

function initContentProcessTarget(msg) {
  const mm = msg.target;
  const prefix = msg.data.prefix;
  const watcherActorID = msg.data.watcherActorID;

  // Setup a server if none started yet
  const loader = setupServer(mm);

  // Connect both parent/child processes devtools servers RDP via message
  // managers
  const { DevToolsServer } = loader.require("devtools/server/devtools-server");
  const conn = DevToolsServer.connectToParent(prefix, mm);
  conn.parentMessageManager = mm;

  const { ContentProcessTargetActor } = loader.require(
    "devtools/server/actors/targets/content-process"
  );
  const actor = new ContentProcessTargetActor(conn);
  actor.manage(actor);

  const response = { watcherActorID, prefix, actor: actor.form() };
  mm.sendAsyncMessage("debug:content-process-actor", response);

  // Clean up things when the client disconnects
  mm.addMessageListener("debug:content-process-disconnect", function onDestroy(
    message
  ) {
    if (message.data.prefix != prefix) {
      // Several copies of this process script can be running for a single process if
      // we are debugging the same process from multiple clients.
      // If this disconnect request doesn't match a connection known here, ignore it.
      return;
    }
    mm.removeMessageListener("debug:content-process-disconnect", onDestroy);

    // Call DevToolsServerConnection.close to destroy all child actors. It should end up
    // calling DevToolsServerConnection.onClosed that would actually cleanup all actor
    // pools.
    conn.close();
  });
  return {
    actor,
    connection: conn,
  };
}