summaryrefslogtreecommitdiffstats
path: root/devtools/server/startup/worker.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/startup/worker.js')
-rw-r--r--devtools/server/startup/worker.js159
1 files changed, 159 insertions, 0 deletions
diff --git a/devtools/server/startup/worker.js b/devtools/server/startup/worker.js
new file mode 100644
index 0000000000..42034831ee
--- /dev/null
+++ b/devtools/server/startup/worker.js
@@ -0,0 +1,159 @@
+/* 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 worker, loadSubScript, global */
+
+/*
+ * Worker debugger script that listens for requests to start a `DevToolsServer` for a
+ * worker in a process. Loaded into a specific worker during worker-connector.js'
+ * `connectToWorker` which is called from the same process as the worker.
+ */
+
+// This function is used to do remote procedure calls from the worker to the
+// main thread. It is exposed as a built-in global to every module by the
+// worker loader. To make sure the worker loader can access it, it needs to be
+// defined before loading the worker loader script below.
+let nextId = 0;
+this.rpc = function (method, ...params) {
+ return new Promise((resolve, reject) => {
+ const id = nextId++;
+ this.addEventListener("message", function onMessageForRpc(event) {
+ const packet = JSON.parse(event.data);
+ if (packet.type !== "rpc" || packet.id !== id) {
+ return;
+ }
+ if (packet.error) {
+ reject(packet.error);
+ } else {
+ resolve(packet.result);
+ }
+ this.removeEventListener("message", onMessageForRpc);
+ });
+
+ postMessage(
+ JSON.stringify({
+ type: "rpc",
+ method,
+ params,
+ id,
+ })
+ );
+ });
+}.bind(this);
+
+loadSubScript("resource://devtools/shared/loader/worker-loader.js");
+
+const { WorkerTargetActor } = worker.require(
+ "resource://devtools/server/actors/targets/worker.js"
+);
+const { DevToolsServer } = worker.require(
+ "resource://devtools/server/devtools-server.js"
+);
+
+DevToolsServer.createRootActor = function () {
+ throw new Error("Should never get here!");
+};
+
+// This file is only instanciated once for a given WorkerDebugger, which means that
+// multiple toolbox could end up using the same instance of this script. In order to handle
+// that, we handle a Map of the different connections, keyed by forwarding prefix.
+const connections = new Map();
+
+this.addEventListener("message", async function (event) {
+ const packet = JSON.parse(event.data);
+ switch (packet.type) {
+ case "connect":
+ const { forwardingPrefix } = packet;
+
+ // Force initializing the server each time on connect
+ // as it may have been destroyed by a previous, now closed toolbox.
+ // Once the last connection drops, the server auto destroy itself.
+ DevToolsServer.init();
+
+ // Step 3: Create a connection to the parent.
+ const connection = DevToolsServer.connectToParent(forwardingPrefix, this);
+
+ // Step 4: Create a WorkerTarget actor.
+ const workerTargetActor = new WorkerTargetActor(
+ connection,
+ global,
+ packet.workerDebuggerData,
+ packet.options.sessionContext
+ );
+ // Make the worker manage itself so it is put in a Pool and assigned an actorID.
+ workerTargetActor.manage(workerTargetActor);
+
+ workerTargetActor.on(
+ "worker-thread-attached",
+ function onThreadAttached() {
+ postMessage(JSON.stringify({ type: "worker-thread-attached" }));
+ }
+ );
+
+ // Step 5: Send a response packet to the parent to notify
+ // it that a connection has been established.
+ connections.set(forwardingPrefix, {
+ connection,
+ workerTargetActor,
+ });
+
+ postMessage(
+ JSON.stringify({
+ type: "connected",
+ forwardingPrefix,
+ workerTargetForm: workerTargetActor.form(),
+ })
+ );
+
+ // We might receive data to watch.
+ if (packet.options.sessionData) {
+ const promises = [];
+ for (const [type, entries] of Object.entries(
+ packet.options.sessionData
+ )) {
+ promises.push(
+ workerTargetActor.addOrSetSessionDataEntry(
+ type,
+ entries,
+ false,
+ "set"
+ )
+ );
+ }
+ await Promise.all(promises);
+ }
+
+ break;
+
+ case "add-or-set-session-data-entry":
+ await connections
+ .get(packet.forwardingPrefix)
+ .workerTargetActor.addOrSetSessionDataEntry(
+ packet.dataEntryType,
+ packet.entries,
+ packet.updateType
+ );
+ postMessage(JSON.stringify({ type: "session-data-entry-added-or-set" }));
+ break;
+
+ case "remove-session-data-entry":
+ await connections
+ .get(packet.forwardingPrefix)
+ .workerTargetActor.removeSessionDataEntry(
+ packet.dataEntryType,
+ packet.entries
+ );
+ break;
+
+ case "disconnect":
+ // This will destroy the associate WorkerTargetActor (and the actors it manages).
+ if (connections.has(packet.forwardingPrefix)) {
+ connections.get(packet.forwardingPrefix).connection.close();
+ connections.delete(packet.forwardingPrefix);
+ }
+ break;
+ }
+});