summaryrefslogtreecommitdiffstats
path: root/remote/cdp/sessions/TabSession.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/cdp/sessions/TabSession.sys.mjs')
-rw-r--r--remote/cdp/sessions/TabSession.sys.mjs150
1 files changed, 150 insertions, 0 deletions
diff --git a/remote/cdp/sessions/TabSession.sys.mjs b/remote/cdp/sessions/TabSession.sys.mjs
new file mode 100644
index 0000000000..90d098fba4
--- /dev/null
+++ b/remote/cdp/sessions/TabSession.sys.mjs
@@ -0,0 +1,150 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+import { Session } from "chrome://remote/content/cdp/sessions/Session.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ Log: "chrome://remote/content/shared/Log.sys.mjs",
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "logger", () =>
+ lazy.Log.get(lazy.Log.TYPES.CDP)
+);
+
+/**
+ * A session to communicate with a given tab
+ */
+export class TabSession extends Session {
+ /**
+ * @param {Connection} connection
+ * The connection used to communicate with the server.
+ * @param {TabTarget} target
+ * The tab target to which this session communicates with.
+ * @param {number=} id
+ * If this session isn't the default one used for the HTTP endpoint we
+ * connected to, the session requires an id to distinguish it from the default
+ * one. This id is used to filter our request, responses and events between
+ * all active sessions.
+ * For now, this is only passed by `Target.attachToTarget()`.
+ * Otherwise it will be undefined when you are connecting directly to
+ * a given Tab. i.e. connect directly to the WebSocket URL provided by
+ * /json/list HTTP endpoint.
+ */
+ constructor(connection, target, id) {
+ super(connection, target, id);
+
+ // Request id => { resolve, reject }
+ this.requestPromises = new Map();
+
+ this.registerFramescript(this.mm);
+
+ this.target.browser.addEventListener("XULFrameLoaderCreated", this);
+ }
+
+ destructor() {
+ super.destructor();
+
+ this.requestPromises.clear();
+
+ this.target.browser.removeEventListener("XULFrameLoaderCreated", this);
+
+ // this.mm might be null if the browser of the TabTarget was already closed.
+ // See Bug 1747301.
+ this.mm?.sendAsyncMessage("remote:destroy", {
+ browsingContextId: this.browsingContext.id,
+ });
+
+ this.mm?.removeMessageListener("remote:event", this);
+ this.mm?.removeMessageListener("remote:result", this);
+ this.mm?.removeMessageListener("remote:error", this);
+ }
+
+ execute(id, domain, command, params) {
+ // Check if the domain and command is implemented in the parent
+ // and execute it there. Otherwise forward the command to the content process
+ // in order to try to execute it in the content process.
+ if (this.domains.domainSupportsMethod(domain, command)) {
+ return super.execute(id, domain, command, params);
+ }
+ return this.executeInChild(id, domain, command, params);
+ }
+
+ executeInChild(id, domain, command, params) {
+ return new Promise((resolve, reject) => {
+ // Save the promise's resolution and rejection handler in order to later
+ // resolve this promise once we receive the reply back from the content process.
+ this.requestPromises.set(id, { resolve, reject });
+
+ this.mm.sendAsyncMessage("remote:request", {
+ browsingContextId: this.browsingContext.id,
+ request: { id, domain, command, params },
+ });
+ });
+ }
+
+ get mm() {
+ return this.target.mm;
+ }
+
+ get browsingContext() {
+ return this.target.browsingContext;
+ }
+
+ /**
+ * Register the framescript and listeners for the given message manager.
+ *
+ * @param {MessageManager} messageManager
+ * The message manager to use.
+ */
+ registerFramescript(messageManager) {
+ messageManager.loadFrameScript(
+ "chrome://remote/content/cdp/sessions/frame-script.js",
+ false
+ );
+
+ messageManager.addMessageListener("remote:event", this);
+ messageManager.addMessageListener("remote:result", this);
+ messageManager.addMessageListener("remote:error", this);
+ }
+
+ // Event handler
+ handleEvent = function ({ target, type }) {
+ switch (type) {
+ case "XULFrameLoaderCreated":
+ if (target === this.target.browser) {
+ lazy.logger.trace("Remoteness change detected");
+ this.registerFramescript(this.mm);
+ }
+ break;
+ }
+ };
+
+ // nsIMessageListener
+
+ receiveMessage({ name, data }) {
+ const { id, result, event, error } = data;
+
+ switch (name) {
+ case "remote:result":
+ const { resolve } = this.requestPromises.get(id);
+ resolve(result);
+ this.requestPromises.delete(id);
+ break;
+
+ case "remote:event":
+ this.connection.sendEvent(event.eventName, event.params, this.id);
+ break;
+
+ case "remote:error":
+ const { reject } = this.requestPromises.get(id);
+ reject(error);
+ this.requestPromises.delete(id);
+ break;
+ }
+ }
+}