mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
Move WebExtensions enabled Add-ons Manager
This commit is contained in:
parent
d7d16e7fbb
commit
358cd0404a
1100 changed files with 133 additions and 106 deletions
|
|
@ -1,269 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
const MSG_PROMISE_REQUEST = "WebAPIPromiseRequest";
|
||||
const MSG_PROMISE_RESULT = "WebAPIPromiseResult";
|
||||
const MSG_INSTALL_EVENT = "WebAPIInstallEvent";
|
||||
const MSG_INSTALL_CLEANUP = "WebAPICleanup";
|
||||
const MSG_ADDON_EVENT_REQ = "WebAPIAddonEventRequest";
|
||||
const MSG_ADDON_EVENT = "WebAPIAddonEvent";
|
||||
|
||||
class APIBroker {
|
||||
constructor(mm) {
|
||||
this.mm = mm;
|
||||
|
||||
this._promises = new Map();
|
||||
|
||||
// _installMap maps integer ids to DOM AddonInstall instances
|
||||
this._installMap = new Map();
|
||||
|
||||
this.mm.addMessageListener(MSG_PROMISE_RESULT, this);
|
||||
this.mm.addMessageListener(MSG_INSTALL_EVENT, this);
|
||||
|
||||
this._eventListener = null;
|
||||
}
|
||||
|
||||
receiveMessage(message) {
|
||||
let payload = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case MSG_PROMISE_RESULT: {
|
||||
if (!this._promises.has(payload.callbackID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let resolve = this._promises.get(payload.callbackID);
|
||||
this._promises.delete(payload.callbackID);
|
||||
resolve(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_INSTALL_EVENT: {
|
||||
let install = this._installMap.get(payload.id);
|
||||
if (!install) {
|
||||
let err = new Error(`Got install event for unknown install ${payload.id}`);
|
||||
Cu.reportError(err);
|
||||
return;
|
||||
}
|
||||
install._dispatch(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_ADDON_EVENT: {
|
||||
if (this._eventListener) {
|
||||
this._eventListener(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendRequest(type, ...args) {
|
||||
return new Promise(resolve => {
|
||||
let callbackID = APIBroker._nextID++;
|
||||
|
||||
this._promises.set(callbackID, resolve);
|
||||
this.mm.sendAsyncMessage(MSG_PROMISE_REQUEST, { type, callbackID, args });
|
||||
});
|
||||
}
|
||||
|
||||
setAddonListener(callback) {
|
||||
this._eventListener = callback;
|
||||
if (callback) {
|
||||
this.mm.addMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: true});
|
||||
} else {
|
||||
this.mm.removeMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: false});
|
||||
}
|
||||
}
|
||||
|
||||
sendCleanup(ids) {
|
||||
this.setAddonListener(null);
|
||||
this.mm.sendAsyncMessage(MSG_INSTALL_CLEANUP, { ids });
|
||||
}
|
||||
}
|
||||
|
||||
APIBroker._nextID = 0;
|
||||
|
||||
// Base class for building classes to back content-exposed interfaces.
|
||||
class APIObject {
|
||||
init(window, broker, properties) {
|
||||
this.window = window;
|
||||
this.broker = broker;
|
||||
|
||||
// Copy any provided properties onto this object, webidl bindings
|
||||
// will only expose to content what should be exposed.
|
||||
for (let key of Object.keys(properties)) {
|
||||
this[key] = properties[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to implement an asychronous method visible to content, where
|
||||
* the method is implemented by sending a message to the parent process
|
||||
* and then wrapping the returned object or error in an appropriate object.
|
||||
* This helper method ensures that:
|
||||
* - Returned Promise objects are from the content window
|
||||
* - Rejected Promises have Error objects from the content window
|
||||
* - Only non-internal errors are exposed to the caller
|
||||
*
|
||||
* @param {string} apiRequest The command to invoke in the parent process.
|
||||
* @param {array<cloneable>} apiArgs The arguments to include with the
|
||||
* request to the parent process.
|
||||
* @param {function} resultConvert If provided, a function called with the
|
||||
* result from the parent process as an
|
||||
* argument. Used to convert the result
|
||||
* into something appropriate for content.
|
||||
* @returns {Promise<any>} A Promise suitable for passing directly to content.
|
||||
*/
|
||||
_apiTask(apiRequest, apiArgs, resultConverter) {
|
||||
let win = this.window;
|
||||
let broker = this.broker;
|
||||
return new win.Promise((resolve, reject) => {
|
||||
Task.spawn(function*() {
|
||||
let result = yield broker.sendRequest(apiRequest, ...apiArgs);
|
||||
if ("reject" in result) {
|
||||
let err = new win.Error(result.reject.message);
|
||||
// We don't currently put any other properties onto Errors
|
||||
// generated by mozAddonManager. If/when we do, they will
|
||||
// need to get copied here.
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = result.resolve;
|
||||
if (resultConverter) {
|
||||
obj = resultConverter(obj);
|
||||
}
|
||||
resolve(obj);
|
||||
}).catch(err => {
|
||||
Cu.reportError(err);
|
||||
reject(new win.Error("Unexpected internal error"));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Addon extends APIObject {
|
||||
constructor(...args) {
|
||||
super();
|
||||
this.init(...args);
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
return this._apiTask("addonUninstall", [this.id]);
|
||||
}
|
||||
|
||||
setEnabled(value) {
|
||||
return this._apiTask("addonSetEnabled", [this.id, value]);
|
||||
}
|
||||
}
|
||||
|
||||
class AddonInstall extends APIObject {
|
||||
constructor(window, broker, properties) {
|
||||
super();
|
||||
this.init(window, broker, properties);
|
||||
|
||||
broker._installMap.set(properties.id, this);
|
||||
}
|
||||
|
||||
_dispatch(data) {
|
||||
// The message for the event includes updated copies of all install
|
||||
// properties. Use the usual "let webidl filter visible properties" trick.
|
||||
for (let key of Object.keys(data)) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
|
||||
let event = new this.window.Event(data.event);
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
}
|
||||
|
||||
install() {
|
||||
return this._apiTask("addonInstallDoInstall", [this.id]);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
return this._apiTask("addonInstallCancel", [this.id]);
|
||||
}
|
||||
}
|
||||
|
||||
class WebAPI extends APIObject {
|
||||
constructor() {
|
||||
super();
|
||||
this.allInstalls = [];
|
||||
this.listenerCount = 0;
|
||||
}
|
||||
|
||||
init(window) {
|
||||
let mm = window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
let broker = new APIBroker(mm);
|
||||
|
||||
super.init(window, broker, {});
|
||||
|
||||
window.addEventListener("unload", event => {
|
||||
this.broker.sendCleanup(this.allInstalls);
|
||||
});
|
||||
}
|
||||
|
||||
getAddonByID(id) {
|
||||
return this._apiTask("getAddonByID", [id], addonInfo => {
|
||||
if (!addonInfo) {
|
||||
return null;
|
||||
}
|
||||
let addon = new Addon(this.window, this.broker, addonInfo);
|
||||
return this.window.Addon._create(this.window, addon);
|
||||
});
|
||||
}
|
||||
|
||||
createInstall(options) {
|
||||
return this._apiTask("createInstall", [options], installInfo => {
|
||||
if (!installInfo) {
|
||||
return null;
|
||||
}
|
||||
let install = new AddonInstall(this.window, this.broker, installInfo);
|
||||
this.allInstalls.push(installInfo.id);
|
||||
return this.window.AddonInstall._create(this.window, install);
|
||||
});
|
||||
}
|
||||
|
||||
eventListenerWasAdded(type) {
|
||||
if (this.listenerCount == 0) {
|
||||
this.broker.setAddonListener(data => {
|
||||
let event = new this.window.AddonEvent(data.event, data);
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
this.listenerCount++;
|
||||
}
|
||||
|
||||
eventListenerWasRemoved(type) {
|
||||
this.listenerCount--;
|
||||
if (this.listenerCount == 0) {
|
||||
this.broker.setAddonListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
QueryInterface(iid) {
|
||||
if (iid.equals(WebAPI.classID) || iid.equals(Ci.nsISupports)
|
||||
|| iid.equals(Ci.nsIDOMGlobalPropertyInitializer)) {
|
||||
return this;
|
||||
}
|
||||
return Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
WebAPI.prototype.classID = Components.ID("{8866d8e3-4ea5-48b7-a891-13ba0ac15235}");
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebAPI]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue