1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 * This component serves as integration between the platform and AddonManager.
7 * It is responsible for initializing and shutting down the AddonManager as well
8 * as passing new installs from webpages to the AddonManager.
11 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
14 XPCOMUtils.defineLazyPreferenceGetter(
16 "separatePrivilegedMozillaWebContentProcess",
17 "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
20 XPCOMUtils.defineLazyPreferenceGetter(
22 "extensionsWebAPITesting",
23 "extensions.webapi.testing",
27 // The old XPInstall error codes
28 const EXECUTION_ERROR = -203;
29 const CANT_READ_ARCHIVE = -207;
30 const USER_CANCELLED = -210;
31 const DOWNLOAD_ERROR = -228;
32 const UNSUPPORTED_TYPE = -244;
35 const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
36 const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
37 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
39 const MSG_PROMISE_REQUEST = "WebAPIPromiseRequest";
40 const MSG_PROMISE_RESULT = "WebAPIPromiseResult";
41 const MSG_INSTALL_EVENT = "WebAPIInstallEvent";
42 const MSG_INSTALL_CLEANUP = "WebAPICleanup";
43 const MSG_ADDON_EVENT_REQ = "WebAPIAddonEventRequest";
44 const MSG_ADDON_EVENT = "WebAPIAddonEvent";
46 var AddonManager, AddonManagerPrivate;
48 export function amManager() {
49 ({ AddonManager, AddonManagerPrivate } = ChromeUtils.importESModule(
50 "resource://gre/modules/AddonManager.sys.mjs"
53 Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
54 Services.mm.addMessageListener(MSG_PROMISE_REQUEST, this);
55 Services.mm.addMessageListener(MSG_INSTALL_CLEANUP, this);
56 Services.mm.addMessageListener(MSG_ADDON_EVENT_REQ, this);
58 Services.ppmm.addMessageListener(MSG_INSTALL_ADDON, this);
60 Services.obs.addObserver(this, "message-manager-close");
61 Services.obs.addObserver(this, "message-manager-disconnect");
63 AddonManager.webAPI.setEventHandler(this.sendEvent);
65 // Needed so receiveMessage can be called directly by JS callers
66 this.wrappedJSObject = this;
69 amManager.prototype = {
70 observe(aSubject, aTopic, aData) {
72 case "addons-startup":
73 AddonManagerPrivate.startup();
76 case "message-manager-close":
77 case "message-manager-disconnect":
78 this.childClosed(aSubject);
83 installAddonFromWebpage(aPayload, aBrowser, aCallback) {
86 const { mimetype, triggeringPrincipal, hash, icon, name, uri } = aPayload;
88 // NOTE: consider removing this call to isInstallAllowed from here, later it is going to be called
89 // again from inside AddonManager.installAddonFromWebpage as part of the block/allow logic.
91 // The sole purpose of the call here seems to be "clearing the optional InstallTrigger callback",
92 // which seems to be actually wrong if we are still proceeding to call getInstallForURL and the same
93 // logic used to block the install flow using the exact same method call later on.
94 if (!AddonManager.isInstallAllowed(mimetype, triggeringPrincipal)) {
100 source: AddonManager.getInstallSourceFromHost(aPayload.sourceHost),
101 sourceURL: aPayload.sourceURL,
104 if ("method" in aPayload) {
105 telemetryInfo.method = aPayload.method;
108 AddonManager.getInstallForURL(uri, {
116 }).then(aInstall => {
117 function callCallback(status) {
119 aCallback?.onInstallEnded(uri, status);
126 callCallback(UNSUPPORTED_TYPE);
131 aInstall.addListener({
132 onDownloadCancelled(aInstall) {
133 callCallback(USER_CANCELLED);
136 onDownloadFailed(aInstall) {
137 if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE) {
138 callCallback(CANT_READ_ARCHIVE);
140 callCallback(DOWNLOAD_ERROR);
144 onInstallFailed(aInstall) {
145 callCallback(EXECUTION_ERROR);
148 onInstallEnded(aInstall, aStatus) {
149 callCallback(SUCCESS);
154 AddonManager.installAddonFromWebpage(
160 hasCrossOriginAncestor: aPayload.hasCrossOriginAncestor,
169 AddonManagerPrivate.backgroundUpdateTimerHandler();
172 // Maps message manager instances for content processes to the associated
173 // AddonListener instances.
174 addonListeners: new Map(),
176 _addAddonListener(target) {
177 if (!this.addonListeners.has(target)) {
178 let handler = (event, id) => {
179 target.sendAsyncMessage(MSG_ADDON_EVENT, { event, id });
182 onEnabling: addon => handler("onEnabling", addon.id),
183 onEnabled: addon => handler("onEnabled", addon.id),
184 onDisabling: addon => handler("onDisabling", addon.id),
185 onDisabled: addon => handler("onDisabled", addon.id),
186 onInstalling: addon => handler("onInstalling", addon.id),
187 onInstalled: addon => handler("onInstalled", addon.id),
188 onUninstalling: addon => handler("onUninstalling", addon.id),
189 onUninstalled: addon => handler("onUninstalled", addon.id),
190 onOperationCancelled: addon =>
191 handler("onOperationCancelled", addon.id),
193 this.addonListeners.set(target, listener);
194 AddonManager.addAddonListener(listener);
198 _removeAddonListener(target) {
199 if (this.addonListeners.has(target)) {
200 AddonManager.removeAddonListener(this.addonListeners.get(target));
201 this.addonListeners.delete(target);
206 * messageManager callback function.
208 * Listens to requests from child processes for InstallTrigger
209 * activity, and sends back callbacks.
211 receiveMessage(aMessage) {
212 let payload = aMessage.data;
214 switch (aMessage.name) {
215 case MSG_INSTALL_ENABLED:
216 return AddonManager.isInstallEnabled(payload.mimetype);
218 case MSG_INSTALL_ADDON: {
219 let browser = payload.browsingContext.top.embedderElement;
222 if (payload.callbackID != -1) {
223 let mm = browser.messageManager;
225 onInstallEnded(url, status) {
226 mm.sendAsyncMessage(MSG_INSTALL_CALLBACK, {
227 callbackID: payload.callbackID,
235 return this.installAddonFromWebpage(payload, browser, callback);
238 case MSG_PROMISE_REQUEST: {
240 !lazy.extensionsWebAPITesting &&
241 lazy.separatePrivilegedMozillaWebContentProcess &&
243 aMessage.target.remoteType != null &&
244 aMessage.target.remoteType !== "privilegedmozilla"
249 let mm = aMessage.target.messageManager;
250 let resolve = value => {
251 mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
252 callbackID: payload.callbackID,
256 let reject = value => {
257 mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
258 callbackID: payload.callbackID,
263 let API = AddonManager.webAPI;
264 if (payload.type in API) {
265 API[payload.type](aMessage.target, ...payload.args).then(
270 reject("Unknown Add-on API request.");
275 case MSG_INSTALL_CLEANUP: {
277 !lazy.extensionsWebAPITesting &&
278 lazy.separatePrivilegedMozillaWebContentProcess &&
280 aMessage.target.remoteType != null &&
281 aMessage.target.remoteType !== "privilegedmozilla"
286 AddonManager.webAPI.clearInstalls(payload.ids);
290 case MSG_ADDON_EVENT_REQ: {
292 !lazy.extensionsWebAPITesting &&
293 lazy.separatePrivilegedMozillaWebContentProcess &&
295 aMessage.target.remoteType != null &&
296 aMessage.target.remoteType !== "privilegedmozilla"
301 let target = aMessage.target.messageManager;
302 if (payload.enabled) {
303 this._addAddonListener(target);
305 this._removeAddonListener(target);
312 childClosed(target) {
313 AddonManager.webAPI.clearInstallsFrom(target);
314 this._removeAddonListener(target);
317 sendEvent(mm, data) {
318 mm.sendAsyncMessage(MSG_INSTALL_EVENT, data);
321 classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
322 QueryInterface: ChromeUtils.generateQI([
329 const BLOCKLIST_SYS_MJS = "resource://gre/modules/Blocklist.sys.mjs";
330 ChromeUtils.defineESModuleGetters(lazy, { Blocklist: BLOCKLIST_SYS_MJS });
332 export function BlocklistService() {
333 this.wrappedJSObject = this;
336 BlocklistService.prototype = {
337 STATE_NOT_BLOCKED: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
338 STATE_SOFTBLOCKED: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
339 STATE_BLOCKED: Ci.nsIBlocklistService.STATE_BLOCKED,
342 return Cu.isESModuleLoaded(BLOCKLIST_SYS_MJS) && lazy.Blocklist.isLoaded;
346 return lazy.Blocklist.observe(...args);
350 lazy.Blocklist.notify();
353 classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
354 QueryInterface: ChromeUtils.generateQI([
356 "nsIBlocklistService",