gecko-dev/remote/sessions/TabSession.jsm

138 строки
4.0 KiB
JavaScript

/* 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";
var EXPORTED_SYMBOLS = ["TabSession"];
const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
const {Session} = ChromeUtils.import("chrome://remote/content/sessions/Session.jsm");
/**
* A session to communicate with a given tab
*/
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 (optional)
* 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.
* @param Session parentSession (optional)
* If this isn't the default session, optional hand over a session to which
* we will forward all request responses and events via
* `Target.receivedMessageFromTarget` events.
*/
constructor(connection, target, id, parentSession) {
super(connection, target, id);
this.parentSession = parentSession;
this.mm.addMessageListener("remote:event", this);
this.mm.addMessageListener("remote:result", this);
this.mm.addMessageListener("remote:error", this);
this.mm.loadFrameScript("chrome://remote/content/sessions/frame-script.js", false);
}
destructor() {
super.destructor();
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);
}
async onMessage({id, method, params}) {
try {
if (typeof id == "undefined") {
throw new TypeError("Message missing 'id' field");
}
if (typeof method == "undefined") {
throw new TypeError("Message missing 'method' field");
}
const {domain, command} = Domains.splitMethod(method);
if (this.domains.has(domain)) {
await this.execute(id, domain, command, params);
} else {
this.executeInChild(id, domain, command, params);
}
} catch (e) {
this.onError(id, e);
}
}
executeInChild(id, domain, command, params) {
this.mm.sendAsyncMessage("remote:request", {
browsingContextId: this.browsingContext.id,
request: {id, domain, command, params},
});
}
onResult(id, result) {
super.onResult(id, result);
// When `Target.sendMessageToTarget` is used, we should forward the responses
// to the parent session from which we called `sendMessageToTarget`.
if (this.parentSession) {
this.parentSession.onEvent("Target.receivedMessageFromTarget", {
sessionId: this.id,
message: JSON.stringify({ id, result }),
});
}
}
onEvent(eventName, params) {
super.onEvent(eventName, params);
// When `Target.sendMessageToTarget` is used, we should forward the responses
// to the parent session from which we called `sendMessageToTarget`.
if (this.parentSession) {
this.parentSession.onEvent("Target.receivedMessageFromTarget", {
sessionId: this.id,
message: JSON.stringify({
method: eventName,
params,
}),
});
}
}
get mm() {
return this.target.mm;
}
get browsingContext() {
return this.target.browsingContext;
}
// nsIMessageListener
receiveMessage({name, data}) {
const {id, result, event, error} = data;
switch (name) {
case "remote:result":
this.onResult(id, result);
break;
case "remote:event":
this.onEvent(event.eventName, event.params);
break;
case "remote:error":
this.onError(id, error);
break;
}
}
}