Files
tubestation/toolkit/mozapps/extensions/amInstallTrigger.js
Boris Zbarsky bb32f68fba Bug 1442360 part 1. Change InstallTrigger to not use the JavaScript-global-property category. r=kmag
test_bug609794.html was testing a behavior that the method before the current
method of attaching InstallTrigger to windows depended on.  We don't really
need that behavior, which is good, because this change is not producing it.

MozReview-Commit-ID: GPzif89UYYl
2018-03-21 23:18:51 -04:00

225 lines
7.1 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";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Preferences.jsm");
ChromeUtils.import("resource://gre/modules/Log.jsm");
const XPINSTALL_MIMETYPE = "application/x-xpinstall";
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
var log = Log.repository.getLogger("AddonManager.InstallTrigger");
log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"];
function CallbackObject(id, callback, mediator) {
this.id = id;
this.callback = callback;
this.callCallback = function(url, status) {
try {
this.callback(url, status);
} catch (e) {
log.warn("InstallTrigger callback threw an exception: " + e);
}
mediator._callbacks.delete(id);
};
}
function RemoteMediator(window) {
window.QueryInterface(Ci.nsIInterfaceRequestor);
let utils = window.getInterface(Ci.nsIDOMWindowUtils);
this._windowID = utils.currentInnerWindowID;
this.mm = window
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
this.mm.addWeakMessageListener(MSG_INSTALL_CALLBACK, this);
this._lastCallbackID = 0;
this._callbacks = new Map();
}
RemoteMediator.prototype = {
receiveMessage(message) {
if (message.name == MSG_INSTALL_CALLBACK) {
let payload = message.data;
let callbackHandler = this._callbacks.get(payload.callbackID);
if (callbackHandler) {
callbackHandler.callCallback(payload.url, payload.status);
}
}
},
enabled(url) {
let params = {
mimetype: XPINSTALL_MIMETYPE
};
return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0];
},
install(install, principal, callback, window) {
let callbackID = this._addCallback(callback);
install.mimetype = XPINSTALL_MIMETYPE;
install.triggeringPrincipal = principal;
install.callbackID = callbackID;
if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
// When running in the main process this might be a frame inside an
// in-content UI page, walk up to find the first frame element in a chrome
// privileged document
let element = window.frameElement;
let ssm = Services.scriptSecurityManager;
while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
element = element.ownerGlobal.frameElement;
if (element) {
let listener = Cc["@mozilla.org/addons/integration;1"].
getService(Ci.nsIMessageListener);
return listener.wrappedJSObject.receiveMessage({
name: MSG_INSTALL_ADDON,
target: element,
data: install,
});
}
}
// Fall back to sending through the message manager
let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
return messageManager.sendSyncMessage(MSG_INSTALL_ADDON, install)[0];
},
_addCallback(callback) {
if (!callback || typeof callback != "function")
return -1;
let callbackID = this._windowID + "-" + ++this._lastCallbackID;
let callbackObject = new CallbackObject(callbackID, callback, this);
this._callbacks.set(callbackID, callbackObject);
return callbackID;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference])
};
function InstallTrigger() {
}
InstallTrigger.prototype = {
// We've declared ourselves as providing the nsIDOMGlobalPropertyInitializer
// interface. This means that when the InstallTrigger property is gotten from
// the window that will createInstance this object and then call init(),
// passing the window were bound to. It will then automatically create the
// WebIDL wrapper (InstallTriggerImpl) for this object. This indirection is
// necessary because webidl does not (yet) support statics (bug 863952). See
// bug 926712 and then bug 1442360 for more details about this implementation.
init(window) {
this._window = window;
this._principal = window.document.nodePrincipal;
this._url = window.document.documentURIObject;
this._mediator = new RemoteMediator(window);
// If we can't set up IPC (e.g., because this is a top-level window or
// something), then don't expose InstallTrigger. The Window code handles
// that, if we throw an exception here.
},
enabled() {
return this._mediator.enabled(this._url.spec);
},
updateEnabled() {
return this.enabled();
},
install(installs, callback) {
let keys = Object.keys(installs);
if (keys.length > 1) {
throw new this._window.Error("Only one XPI may be installed at a time");
}
let item = installs[keys[0]];
if (typeof item === "string") {
item = { URL: item };
}
if (!item.URL) {
throw new this._window.Error("Missing URL property for '" + name + "'");
}
let url = this._resolveURL(item.URL);
if (!this._checkLoadURIFromScript(url)) {
throw new this._window.Error("Insufficient permissions to install: " + url.spec);
}
let iconUrl = null;
if (item.IconURL) {
iconUrl = this._resolveURL(item.IconURL);
if (!this._checkLoadURIFromScript(iconUrl)) {
iconUrl = null; // If page can't load the icon, just ignore it
}
}
let installData = {
uri: url.spec,
hash: item.Hash || null,
name: item.name,
icon: iconUrl ? iconUrl.spec : null,
};
return this._mediator.install(installData, this._principal, callback, this._window);
},
startSoftwareUpdate(url, flags) {
let filename = Services.io.newURI(url)
.QueryInterface(Ci.nsIURL)
.filename;
let args = {};
args[filename] = { "URL": url };
return this.install(args);
},
installChrome(type, url, skin) {
return this.startSoftwareUpdate(url);
},
_resolveURL(url) {
return Services.io.newURI(url, null, this._url);
},
_checkLoadURIFromScript(uri) {
let secman = Services.scriptSecurityManager;
try {
secman.checkLoadURIWithPrincipal(this._principal,
uri,
secman.DISALLOW_INHERIT_PRINCIPAL);
return true;
} catch (e) {
return false;
}
},
classID: Components.ID("{9df8ef2b-94da-45c9-ab9f-132eb55fddf1}"),
contractID: "@mozilla.org/addons/installtrigger;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIDOMGlobalPropertyInitializer])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InstallTrigger]);