зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311180 Switch mozAddonManager to frame message managers r=rhelmer
MozReview-Commit-ID: GbX0VRn4HUF --HG-- extra : rebase_source : a852a68d0c0b07f7844650e41783c577534c4527 extra : source : 5656093771903569104f411742748777ae3585a5
This commit is contained in:
Родитель
6199bfe74b
Коммит
90b062a959
|
@ -2885,7 +2885,7 @@ var AddonManagerInternal = {
|
|||
obj.maxProgress = install.maxProgress;
|
||||
},
|
||||
|
||||
makeListener(id, target) {
|
||||
makeListener(id, mm) {
|
||||
const events = [
|
||||
"onDownloadStarted",
|
||||
"onDownloadProgress",
|
||||
|
@ -2903,7 +2903,7 @@ var AddonManagerInternal = {
|
|||
listener[event] = (install) => {
|
||||
let data = {event, id};
|
||||
AddonManager.webAPI.copyProps(install, data);
|
||||
this.sendEvent(target, data);
|
||||
this.sendEvent(mm, data);
|
||||
}
|
||||
});
|
||||
return listener;
|
||||
|
@ -2944,7 +2944,7 @@ var AddonManagerInternal = {
|
|||
|
||||
let newInstall = install => {
|
||||
let id = this.nextInstall++;
|
||||
let listener = this.makeListener(id, target);
|
||||
let listener = this.makeListener(id, target.messageManager);
|
||||
install.addListener(listener);
|
||||
|
||||
this.installs.set(id, {install, target, listener});
|
||||
|
@ -3011,7 +3011,7 @@ var AddonManagerInternal = {
|
|||
|
||||
clearInstallsFrom(mm) {
|
||||
for (let [id, info] of this.installs) {
|
||||
if (info.target == mm) {
|
||||
if (info.target.messageManager == mm) {
|
||||
this.forgetInstall(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,22 +38,16 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
|
||||
var gSingleton = null;
|
||||
|
||||
var gParentMM = null;
|
||||
|
||||
|
||||
function amManager() {
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
/*globals AddonManagerPrivate*/
|
||||
|
||||
let globalMM = Services.mm;
|
||||
globalMM.loadFrameScript(CHILD_SCRIPT, true);
|
||||
globalMM.addMessageListener(MSG_INSTALL_ENABLED, this);
|
||||
globalMM.addMessageListener(MSG_INSTALL_ADDONS, this);
|
||||
|
||||
gParentMM = Services.ppmm;
|
||||
gParentMM.addMessageListener(MSG_PROMISE_REQUEST, this);
|
||||
gParentMM.addMessageListener(MSG_INSTALL_CLEANUP, this);
|
||||
gParentMM.addMessageListener(MSG_ADDON_EVENT_REQ, this);
|
||||
Services.mm.loadFrameScript(CHILD_SCRIPT, true);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_ADDONS, this);
|
||||
Services.mm.addMessageListener(MSG_PROMISE_REQUEST, this);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_CLEANUP, this);
|
||||
Services.mm.addMessageListener(MSG_ADDON_EVENT_REQ, this);
|
||||
|
||||
Services.obs.addObserver(this, "message-manager-close", false);
|
||||
Services.obs.addObserver(this, "message-manager-disconnect", false);
|
||||
|
@ -230,14 +224,15 @@ amManager.prototype = {
|
|||
}
|
||||
|
||||
case MSG_PROMISE_REQUEST: {
|
||||
let mm = aMessage.target.messageManager;
|
||||
let resolve = (value) => {
|
||||
aMessage.target.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
callbackID: payload.callbackID,
|
||||
resolve: value
|
||||
});
|
||||
}
|
||||
let reject = (value) => {
|
||||
aMessage.target.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
callbackID: payload.callbackID,
|
||||
reject: value
|
||||
});
|
||||
|
@ -259,7 +254,7 @@ amManager.prototype = {
|
|||
}
|
||||
|
||||
case MSG_ADDON_EVENT_REQ: {
|
||||
let target = aMessage.target;
|
||||
let target = aMessage.target.messageManager;
|
||||
if (payload.enabled) {
|
||||
this._addAddonListener(target);
|
||||
} else {
|
||||
|
@ -275,8 +270,8 @@ amManager.prototype = {
|
|||
this._removeAddonListener(target);
|
||||
},
|
||||
|
||||
sendEvent(target, data) {
|
||||
target.sendAsyncMessage(MSG_INSTALL_EVENT, data);
|
||||
sendEvent(mm, data) {
|
||||
mm.sendAsyncMessage(MSG_INSTALL_EVENT, data);
|
||||
},
|
||||
|
||||
classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -17,20 +17,20 @@ const MSG_INSTALL_CLEANUP = "WebAPICleanup";
|
|||
const MSG_ADDON_EVENT_REQ = "WebAPIAddonEventRequest";
|
||||
const MSG_ADDON_EVENT = "WebAPIAddonEvent";
|
||||
|
||||
const APIBroker = {
|
||||
_nextID: 0,
|
||||
class APIBroker {
|
||||
constructor(mm) {
|
||||
this.mm = mm;
|
||||
|
||||
init() {
|
||||
this._promises = new Map();
|
||||
|
||||
// _installMap maps integer ids to DOM AddonInstall instances
|
||||
this._installMap = new Map();
|
||||
|
||||
Services.cpmm.addMessageListener(MSG_PROMISE_RESULT, this);
|
||||
Services.cpmm.addMessageListener(MSG_INSTALL_EVENT, this);
|
||||
this.mm.addMessageListener(MSG_PROMISE_RESULT, this);
|
||||
this.mm.addMessageListener(MSG_INSTALL_EVENT, this);
|
||||
|
||||
this._eventListener = null;
|
||||
},
|
||||
}
|
||||
|
||||
receiveMessage(message) {
|
||||
let payload = message.data;
|
||||
|
@ -64,86 +64,73 @@ const APIBroker = {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
sendRequest: function(type, ...args) {
|
||||
sendRequest(type, ...args) {
|
||||
return new Promise(resolve => {
|
||||
let callbackID = this._nextID++;
|
||||
let callbackID = APIBroker._nextID++;
|
||||
|
||||
this._promises.set(callbackID, resolve);
|
||||
Services.cpmm.sendAsyncMessage(MSG_PROMISE_REQUEST, { type, callbackID, args });
|
||||
this.mm.sendAsyncMessage(MSG_PROMISE_REQUEST, { type, callbackID, args });
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
setAddonListener(callback) {
|
||||
this._eventListener = callback;
|
||||
if (callback) {
|
||||
Services.cpmm.addMessageListener(MSG_ADDON_EVENT, this);
|
||||
Services.cpmm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: true});
|
||||
this.mm.addMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: true});
|
||||
} else {
|
||||
Services.cpmm.removeMessageListener(MSG_ADDON_EVENT, this);
|
||||
Services.cpmm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: false});
|
||||
this.mm.removeMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: false});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
sendCleanup: function(ids) {
|
||||
sendCleanup(ids) {
|
||||
this.setAddonListener(null);
|
||||
Services.cpmm.sendAsyncMessage(MSG_INSTALL_CLEANUP, { ids });
|
||||
},
|
||||
};
|
||||
|
||||
APIBroker.init();
|
||||
|
||||
function Addon(window, properties) {
|
||||
this.window = window;
|
||||
|
||||
// We trust the webidl binding to broker access to our properties.
|
||||
for (let key of Object.keys(properties)) {
|
||||
this[key] = properties[key];
|
||||
this.mm.sendAsyncMessage(MSG_INSTALL_CLEANUP, { ids });
|
||||
}
|
||||
}
|
||||
|
||||
function AddonInstall(window, properties) {
|
||||
let id = properties.id;
|
||||
APIBroker._installMap.set(id, this);
|
||||
APIBroker._nextID = 0;
|
||||
|
||||
this.window = window;
|
||||
this.handlers = new Map();
|
||||
// Base class for building classes to back content-exposed interfaces.
|
||||
class APIObject {
|
||||
init(window, broker, properties) {
|
||||
this.window = window;
|
||||
this.broker = broker;
|
||||
|
||||
for (let key of Object.keys(properties)) {
|
||||
this[key] = properties[key];
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API methods all return promises from content. They also follow a
|
||||
* similar pattern of sending a request to the parent process, then
|
||||
* wrapping the returned object or error appropriately for the page.
|
||||
* We must take care only to wrap and reject with errors that are meant
|
||||
* to be visible to content, and not internal errors.
|
||||
* This function is a wrapper to handle the common bits.
|
||||
*
|
||||
* apiRequest is the name of the command to invoke in the parent process
|
||||
* apiArgs is a callable that takes the content-provided args for the
|
||||
* method and returns the arguments to send in the request to
|
||||
* the parent process.
|
||||
* if processor is non-null, it is called on the returned object to
|
||||
* convert the result from the parent process back to an
|
||||
* object appropriate for content.
|
||||
*
|
||||
* Both apiArgs and processor are called with "this" bound to the value
|
||||
* that is held when the actual api method was called.
|
||||
*/
|
||||
function WebAPITask(apiRequest, apiArgs, processor) {
|
||||
return function(...args) {
|
||||
/**
|
||||
* 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 boundApiArgs = apiArgs.bind(this);
|
||||
let boundProcessor = processor ? processor.bind(this) : null;
|
||||
|
||||
let broker = this.broker;
|
||||
return new win.Promise((resolve, reject) => {
|
||||
Task.spawn(function* () {
|
||||
let sendArgs = boundApiArgs(...args);
|
||||
let result = yield APIBroker.sendRequest(apiRequest, ...sendArgs);
|
||||
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
|
||||
|
@ -154,8 +141,8 @@ function WebAPITask(apiRequest, apiArgs, processor) {
|
|||
}
|
||||
|
||||
let obj = result.resolve;
|
||||
if (boundProcessor) {
|
||||
obj = boundProcessor(obj);
|
||||
if (resultConverter) {
|
||||
obj = resultConverter(obj);
|
||||
}
|
||||
resolve(obj);
|
||||
}).catch(err => {
|
||||
|
@ -166,24 +153,29 @@ function WebAPITask(apiRequest, apiArgs, processor) {
|
|||
}
|
||||
}
|
||||
|
||||
Addon.prototype = {
|
||||
uninstall: WebAPITask("addonUninstall", function() { return [this.id]; }),
|
||||
setEnabled: WebAPITask("addonSetEnabled", function(value) { return [this.id, value]; }),
|
||||
};
|
||||
class Addon extends APIObject {
|
||||
constructor(...args) {
|
||||
super();
|
||||
this.init(...args);
|
||||
}
|
||||
|
||||
const INSTALL_EVENTS = [
|
||||
"onDownloadStarted",
|
||||
"onDownloadProgress",
|
||||
"onDownloadEnded",
|
||||
"onDownloadCancelled",
|
||||
"onDownloadFailed",
|
||||
"onInstallStarted",
|
||||
"onInstallEnded",
|
||||
"onInstallCancelled",
|
||||
"onInstallFailed",
|
||||
];
|
||||
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);
|
||||
}
|
||||
|
||||
AddonInstall.prototype = {
|
||||
_dispatch(data) {
|
||||
// The message for the event includes updated copies of all install
|
||||
// properties. Use the usual "let webidl filter visible properties" trick.
|
||||
|
@ -193,63 +185,85 @@ AddonInstall.prototype = {
|
|||
|
||||
let event = new this.window.Event(data.event);
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
}
|
||||
|
||||
install: WebAPITask("addonInstallDoInstall", function() { return [this.id]; }),
|
||||
cancel: WebAPITask("addonInstallCancel", function() { return [this.id]; }),
|
||||
};
|
||||
install() {
|
||||
return this._apiTask("addonInstallDoInstall", [this.id]);
|
||||
}
|
||||
|
||||
function WebAPI() {
|
||||
cancel() {
|
||||
return this._apiTask("addonInstallCancel", [this.id]);
|
||||
}
|
||||
}
|
||||
|
||||
WebAPI.prototype = {
|
||||
init(window) {
|
||||
this.window = window;
|
||||
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 => {
|
||||
APIBroker.sendCleanup(this.allInstalls);
|
||||
this.broker.sendCleanup(this.allInstalls);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
getAddonByID: WebAPITask("getAddonByID", id => [id], function(addonInfo) {
|
||||
if (!addonInfo) {
|
||||
return null;
|
||||
}
|
||||
let addon = new Addon(this.window, addonInfo);
|
||||
return this.window.Addon._create(this.window, addon);
|
||||
}),
|
||||
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: WebAPITask("createInstall", options => [options], function(installInfo) {
|
||||
if (!installInfo) {
|
||||
return null;
|
||||
}
|
||||
let install = new AddonInstall(this.window, installInfo);
|
||||
this.allInstalls.push(installInfo.id);
|
||||
return this.window.AddonInstall._create(this.window, install);
|
||||
}),
|
||||
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) {
|
||||
APIBroker.setAddonListener(data => {
|
||||
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) {
|
||||
APIBroker.setAddonListener(null);
|
||||
this.broker.setAddonListener(null);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
classID: Components.ID("{8866d8e3-4ea5-48b7-a891-13ba0ac15235}"),
|
||||
contractID: "@mozilla.org/addon-web-api/manager;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIDOMGlobalPropertyInitializer])
|
||||
};
|
||||
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]);
|
||||
|
|
|
@ -12,13 +12,13 @@ function waitForClear() {
|
|||
let listener = {
|
||||
receiveMessage: function(msg) {
|
||||
if (msg.name == MSG) {
|
||||
Services.ppmm.removeMessageListener(MSG, listener);
|
||||
Services.mm.removeMessageListener(MSG, listener);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Services.ppmm.addMessageListener(MSG, listener);
|
||||
Services.mm.addMessageListener(MSG, listener, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<p id="result"></p>
|
||||
<script type="text/javascript">
|
||||
|
|
Загрузка…
Ссылка в новой задаче