зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1602639 - Switch native messaging from MessageManagers to Conduits r=robwu
Differential Revision: https://phabricator.services.mozilla.com/D58356 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
3f8e528138
Коммит
48d05a45cb
|
@ -40,7 +40,6 @@ class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
|||
this._panelContext = null;
|
||||
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
recv: ["PanelHidden", "PanelShown"],
|
||||
});
|
||||
}
|
||||
|
@ -147,7 +146,6 @@ class ChildDevToolsInspectorSidebar extends ExtensionCommon.EventEmitter {
|
|||
this.id = id;
|
||||
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
recv: ["InspectorSidebarHidden", "InspectorSidebarShown"],
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||
EventDispatcher: "resource://gre/modules/Messaging.jsm",
|
||||
Extension: "resource://gre/modules/Extension.jsm",
|
||||
ExtensionChild: "resource://gre/modules/ExtensionChild.jsm",
|
||||
GeckoViewTabBridge: "resource://gre/modules/GeckoViewTab.jsm",
|
||||
});
|
||||
|
||||
|
@ -100,26 +99,21 @@ class ExtensionActionHelper {
|
|||
}
|
||||
}
|
||||
|
||||
class EmbedderPort extends ExtensionChild.Port {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
class EmbedderPort {
|
||||
constructor(portId, messenger) {
|
||||
this.id = portId;
|
||||
this.messenger = messenger;
|
||||
EventDispatcher.instance.registerListener(this, [
|
||||
"GeckoView:WebExtension:PortMessageFromApp",
|
||||
"GeckoView:WebExtension:PortDisconnect",
|
||||
]);
|
||||
}
|
||||
handleDisconnection() {
|
||||
super.handleDisconnection();
|
||||
close() {
|
||||
EventDispatcher.instance.unregisterListener(this, [
|
||||
"GeckoView:WebExtension:PortMessageFromApp",
|
||||
"GeckoView:WebExtension:PortDisconnect",
|
||||
]);
|
||||
}
|
||||
close() {
|
||||
// Notify listeners that this port is being closed because the context is
|
||||
// gone.
|
||||
this.disconnectByOtherEnd();
|
||||
}
|
||||
onEvent(aEvent, aData, aCallback) {
|
||||
debug`onEvent ${aEvent} ${aData}`;
|
||||
|
||||
|
@ -129,12 +123,14 @@ class EmbedderPort extends ExtensionChild.Port {
|
|||
|
||||
switch (aEvent) {
|
||||
case "GeckoView:WebExtension:PortMessageFromApp": {
|
||||
this.postMessage(aData.message);
|
||||
const holder = new StructuredCloneHolder(aData.message);
|
||||
this.messenger.sendPortMessage(this.id, holder);
|
||||
break;
|
||||
}
|
||||
|
||||
case "GeckoView:WebExtension:PortDisconnect": {
|
||||
this.disconnect();
|
||||
this.messenger.sendPortDisconnect(this.id);
|
||||
this.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -142,29 +138,24 @@ class EmbedderPort extends ExtensionChild.Port {
|
|||
}
|
||||
|
||||
class GeckoViewConnection {
|
||||
constructor(context, sender, target, nativeApp) {
|
||||
this.context = context;
|
||||
constructor(sender, nativeApp) {
|
||||
this.sender = sender;
|
||||
this.target = target;
|
||||
this.nativeApp = nativeApp;
|
||||
this.allowContentMessaging = GeckoViewWebExtension.extensionScopes.get(
|
||||
sender.extensionId
|
||||
).allowContentMessaging;
|
||||
}
|
||||
|
||||
_getMessageManager(aTarget) {
|
||||
if (aTarget.frameLoader) {
|
||||
return aTarget.frameLoader.messageManager;
|
||||
const scope = GeckoViewWebExtension.extensionScopes.get(sender.extensionId);
|
||||
this.allowContentMessaging = scope.allowContentMessaging;
|
||||
if (!this.allowContentMessaging && !sender.verified) {
|
||||
throw new Error(`Unexpected messaging sender: ${JSON.stringify(sender)}`);
|
||||
}
|
||||
return aTarget;
|
||||
}
|
||||
|
||||
get dispatcher() {
|
||||
const target = this.sender.actor.browsingContext.top.embedderElement;
|
||||
|
||||
if (this.sender.envType === "addon_child") {
|
||||
// If this is a WebExtension Page we will have a GeckoSession associated
|
||||
// to it and thus a dispatcher.
|
||||
const dispatcher = GeckoViewUtils.getDispatcherForWindow(
|
||||
this.target.ownerGlobal
|
||||
target.ownerGlobal
|
||||
);
|
||||
if (dispatcher) {
|
||||
return dispatcher;
|
||||
|
@ -180,7 +171,7 @@ class GeckoViewConnection {
|
|||
// If this message came from a content script, send the message to
|
||||
// the corresponding tab messenger so that GeckoSession can pick it
|
||||
// up.
|
||||
return GeckoViewUtils.getDispatcherForWindow(this.target.ownerGlobal);
|
||||
return GeckoViewUtils.getDispatcherForWindow(target.ownerGlobal);
|
||||
}
|
||||
|
||||
throw new Error(`Uknown sender envType: ${this.sender.envType}`);
|
||||
|
@ -205,37 +196,32 @@ class GeckoViewConnection {
|
|||
});
|
||||
}
|
||||
|
||||
onConnect(portId) {
|
||||
const port = new EmbedderPort(
|
||||
this.context,
|
||||
this.target.messageManager,
|
||||
[Services.mm],
|
||||
"",
|
||||
portId,
|
||||
this.sender,
|
||||
this.sender
|
||||
);
|
||||
port.registerOnMessage(holder =>
|
||||
onConnect(portId, messenger) {
|
||||
const port = new EmbedderPort(portId, messenger);
|
||||
|
||||
port.onPortMessage = holder =>
|
||||
this._sendMessage({
|
||||
type: "GeckoView:WebExtension:PortMessage",
|
||||
portId: port.id,
|
||||
data: holder.deserialize({}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
port.registerOnDisconnect(msg =>
|
||||
port.onPortDisconnect = () => {
|
||||
EventDispatcher.instance.sendRequest({
|
||||
type: "GeckoView:WebExtension:Disconnect",
|
||||
sender: this.sender,
|
||||
portId: port.id,
|
||||
})
|
||||
);
|
||||
});
|
||||
port.close();
|
||||
};
|
||||
|
||||
return this._sendMessage({
|
||||
this._sendMessage({
|
||||
type: "GeckoView:WebExtension:Connect",
|
||||
data: {},
|
||||
portId: port.id,
|
||||
});
|
||||
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ const {
|
|||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
defineLazyGetter,
|
||||
EventEmitter,
|
||||
EventManager,
|
||||
LocalAPIImplementation,
|
||||
|
@ -431,11 +432,92 @@ class Port {
|
|||
}
|
||||
}
|
||||
|
||||
class NativePort extends Port {
|
||||
postMessage(data) {
|
||||
data = NativeApp.encodeMessage(this.context, data);
|
||||
// Simple single-event emitter-like helper, exposes the EventManager api.
|
||||
class SimpleEventAPI extends EventManager {
|
||||
constructor(context, name) {
|
||||
super({ context, name });
|
||||
this.fires = new Set();
|
||||
this.register = fire => {
|
||||
this.fires.add(fire);
|
||||
return () => this.fires.delete(fire);
|
||||
};
|
||||
}
|
||||
emit(...args) {
|
||||
return [...this.fires].map(fire => fire.asyncWithoutClone(...args));
|
||||
}
|
||||
}
|
||||
|
||||
return super.postMessage(data);
|
||||
function holdMessage(sender, data) {
|
||||
if (AppConstants.platform !== "android") {
|
||||
data = NativeApp.encodeMessage(sender.context, data);
|
||||
}
|
||||
return new StructuredCloneHolder(data);
|
||||
}
|
||||
|
||||
class NativePort {
|
||||
constructor(context, name, portId) {
|
||||
this.id = portId;
|
||||
this.context = context;
|
||||
this.conduit = context.openConduit(this, {
|
||||
recv: ["PortMessage", "PortDisconnect"],
|
||||
send: ["PortMessage"],
|
||||
});
|
||||
|
||||
this.onMessage = new SimpleEventAPI(context, "Port.onMessage");
|
||||
this.onDisconnect = new SimpleEventAPI(context, "Port.onDisconnect");
|
||||
|
||||
let api = {
|
||||
name,
|
||||
error: null,
|
||||
onMessage: this.onMessage.api(),
|
||||
onDisconnect: this.onDisconnect.api(),
|
||||
postMessage: this.sendPortMessage.bind(this),
|
||||
disconnect: () => this.conduit.close(),
|
||||
};
|
||||
this.api = Cu.cloneInto(api, context.cloneScope, { cloneFunctions: true });
|
||||
}
|
||||
|
||||
recvPortMessage({ holder }) {
|
||||
this.onMessage.emit(holder.deserialize(this.api), this.api);
|
||||
}
|
||||
|
||||
recvPortDisconnect({ error }) {
|
||||
this.api.error = error && this.context.normalizeError(error);
|
||||
this.onDisconnect.emit(this.api);
|
||||
this.conduit.close();
|
||||
}
|
||||
|
||||
sendPortMessage(json) {
|
||||
if (this.conduit.actor) {
|
||||
return this.conduit.sendPortMessage({ holder: holdMessage(this, json) });
|
||||
}
|
||||
throw new this.context.Error("Attempt to postMessage on disconnected port");
|
||||
}
|
||||
}
|
||||
|
||||
// Handles native messaging for a context, similar to the Messenger below.
|
||||
class NativeMessenger {
|
||||
constructor(context, sender) {
|
||||
this.context = context;
|
||||
this.conduit = context.openConduit(this, {
|
||||
url: sender.url,
|
||||
frameId: sender.frameId,
|
||||
childId: context.childManager.id,
|
||||
query: ["NativeMessage", "NativeConnect"],
|
||||
});
|
||||
}
|
||||
|
||||
sendNativeMessage(nativeApp, json) {
|
||||
let holder = holdMessage(this, json);
|
||||
return this.conduit.queryNativeMessage({ nativeApp, holder });
|
||||
}
|
||||
|
||||
connectNative(nativeApp) {
|
||||
let port = new NativePort(this.context, nativeApp, getUniqueId());
|
||||
this.conduit
|
||||
.queryNativeConnect({ nativeApp, portId: port.id })
|
||||
.catch(error => port.recvPortDisconnect({ error }));
|
||||
return port.api;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,16 +593,6 @@ class Messenger {
|
|||
return this.context.wrapPromise(promise, responseCallback);
|
||||
}
|
||||
|
||||
sendNativeMessage(messageManager, msg, recipient, responseCallback) {
|
||||
if (
|
||||
AppConstants.platform !== "android" ||
|
||||
!this.context.extension.hasPermission("geckoViewAddons")
|
||||
) {
|
||||
msg = NativeApp.encodeMessage(this.context, msg);
|
||||
}
|
||||
return this.sendMessage(messageManager, msg, recipient, responseCallback);
|
||||
}
|
||||
|
||||
_onMessage(name, filter) {
|
||||
return new EventManager({
|
||||
context: this.context,
|
||||
|
@ -674,38 +746,6 @@ class Messenger {
|
|||
return this._connect(messageManager, port, recipient);
|
||||
}
|
||||
|
||||
connectNative(messageManager, name, recipient) {
|
||||
let portId = getUniqueId();
|
||||
|
||||
let port;
|
||||
if (
|
||||
AppConstants.platform === "android" &&
|
||||
this.context.extension.hasPermission("geckoViewAddons")
|
||||
) {
|
||||
port = new Port(
|
||||
this.context,
|
||||
messageManager,
|
||||
this.messageManagers,
|
||||
name,
|
||||
portId,
|
||||
null,
|
||||
recipient
|
||||
);
|
||||
} else {
|
||||
port = new NativePort(
|
||||
this.context,
|
||||
messageManager,
|
||||
this.messageManagers,
|
||||
name,
|
||||
portId,
|
||||
null,
|
||||
recipient
|
||||
);
|
||||
}
|
||||
|
||||
return this._connect(messageManager, port, recipient);
|
||||
}
|
||||
|
||||
_onConnect(name, filter) {
|
||||
return new EventManager({
|
||||
context: this.context,
|
||||
|
@ -795,6 +835,10 @@ class Messenger {
|
|||
}
|
||||
}
|
||||
|
||||
defineLazyGetter(Messenger.prototype, "nm", function() {
|
||||
return new NativeMessenger(this.context, this.sender);
|
||||
});
|
||||
|
||||
// For test use only.
|
||||
var ExtensionManager = {
|
||||
extensions: new Map(),
|
||||
|
@ -974,7 +1018,6 @@ class BrowserExtensionContent extends EventEmitter {
|
|||
|
||||
emit(event, ...args) {
|
||||
Services.cpmm.sendAsyncMessage(this.MESSAGE_EMIT_EVENT, { event, args });
|
||||
|
||||
super.emit(event, ...args);
|
||||
}
|
||||
|
||||
|
@ -1174,7 +1217,6 @@ class ChildAPIManager {
|
|||
this.id = `${context.extension.id}.${context.contextId}`;
|
||||
|
||||
this.conduit = context.openConduit(this, {
|
||||
id: this.id,
|
||||
send: ["CreateProxyContext", "APICall", "AddListener", "RemoveListener"],
|
||||
recv: ["CallResult", "RunListener"],
|
||||
});
|
||||
|
|
|
@ -499,6 +499,7 @@ class BaseContext {
|
|||
openConduit(subject, address) {
|
||||
let wgc = this.contentWindow.getWindowGlobalChild();
|
||||
let conduit = wgc.getActor("Conduits").openConduit(subject, {
|
||||
id: subject.id || getUniqueId(),
|
||||
extensionId: this.extension.id,
|
||||
envType: this.envType,
|
||||
...address,
|
||||
|
|
|
@ -328,6 +328,66 @@ class ExtensionPortProxy {
|
|||
}
|
||||
}
|
||||
|
||||
// Handles NativeMessaging and GeckoView, similar to ProxyMessenger below.
|
||||
const NativeMessenger = {
|
||||
/**
|
||||
* @typedef {object} ParentPort
|
||||
* @prop {function(StructuredCloneHolder)} onPortMessage
|
||||
* @prop {function()} onPortDisconnect
|
||||
*/
|
||||
/** @type Map<number, ParentPort> */
|
||||
ports: new Map(),
|
||||
|
||||
init() {
|
||||
this.conduit = new BroadcastConduit(NativeMessenger, {
|
||||
id: "NativeMessenger",
|
||||
recv: ["NativeMessage", "NativeConnect", "PortMessage"],
|
||||
send: ["PortMessage", "PortDisconnect"],
|
||||
});
|
||||
},
|
||||
|
||||
openNative(nativeApp, sender) {
|
||||
let context = ParentAPIManager.getContextById(sender.childId);
|
||||
if (context.extension.hasPermission("geckoViewAddons")) {
|
||||
return new GeckoViewConnection(sender, nativeApp);
|
||||
} else if (sender.verified) {
|
||||
return new NativeApp(context, nativeApp);
|
||||
}
|
||||
throw new Error(`Native messaging not allowed: ${JSON.stringify(sender)}`);
|
||||
},
|
||||
|
||||
recvNativeMessage({ nativeApp, holder }, { sender }) {
|
||||
return this.openNative(nativeApp, sender).sendMessage(holder);
|
||||
},
|
||||
|
||||
recvNativeConnect({ nativeApp, portId }, { sender }) {
|
||||
let port = this.openNative(nativeApp, sender).onConnect(portId, this);
|
||||
this.conduit.reportOnClosed(portId);
|
||||
this.ports.set(portId, port);
|
||||
},
|
||||
|
||||
recvConduitClosed(sender) {
|
||||
let app = this.ports.get(sender.id);
|
||||
if (this.ports.delete(sender.id)) {
|
||||
app.onPortDisconnect();
|
||||
}
|
||||
},
|
||||
|
||||
recvPortMessage({ holder }, { sender }) {
|
||||
this.ports.get(sender.id).onPortMessage(holder);
|
||||
},
|
||||
|
||||
sendPortMessage(portId, holder) {
|
||||
this.conduit.sendPortMessage(portId, { holder });
|
||||
},
|
||||
|
||||
sendPortDisconnect(portId, error) {
|
||||
this.conduit.sendPortDisconnect(portId, { error });
|
||||
this.ports.delete(portId);
|
||||
},
|
||||
};
|
||||
NativeMessenger.init();
|
||||
|
||||
// Subscribes to messages related to the extension messaging API and forwards it
|
||||
// to the relevant message manager. The "sender" field for the `onMessage` and
|
||||
// `onConnect` events are updated if needed.
|
||||
|
@ -431,57 +491,6 @@ ProxyMessenger = {
|
|||
data,
|
||||
responseType,
|
||||
}) {
|
||||
if (recipient.toNativeApp) {
|
||||
let { childId, toNativeApp } = recipient;
|
||||
let context = ParentAPIManager.getContextById(childId);
|
||||
|
||||
if (
|
||||
context.parentMessageManager !== target.messageManager ||
|
||||
(sender.envType === "addon_child" &&
|
||||
context.envType !== "addon_parent") ||
|
||||
(sender.envType === "content_child" &&
|
||||
context.envType !== "content_parent") ||
|
||||
context.extension.id !== sender.extensionId
|
||||
) {
|
||||
throw new Error("Got message for an unexpected messageManager.");
|
||||
}
|
||||
|
||||
if (
|
||||
AppConstants.platform === "android" &&
|
||||
context.extension.hasPermission("geckoViewAddons")
|
||||
) {
|
||||
let connection = new GeckoViewConnection(
|
||||
context,
|
||||
sender,
|
||||
target,
|
||||
toNativeApp
|
||||
);
|
||||
if (messageName == "Extension:Message") {
|
||||
return connection.sendMessage(data);
|
||||
} else if (messageName == "Extension:Connect") {
|
||||
return connection.onConnect(data.portId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageName == "Extension:Message") {
|
||||
return new NativeApp(context, toNativeApp).sendMessage(data);
|
||||
}
|
||||
if (messageName == "Extension:Connect") {
|
||||
NativeApp.onConnectNative(
|
||||
context,
|
||||
target.messageManager,
|
||||
data.portId,
|
||||
sender,
|
||||
toNativeApp
|
||||
);
|
||||
return true;
|
||||
}
|
||||
// "Extension:Port:Disconnect" and "Extension:Port:PostMessage" for
|
||||
// native messages are handled by NativeApp or GeckoViewConnection.
|
||||
return;
|
||||
}
|
||||
|
||||
const noHandlerError = {
|
||||
result: MessageChannel.RESULT_NO_HANDLER,
|
||||
message: "No matching message handler for the given recipient.",
|
||||
|
|
|
@ -51,7 +51,15 @@ function promiseTimeout(delay) {
|
|||
* An Error subclass for which complete error messages are always passed
|
||||
* to extensions, rather than being interpreted as an unknown error.
|
||||
*/
|
||||
class ExtensionError extends Error {}
|
||||
class ExtensionError extends DOMException {
|
||||
constructor(message) {
|
||||
super(message, "ExtensionError");
|
||||
}
|
||||
// Custom JS classes can't survive IPC, so need to check error name.
|
||||
static [Symbol.hasInstance](e) {
|
||||
return e instanceof DOMException && e.name === "ExtensionError";
|
||||
}
|
||||
}
|
||||
|
||||
function filterStack(error) {
|
||||
return String(error.stack).replace(
|
||||
|
|
|
@ -142,12 +142,6 @@ var NativeManifests = {
|
|||
);
|
||||
return null;
|
||||
}
|
||||
if (type !== "storage" && context.envType !== "addon_parent") {
|
||||
Cu.reportError(
|
||||
`Native manifest at ${path} is not available to non-extension contexts`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return manifest;
|
||||
})
|
||||
|
|
|
@ -15,10 +15,13 @@ const { EventEmitter } = ChromeUtils.import(
|
|||
"resource://gre/modules/EventEmitter.jsm"
|
||||
);
|
||||
|
||||
const {
|
||||
ExtensionUtils: { ExtensionError },
|
||||
} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
||||
ExtensionChild: "resource://gre/modules/ExtensionChild.jsm",
|
||||
NativeManifests: "resource://gre/modules/NativeManifests.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
|
@ -79,9 +82,7 @@ var NativeApp = class extends EventEmitter {
|
|||
// Report a generic error to not leak information about whether a native
|
||||
// application is installed to addons that do not have the right permission.
|
||||
if (!hostInfo) {
|
||||
throw new context.cloneScope.Error(
|
||||
`No such native application ${application}`
|
||||
);
|
||||
throw new ExtensionError(`No such native application ${application}`);
|
||||
}
|
||||
|
||||
let command = hostInfo.manifest.path;
|
||||
|
@ -118,34 +119,22 @@ var NativeApp = class extends EventEmitter {
|
|||
|
||||
/**
|
||||
* Open a connection to a native messaging host.
|
||||
*
|
||||
* @param {BaseContext} context The context associated with the port.
|
||||
* @param {nsIMessageSender} messageManager The message manager used to send
|
||||
* and receive messages from the port's creator.
|
||||
* @param {number} portId A unique internal ID that identifies the port.
|
||||
* @param {object} sender The object describing the creator of the connection
|
||||
* request.
|
||||
* @param {string} application The name of the native messaging host.
|
||||
* @param {NativeMessenger} port Parent NativeMessenger used to send messages.
|
||||
* @returns {ParentPort}
|
||||
*/
|
||||
static onConnectNative(context, messageManager, portId, sender, application) {
|
||||
let app = new NativeApp(context, application);
|
||||
let port = new ExtensionChild.Port(
|
||||
context,
|
||||
messageManager,
|
||||
[Services.mm],
|
||||
"",
|
||||
portId,
|
||||
sender,
|
||||
sender
|
||||
);
|
||||
app.once("disconnect", (what, err) => port.disconnect(err));
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
app.on("message", (what, msg) => port.postMessage(msg));
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
port.registerOnMessage(holder => app.send(holder));
|
||||
port.registerOnDisconnect(msg => app.close());
|
||||
onConnect(portId, port) {
|
||||
// eslint-disable-next-line
|
||||
this.on("message", (_, message) => {
|
||||
port.sendPortMessage(portId, new StructuredCloneHolder(message));
|
||||
});
|
||||
this.once("disconnect", (_, error) => {
|
||||
port.sendPortDisconnect(portId, error && new ClonedErrorHolder(error));
|
||||
});
|
||||
return {
|
||||
onPortMessage: holder => this.send(holder),
|
||||
onPortDisconnect: () => this.close(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +146,7 @@ var NativeApp = class extends EventEmitter {
|
|||
message = context.jsonStringify(message);
|
||||
let buffer = new TextEncoder().encode(message).buffer;
|
||||
if (buffer.byteLength > NativeApp.maxWrite) {
|
||||
throw new context.cloneScope.Error("Write too big");
|
||||
throw new context.Error("Write too big");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
@ -178,7 +167,7 @@ var NativeApp = class extends EventEmitter {
|
|||
.readUint32()
|
||||
.then(len => {
|
||||
if (len > NativeApp.maxRead) {
|
||||
throw new this.context.cloneScope.Error(
|
||||
throw new ExtensionError(
|
||||
`Native application tried to send a message of ${len} bytes, which exceeds the limit of ${
|
||||
NativeApp.maxRead
|
||||
} bytes.`
|
||||
|
@ -257,9 +246,7 @@ var NativeApp = class extends EventEmitter {
|
|||
|
||||
send(holder) {
|
||||
if (this._isDisconnected) {
|
||||
throw new this.context.cloneScope.Error(
|
||||
"Attempt to postMessage on disconnected port"
|
||||
);
|
||||
throw new ExtensionError("Attempt to postMessage on disconnected port");
|
||||
}
|
||||
let msg = holder.deserialize(global);
|
||||
if (Cu.getClassName(msg, true) != "ArrayBuffer") {
|
||||
|
@ -273,7 +260,7 @@ var NativeApp = class extends EventEmitter {
|
|||
let buffer = msg;
|
||||
|
||||
if (buffer.byteLength > NativeApp.maxWrite) {
|
||||
throw new this.context.cloneScope.Error("Write too big");
|
||||
throw new ExtensionError("Write too big");
|
||||
}
|
||||
|
||||
this.sendQueue.push(buffer);
|
||||
|
@ -282,9 +269,9 @@ var NativeApp = class extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// Shut down the native application and also signal to the extension
|
||||
// Shut down the native application and (by default) signal to the extension
|
||||
// that the connect has been disconnected.
|
||||
_cleanup(err) {
|
||||
_cleanup(err, fromExtension = false) {
|
||||
this.context.forgetOnClose(this);
|
||||
|
||||
let doCleanup = () => {
|
||||
|
@ -326,18 +313,18 @@ var NativeApp = class extends EventEmitter {
|
|||
this.startupPromise.then(doCleanup);
|
||||
}
|
||||
|
||||
if (!this.sentDisconnect) {
|
||||
this.sentDisconnect = true;
|
||||
if (!this.sentDisconnect && !fromExtension) {
|
||||
if (err && err.errorCode == Subprocess.ERROR_END_OF_FILE) {
|
||||
err = null;
|
||||
}
|
||||
this.emit("disconnect", err);
|
||||
}
|
||||
this.sentDisconnect = true;
|
||||
}
|
||||
|
||||
// Called from Context when the extension is shut down.
|
||||
// Called when the Context or Port is closed.
|
||||
close() {
|
||||
this._cleanup();
|
||||
this._cleanup(null, true);
|
||||
}
|
||||
|
||||
sendMessage(holder) {
|
||||
|
|
|
@ -115,29 +115,12 @@ this.runtime = class extends ExtensionAPI {
|
|||
);
|
||||
},
|
||||
|
||||
connectNative(application) {
|
||||
let recipient = {
|
||||
childId: context.childManager.id,
|
||||
toNativeApp: application,
|
||||
};
|
||||
|
||||
return context.messenger.connectNative(
|
||||
context.messageManager,
|
||||
"",
|
||||
recipient
|
||||
);
|
||||
connectNative(nativeApp) {
|
||||
return context.messenger.nm.connectNative(nativeApp);
|
||||
},
|
||||
|
||||
sendNativeMessage(application, message) {
|
||||
let recipient = {
|
||||
childId: context.childManager.id,
|
||||
toNativeApp: application,
|
||||
};
|
||||
return context.messenger.sendNativeMessage(
|
||||
context.messageManager,
|
||||
message,
|
||||
recipient
|
||||
);
|
||||
sendNativeMessage(nativeApp, message) {
|
||||
return context.messenger.nm.sendNativeMessage(nativeApp, message);
|
||||
},
|
||||
|
||||
get lastError() {
|
||||
|
@ -150,7 +133,7 @@ this.runtime = class extends ExtensionAPI {
|
|||
|
||||
id: extension.id,
|
||||
|
||||
getURL: function(url) {
|
||||
getURL(url) {
|
||||
return extension.baseURI.resolve(url);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
// Disable extra warnings "Reference to undefined property".
|
||||
options("strict"); // eslint-disable-line
|
||||
|
||||
/* exported createHttpServer, cleanupDir, clearCache, promiseConsoleOutput,
|
||||
promiseQuotaManagerServiceReset, promiseQuotaManagerServiceClear,
|
||||
runWithPrefs, testEnv, withHandlingUserInput */
|
||||
|
|
|
@ -269,6 +269,10 @@ add_task(async function test_disconnect() {
|
|||
} else if (what == "disconnect") {
|
||||
try {
|
||||
port.disconnect();
|
||||
browser.test.assertThrows(
|
||||
() => port.postMessage("void"),
|
||||
"Attempt to postMessage on disconnected port"
|
||||
);
|
||||
browser.test.sendMessage("disconnect-result", { success: true });
|
||||
} catch (err) {
|
||||
browser.test.sendMessage("disconnect-result", {
|
||||
|
@ -630,7 +634,7 @@ add_task(async function test_connect_native_from_content_script() {
|
|||
"onDisconnect handler should receive the port as the first argument"
|
||||
);
|
||||
browser.test.assertEq(
|
||||
"No such native application echo",
|
||||
"An unexpected error occurred",
|
||||
port.error && port.error.message
|
||||
);
|
||||
browser.test.sendMessage("result", "disconnected");
|
||||
|
|
|
@ -282,19 +282,6 @@ add_task(async function test_no_allowed_extensions() {
|
|||
);
|
||||
});
|
||||
|
||||
add_task(async function test_invalid_context_envType() {
|
||||
let manifest = Object.assign({}, templateManifest);
|
||||
await writeManifest(USER_TEST_JSON, manifest);
|
||||
for (let type of ["stdio", "pkcs11"]) {
|
||||
let badContext = { ...context, type, envType: "content_parent" };
|
||||
let result = await lookupApplication("test", badContext);
|
||||
equal(result, null, `lookupApplication ignores bad envType for "${type}"`);
|
||||
}
|
||||
// type=storage is allowed to be accessed from content_parent, which is
|
||||
// covered by the test task "test_storage_managed_from_content_script"
|
||||
// in ./test_ext_storage_managed.js.
|
||||
});
|
||||
|
||||
const GLOBAL_TEST_JSON = OS.Path.join(globalDir.path, TYPE_SLUG, "test.json");
|
||||
let globalManifest = Object.assign({}, templateManifest);
|
||||
globalManifest.description = "This manifest is from the systemwide directory";
|
||||
|
|
|
@ -104,6 +104,7 @@ module.exports = {
|
|||
ChromeWorker: false,
|
||||
Clipboard: false,
|
||||
ClipboardEvent: false,
|
||||
ClonedErrorHolder: false,
|
||||
CloseEvent: false,
|
||||
CommandEvent: false,
|
||||
Comment: false,
|
||||
|
|
Загрузка…
Ссылка в новой задаче