зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1323129 part 2: remove amIWebInstaller r=rhelmer
MozReview-Commit-ID: O0jtQi9BzQ --HG-- rename : toolkit/mozapps/extensions/amIWebInstallListener.idl => toolkit/mozapps/extensions/amIWebInstallPrompt.idl extra : rebase_source : 7d1981f282b5e2c34b8b720c0a96e2cc8b71a86f extra : source : 8a61b376d87f52d1bd660af41bdc6bdb5d5e44ae
This commit is contained in:
Родитель
090c727768
Коммит
c6e46bcd64
|
@ -449,7 +449,6 @@
|
|||
@RESPATH@/components/addonManager.js
|
||||
@RESPATH@/components/amContentHandler.js
|
||||
@RESPATH@/components/amInstallTrigger.js
|
||||
@RESPATH@/components/amWebInstallListener.js
|
||||
|
||||
@RESPATH@/components/OopCommandLine.js
|
||||
@RESPATH@/components/CommandLine.js
|
||||
|
|
|
@ -215,7 +215,7 @@ const gXPInstallObserver = {
|
|||
|
||||
observe(aSubject, aTopic, aData) {
|
||||
var brandBundle = document.getElementById("bundle_brand");
|
||||
var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
|
||||
var installInfo = aSubject.wrappedJSObject;
|
||||
var browser = installInfo.browser;
|
||||
|
||||
// Make sure the browser is still alive.
|
||||
|
|
|
@ -412,7 +412,6 @@
|
|||
@RESPATH@/components/amContentHandler.js
|
||||
@RESPATH@/components/amInstallTrigger.js
|
||||
@RESPATH@/components/amWebAPI.js
|
||||
@RESPATH@/components/amWebInstallListener.js
|
||||
@RESPATH@/components/nsBlocklistService.js
|
||||
@RESPATH@/components/nsBlocklistServiceContent.js
|
||||
#ifdef MOZ_UPDATER
|
||||
|
|
|
@ -303,7 +303,6 @@
|
|||
@BINPATH@/components/amContentHandler.js
|
||||
@BINPATH@/components/amInstallTrigger.js
|
||||
@BINPATH@/components/amWebAPI.js
|
||||
@BINPATH@/components/amWebInstallListener.js
|
||||
@BINPATH@/components/nsBlocklistService.js
|
||||
#ifndef RELEASE_OR_BETA
|
||||
@BINPATH@/components/TabSource.js
|
||||
|
|
|
@ -89,6 +89,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "Extension",
|
|||
"resource://gre/modules/Extension.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromptUtils",
|
||||
"resource://gre/modules/SharedPromptUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
|
||||
let certUtils = {};
|
||||
|
@ -444,6 +448,257 @@ BrowserListener.prototype = {
|
|||
Ci.nsIObserver])
|
||||
};
|
||||
|
||||
function installNotifyObservers(aTopic, aBrowser, aUri, aInstalls, aInstallFn) {
|
||||
let info = {
|
||||
wrappedJSObject: {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
install: aInstallFn,
|
||||
},
|
||||
};
|
||||
Services.obs.notifyObservers(info, aTopic, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new installer to monitor downloads and prompt to install when
|
||||
* ready
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that started the installations
|
||||
* @param aUrl
|
||||
* The URL that started the installations
|
||||
* @param aInstalls
|
||||
* An array of AddonInstalls
|
||||
*/
|
||||
function Installer(aBrowser, aUrl, aInstalls) {
|
||||
this.browser = aBrowser;
|
||||
this.url = aUrl;
|
||||
this.downloads = aInstalls;
|
||||
this.installed = [];
|
||||
|
||||
installNotifyObservers("addon-install-started", aBrowser, aUrl, aInstalls);
|
||||
|
||||
const READY_STATES = [
|
||||
AddonManager.STATE_AVAILABLE,
|
||||
AddonManager.STATE_DOWNLOAD_FAILED,
|
||||
AddonManager.STATE_INSTALL_FAILED,
|
||||
AddonManager.STATE_CANCELLED,
|
||||
];
|
||||
for (let install of aInstalls) {
|
||||
install.addListener(this);
|
||||
|
||||
// Start downloading if it hasn't already begun
|
||||
if (READY_STATES.indexOf(install.state) != -1)
|
||||
install.install();
|
||||
}
|
||||
|
||||
this.checkAllDownloaded();
|
||||
}
|
||||
|
||||
Installer.prototype = {
|
||||
URI_XPINSTALL_DIALOG: "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul",
|
||||
browser: null,
|
||||
downloads: null,
|
||||
installed: null,
|
||||
isDownloading: true,
|
||||
|
||||
/**
|
||||
* Checks if all downloads are now complete and if so prompts to install.
|
||||
*/
|
||||
checkAllDownloaded: function() {
|
||||
// Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
|
||||
// installs.
|
||||
if (!this.isDownloading)
|
||||
return;
|
||||
|
||||
var failed = [];
|
||||
var installs = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_AVAILABLE:
|
||||
case AddonManager.STATE_DOWNLOADING:
|
||||
// Exit early if any add-ons haven't started downloading yet or are
|
||||
// still downloading
|
||||
return;
|
||||
case AddonManager.STATE_DOWNLOAD_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
// App disabled items are not compatible and so fail to install
|
||||
if (install.addon.appDisabled)
|
||||
failed.push(install);
|
||||
else
|
||||
installs.push(install);
|
||||
break;
|
||||
case AddonManager.STATE_CANCELLED:
|
||||
// Just ignore cancelled downloads
|
||||
break;
|
||||
default:
|
||||
logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
|
||||
install.state);
|
||||
}
|
||||
}
|
||||
|
||||
this.isDownloading = false;
|
||||
this.downloads = installs;
|
||||
|
||||
if (failed.length > 0) {
|
||||
// Stop listening and cancel any installs that are failed because of
|
||||
// compatibility reasons.
|
||||
for (let install of failed) {
|
||||
if (install.state == AddonManager.STATE_DOWNLOADED) {
|
||||
install.removeListener(this);
|
||||
install.cancel();
|
||||
}
|
||||
}
|
||||
installNotifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
}
|
||||
|
||||
// If none of the downloads were successful then exit early
|
||||
if (this.downloads.length == 0)
|
||||
return;
|
||||
|
||||
// Check for a custom installation prompt that may be provided by the
|
||||
// applicaton
|
||||
if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
|
||||
try {
|
||||
let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
|
||||
getService(Ci.amIWebInstallPrompt);
|
||||
prompt.confirm(this.browser, this.url, this.downloads, this.downloads.length);
|
||||
return;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
|
||||
installNotifyObservers("addon-install-confirmation", this.browser, this.url, this.downloads);
|
||||
return;
|
||||
}
|
||||
|
||||
let args = {};
|
||||
args.url = this.url;
|
||||
args.installs = this.downloads;
|
||||
args.wrappedJSObject = args;
|
||||
|
||||
try {
|
||||
Cc["@mozilla.org/base/telemetry;1"].
|
||||
getService(Ci.nsITelemetry).
|
||||
getHistogramById("SECURITY_UI").
|
||||
add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
let parentWindow = null;
|
||||
if (this.browser) {
|
||||
parentWindow = this.browser.ownerDocument.defaultView;
|
||||
PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
|
||||
}
|
||||
Services.ww.openWindow(parentWindow, this.URI_XPINSTALL_DIALOG,
|
||||
null, "chrome,modal,centerscreen", args);
|
||||
} catch (e) {
|
||||
logger.warn("Exception showing install confirmation dialog", e);
|
||||
for (let install of this.downloads) {
|
||||
install.removeListener(this);
|
||||
// Cancel the installs, as currently there is no way to make them fail
|
||||
// from here.
|
||||
install.cancel();
|
||||
}
|
||||
installNotifyObservers("addon-install-cancelled", this.browser, this.url,
|
||||
this.downloads);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if all installs are now complete and if so notifies observers.
|
||||
*/
|
||||
checkAllInstalled: function() {
|
||||
var failed = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
case AddonManager.STATE_INSTALLING:
|
||||
// Exit early if any add-ons haven't started installing yet or are
|
||||
// still installing
|
||||
return;
|
||||
case AddonManager.STATE_INSTALL_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.downloads = null;
|
||||
|
||||
if (failed.length > 0)
|
||||
installNotifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
|
||||
if (this.installed.length > 0)
|
||||
installNotifyObservers("addon-install-complete", this.browser, this.url, this.installed);
|
||||
this.installed = null;
|
||||
},
|
||||
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadFailed: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadEnded: function(aInstall) {
|
||||
this.checkAllDownloaded();
|
||||
return false;
|
||||
},
|
||||
|
||||
onInstallCancelled: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallFailed: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.installed.push(aInstall);
|
||||
|
||||
// If installing a theme that is disabled and can be enabled then enable it
|
||||
if (aInstall.addon.type == "theme" &&
|
||||
aInstall.addon.userDisabled == true &&
|
||||
aInstall.addon.appDisabled == false) {
|
||||
aInstall.addon.userDisabled = false;
|
||||
}
|
||||
|
||||
this.checkAllInstalled();
|
||||
}
|
||||
};
|
||||
|
||||
const weblistener = {
|
||||
onWebInstallDisabled: function(aBrowser, aUri, aInstalls) {
|
||||
installNotifyObservers("addon-install-disabled", aBrowser, aUri, aInstalls);
|
||||
},
|
||||
|
||||
onWebInstallOriginBlocked: function(aBrowser, aUri, aInstalls) {
|
||||
installNotifyObservers("addon-install-origin-blocked", aBrowser, aUri, aInstalls);
|
||||
return false;
|
||||
},
|
||||
|
||||
onWebInstallBlocked: function(aBrowser, aUri, aInstalls) {
|
||||
installNotifyObservers("addon-install-blocked", aBrowser, aUri, aInstalls,
|
||||
function() { new Installer(this.browser, this.originatingURI, this.installs); });
|
||||
return false;
|
||||
},
|
||||
|
||||
onWebInstallRequested: function(aBrowser, aUri, aInstalls) {
|
||||
new Installer(aBrowser, aUri, aInstalls);
|
||||
|
||||
// We start the installs ourself
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This represents an author of an add-on (e.g. creator or developer)
|
||||
*
|
||||
|
@ -2045,12 +2300,6 @@ var AddonManagerInternal = {
|
|||
throw Components.Exception("aInstallingPrincipal must be a nsIPrincipal",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
|
||||
logger.warn("No web installer available, cancelling install");
|
||||
aInstall.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// When a chrome in-content UI has loaded a <browser> inside to host a
|
||||
// website we want to do our security checks on the inner-browser but
|
||||
// notify front-end that install events came from the outer-browser (the
|
||||
|
@ -2065,9 +2314,6 @@ var AddonManagerInternal = {
|
|||
topBrowser = docShell.chromeEventHandler;
|
||||
|
||||
try {
|
||||
let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
|
||||
getService(Ci.amIWebInstallListener);
|
||||
|
||||
if (!this.isInstallEnabled(aMimetype)) {
|
||||
aInstall.cancel();
|
||||
|
||||
|
@ -2077,10 +2323,8 @@ var AddonManagerInternal = {
|
|||
} else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
|
||||
aInstall.cancel();
|
||||
|
||||
if (weblistener instanceof Ci.amIWebInstallListener2) {
|
||||
weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
|
||||
[aInstall], 1);
|
||||
}
|
||||
weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
|
||||
[aInstall], 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2107,6 +2351,25 @@ var AddonManagerInternal = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts installation of an AddonInstall created from add-ons manager
|
||||
* front-end code (e.g., drag-and-drop of xpis or "Install Add-on from File"
|
||||
*
|
||||
* @param browser
|
||||
* The browser element where the installation was initiated
|
||||
* @param uri
|
||||
* The URI of the page where the installation was initiated
|
||||
* @param install
|
||||
* The AddonInstall to be installed
|
||||
*/
|
||||
installAddonFromAOM(browser, uri, install) {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
weblistener.onWebInstallRequested(browser, uri, [install]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new InstallListener if the listener is not already registered.
|
||||
*
|
||||
|
@ -3409,6 +3672,10 @@ this.AddonManager = {
|
|||
aInstall);
|
||||
},
|
||||
|
||||
installAddonFromAOM(aBrowser, aUri, aInstall) {
|
||||
AddonManagerInternal.installAddonFromAOM(aBrowser, aUri, aInstall);
|
||||
},
|
||||
|
||||
installTemporaryAddon(aDirectory) {
|
||||
return AddonManagerInternal.installTemporaryAddon(aDirectory);
|
||||
},
|
||||
|
|
|
@ -1,134 +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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMElement;
|
||||
interface nsIURI;
|
||||
interface nsIVariant;
|
||||
|
||||
/**
|
||||
* amIWebInstallInfo is used by the default implementation of
|
||||
* amIWebInstallListener to communicate with the running application and allow
|
||||
* it to warn the user about blocked installs and start the installs running.
|
||||
*/
|
||||
[scriptable, uuid(fa0b47a3-f819-47ac-bc66-4bd1d7f67b1d)]
|
||||
interface amIWebInstallInfo : nsISupports
|
||||
{
|
||||
readonly attribute nsIDOMElement browser;
|
||||
readonly attribute nsIURI originatingURI;
|
||||
readonly attribute nsIVariant installs;
|
||||
|
||||
/**
|
||||
* Starts all installs.
|
||||
*/
|
||||
void install();
|
||||
};
|
||||
|
||||
/**
|
||||
* The registered amIWebInstallListener is used to notify about new installs
|
||||
* triggered by websites. The default implementation displays a confirmation
|
||||
* dialog when add-ons are ready to install and uses the observer service to
|
||||
* notify when installations are blocked.
|
||||
*/
|
||||
[scriptable, uuid(d9240d4b-6b3a-4cad-b402-de6c93337e0c)]
|
||||
interface amIWebInstallListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when installation by websites is currently disabled.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
void onWebInstallDisabled(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
|
||||
/**
|
||||
* Called when the website is not allowed to directly prompt the user to
|
||||
* install add-ons.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
* @return true if the caller should start the installs
|
||||
*/
|
||||
boolean onWebInstallBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
|
||||
/**
|
||||
* Called when a website wants to ask the user to install add-ons.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were requested
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
* @return true if the caller should start the installs
|
||||
*/
|
||||
boolean onWebInstallRequested(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
||||
|
||||
[scriptable, uuid(a80b89ad-bb1a-4c43-9cb7-3ae656556f78)]
|
||||
interface amIWebInstallListener2 : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when a non-same-origin resource attempted to initiate an install.
|
||||
* Installs will have already been cancelled and cannot be restarted.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
boolean onWebInstallOriginBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
||||
|
||||
/**
|
||||
* amIWebInstallPrompt is used, if available, by the default implementation of
|
||||
* amIWebInstallInfo to display a confirmation UI to the user before running
|
||||
* installs.
|
||||
*/
|
||||
[scriptable, uuid(386906f1-4d18-45bf-bc81-5dcd68e42c3b)]
|
||||
interface amIWebInstallPrompt : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get a confirmation that the user wants to start the installs.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were requested
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
void confirm(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMElement;
|
||||
interface nsIURI;
|
||||
interface nsIVariant;
|
||||
|
||||
/**
|
||||
* amIWebInstallPrompt is used, if available, by the default implementation of
|
||||
* amIWebInstallInfo to display a confirmation UI to the user before running
|
||||
* installs.
|
||||
*/
|
||||
[scriptable, uuid(386906f1-4d18-45bf-bc81-5dcd68e42c3b)]
|
||||
interface amIWebInstallPrompt : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get a confirmation that the user wants to start the installs.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were requested
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
void confirm(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
|
@ -1,336 +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/. */
|
||||
|
||||
/**
|
||||
* This is a default implementation of amIWebInstallListener that should work
|
||||
* for most applications but can be overriden. It notifies the observer service
|
||||
* about blocked installs. For normal installs it pops up an install
|
||||
* confirmation when all the add-ons have been downloaded.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromptUtils", "resource://gre/modules/SharedPromptUtils.jsm");
|
||||
|
||||
const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
|
||||
|
||||
// Installation can begin from any of these states
|
||||
const READY_STATES = [
|
||||
AddonManager.STATE_AVAILABLE,
|
||||
AddonManager.STATE_DOWNLOAD_FAILED,
|
||||
AddonManager.STATE_INSTALL_FAILED,
|
||||
AddonManager.STATE_CANCELLED
|
||||
];
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
const LOGGER_ID = "addons.weblistener";
|
||||
|
||||
// Create a new logger for use by the Addons Web Listener
|
||||
// (Requires AddonManager.jsm)
|
||||
var logger = Log.repository.getLogger(LOGGER_ID);
|
||||
|
||||
function notifyObservers(aTopic, aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, aTopic, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new installer to monitor downloads and prompt to install when
|
||||
* ready
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that started the installations
|
||||
* @param aUrl
|
||||
* The URL that started the installations
|
||||
* @param aInstalls
|
||||
* An array of AddonInstalls
|
||||
*/
|
||||
function Installer(aBrowser, aUrl, aInstalls) {
|
||||
this.browser = aBrowser;
|
||||
this.url = aUrl;
|
||||
this.downloads = aInstalls;
|
||||
this.installed = [];
|
||||
|
||||
notifyObservers("addon-install-started", aBrowser, aUrl, aInstalls);
|
||||
|
||||
for (let install of aInstalls) {
|
||||
install.addListener(this);
|
||||
|
||||
// Start downloading if it hasn't already begun
|
||||
if (READY_STATES.indexOf(install.state) != -1)
|
||||
install.install();
|
||||
}
|
||||
|
||||
this.checkAllDownloaded();
|
||||
}
|
||||
|
||||
Installer.prototype = {
|
||||
browser: null,
|
||||
downloads: null,
|
||||
installed: null,
|
||||
isDownloading: true,
|
||||
|
||||
/**
|
||||
* Checks if all downloads are now complete and if so prompts to install.
|
||||
*/
|
||||
checkAllDownloaded() {
|
||||
// Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
|
||||
// installs.
|
||||
if (!this.isDownloading)
|
||||
return;
|
||||
|
||||
var failed = [];
|
||||
var installs = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_AVAILABLE:
|
||||
case AddonManager.STATE_DOWNLOADING:
|
||||
// Exit early if any add-ons haven't started downloading yet or are
|
||||
// still downloading
|
||||
return;
|
||||
case AddonManager.STATE_DOWNLOAD_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
// App disabled items are not compatible and so fail to install
|
||||
if (install.addon.appDisabled)
|
||||
failed.push(install);
|
||||
else
|
||||
installs.push(install);
|
||||
break;
|
||||
case AddonManager.STATE_CANCELLED:
|
||||
// Just ignore cancelled downloads
|
||||
break;
|
||||
default:
|
||||
logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
|
||||
install.state);
|
||||
}
|
||||
}
|
||||
|
||||
this.isDownloading = false;
|
||||
this.downloads = installs;
|
||||
|
||||
if (failed.length > 0) {
|
||||
// Stop listening and cancel any installs that are failed because of
|
||||
// compatibility reasons.
|
||||
for (let install of failed) {
|
||||
if (install.state == AddonManager.STATE_DOWNLOADED) {
|
||||
install.removeListener(this);
|
||||
install.cancel();
|
||||
}
|
||||
}
|
||||
notifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
}
|
||||
|
||||
// If none of the downloads were successful then exit early
|
||||
if (this.downloads.length == 0)
|
||||
return;
|
||||
|
||||
// Check for a custom installation prompt that may be provided by the
|
||||
// applicaton
|
||||
if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
|
||||
try {
|
||||
let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
|
||||
getService(Ci.amIWebInstallPrompt);
|
||||
prompt.confirm(this.browser, this.url, this.downloads, this.downloads.length);
|
||||
return;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
|
||||
notifyObservers("addon-install-confirmation", this.browser, this.url, this.downloads);
|
||||
return;
|
||||
}
|
||||
|
||||
let args = {};
|
||||
args.url = this.url;
|
||||
args.installs = this.downloads;
|
||||
args.wrappedJSObject = args;
|
||||
|
||||
try {
|
||||
Cc["@mozilla.org/base/telemetry;1"].
|
||||
getService(Ci.nsITelemetry).
|
||||
getHistogramById("SECURITY_UI").
|
||||
add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
let parentWindow = null;
|
||||
if (this.browser) {
|
||||
parentWindow = this.browser.ownerDocument.defaultView;
|
||||
PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
|
||||
}
|
||||
Services.ww.openWindow(parentWindow, URI_XPINSTALL_DIALOG,
|
||||
null, "chrome,modal,centerscreen", args);
|
||||
} catch (e) {
|
||||
logger.warn("Exception showing install confirmation dialog", e);
|
||||
for (let install of this.downloads) {
|
||||
install.removeListener(this);
|
||||
// Cancel the installs, as currently there is no way to make them fail
|
||||
// from here.
|
||||
install.cancel();
|
||||
}
|
||||
notifyObservers("addon-install-cancelled", this.browser, this.url,
|
||||
this.downloads);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if all installs are now complete and if so notifies observers.
|
||||
*/
|
||||
checkAllInstalled() {
|
||||
var failed = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
case AddonManager.STATE_INSTALLING:
|
||||
// Exit early if any add-ons haven't started installing yet or are
|
||||
// still installing
|
||||
return;
|
||||
case AddonManager.STATE_INSTALL_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.downloads = null;
|
||||
|
||||
if (failed.length > 0)
|
||||
notifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
|
||||
if (this.installed.length > 0)
|
||||
notifyObservers("addon-install-complete", this.browser, this.url, this.installed);
|
||||
this.installed = null;
|
||||
},
|
||||
|
||||
onDownloadCancelled(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadFailed(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadEnded(aInstall) {
|
||||
this.checkAllDownloaded();
|
||||
return false;
|
||||
},
|
||||
|
||||
onInstallCancelled(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallFailed(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallEnded(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.installed.push(aInstall);
|
||||
|
||||
// If installing a theme that is disabled and can be enabled then enable it
|
||||
if (aInstall.addon.type == "theme" &&
|
||||
aInstall.addon.userDisabled == true &&
|
||||
aInstall.addon.appDisabled == false) {
|
||||
aInstall.addon.userDisabled = false;
|
||||
}
|
||||
|
||||
this.checkAllInstalled();
|
||||
}
|
||||
};
|
||||
|
||||
function extWebInstallListener() {
|
||||
}
|
||||
|
||||
extWebInstallListener.prototype = {
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallDisabled(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-disabled", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallOriginBlocked(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
install() {
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-origin-blocked", null);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallBlocked(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
install() {
|
||||
new Installer(this.browser, this.originatingURI, this.installs);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-blocked", null);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallRequested(aBrowser, aUri, aInstalls) {
|
||||
new Installer(aBrowser, aUri, aInstalls);
|
||||
|
||||
// We start the installs ourself
|
||||
return false;
|
||||
},
|
||||
|
||||
classDescription: "XPI Install Handler",
|
||||
contractID: "@mozilla.org/addons/web-install-listener;1",
|
||||
classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener,
|
||||
Ci.amIWebInstallListener2])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([extWebInstallListener]);
|
|
@ -1345,30 +1345,14 @@ var gViewController = {
|
|||
if (fp.show() != nsIFilePicker.returnOK)
|
||||
return;
|
||||
|
||||
var files = fp.files;
|
||||
var installs = [];
|
||||
|
||||
function buildNextInstall() {
|
||||
if (!files.hasMoreElements()) {
|
||||
if (installs.length > 0) {
|
||||
// Display the normal install confirmation for the installs
|
||||
let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"].
|
||||
getService(Ci.amIWebInstallListener);
|
||||
webInstaller.onWebInstallRequested(getBrowserElement(),
|
||||
document.documentURIObject,
|
||||
installs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var file = files.getNext();
|
||||
AddonManager.getInstallForFile(file, function(aInstall) {
|
||||
installs.push(aInstall);
|
||||
buildNextInstall();
|
||||
let browser = getBrowserElement();
|
||||
let files = fp.files;
|
||||
while (files.hasMoreElements()) {
|
||||
let file = files.getNext();
|
||||
AddonManager.getInstallForFile(file, install => {
|
||||
AddonManager.installAddonFromAOM(browser, document.documentURI, install);
|
||||
});
|
||||
}
|
||||
|
||||
buildNextInstall();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3889,54 +3873,31 @@ var gDragDrop = {
|
|||
},
|
||||
|
||||
onDrop(aEvent) {
|
||||
var dataTransfer = aEvent.dataTransfer;
|
||||
var urls = [];
|
||||
let dataTransfer = aEvent.dataTransfer;
|
||||
let browser = getBrowserElement();
|
||||
|
||||
// Convert every dropped item into a url
|
||||
// Convert every dropped item into a url and install it
|
||||
for (var i = 0; i < dataTransfer.mozItemCount; i++) {
|
||||
var url = dataTransfer.mozGetDataAt("text/uri-list", i);
|
||||
let url = dataTransfer.mozGetDataAt("text/uri-list", i);
|
||||
if (!url) {
|
||||
url = dataTransfer.mozGetDataAt("text/x-moz-url", i);
|
||||
}
|
||||
if (url) {
|
||||
urls.push(url);
|
||||
continue;
|
||||
}
|
||||
|
||||
url = dataTransfer.mozGetDataAt("text/x-moz-url", i);
|
||||
if (url) {
|
||||
urls.push(url.split("\n")[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = dataTransfer.mozGetDataAt("application/x-moz-file", i);
|
||||
if (file) {
|
||||
urls.push(Services.io.newFileURI(file).spec);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var pos = 0;
|
||||
var installs = [];
|
||||
|
||||
function buildNextInstall() {
|
||||
if (pos == urls.length) {
|
||||
if (installs.length > 0) {
|
||||
// Display the normal install confirmation for the installs
|
||||
let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"].
|
||||
getService(Ci.amIWebInstallListener);
|
||||
webInstaller.onWebInstallRequested(getBrowserElement(),
|
||||
document.documentURIObject,
|
||||
installs);
|
||||
url = url.split("\n")[0];
|
||||
} else {
|
||||
let file = dataTransfer.mozGetDataAt("application/x-moz-file", i);
|
||||
if (file) {
|
||||
url = Services.io.newFileURI(file).spec;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.getInstallForURL(urls[pos++], function(aInstall) {
|
||||
installs.push(aInstall);
|
||||
buildNextInstall();
|
||||
}, "application/x-xpinstall");
|
||||
if (url) {
|
||||
AddonManager.getInstallForURL(url, install => {
|
||||
AddonManager.installAddonFromAOM(browser, document.documentURI, install);
|
||||
}, "application/x-xpinstall");
|
||||
}
|
||||
}
|
||||
|
||||
buildNextInstall();
|
||||
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,8 +13,6 @@ category update-timer addonManager @mozilla.org/addons/integration;1,getService,
|
|||
#endif
|
||||
component {7beb3ba8-6ec3-41b4-b67c-da89b8518922} amContentHandler.js
|
||||
contract @mozilla.org/uriloader/content-handler;1?type=application/x-xpinstall {7beb3ba8-6ec3-41b4-b67c-da89b8518922}
|
||||
component {0f38e086-89a3-40a5-8ffc-9b694de1d04a} amWebInstallListener.js
|
||||
contract @mozilla.org/addons/web-install-listener;1 {0f38e086-89a3-40a5-8ffc-9b694de1d04a}
|
||||
component {9df8ef2b-94da-45c9-ab9f-132eb55fddf1} amInstallTrigger.js
|
||||
contract @mozilla.org/addons/installtrigger;1 {9df8ef2b-94da-45c9-ab9f-132eb55fddf1}
|
||||
category JavaScript-global-property InstallTrigger @mozilla.org/addons/installtrigger;1
|
||||
|
|
|
@ -15,7 +15,7 @@ TEST_DIRS += ['test']
|
|||
XPIDL_SOURCES += [
|
||||
'amIAddonManager.idl',
|
||||
'amIAddonPathService.idl',
|
||||
'amIWebInstallListener.idl',
|
||||
'amIWebInstallPrompt.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'extensions'
|
||||
|
@ -25,7 +25,6 @@ EXTRA_COMPONENTS += [
|
|||
'amContentHandler.js',
|
||||
'amInstallTrigger.js',
|
||||
'amWebAPI.js',
|
||||
'amWebInstallListener.js',
|
||||
'nsBlocklistService.js',
|
||||
'nsBlocklistServiceContent.js',
|
||||
]
|
||||
|
|
|
@ -8,97 +8,80 @@ var MockFilePicker = SpecialPowers.MockFilePicker;
|
|||
MockFilePicker.init(window);
|
||||
|
||||
var gManagerWindow;
|
||||
var gSawInstallNotification = false;
|
||||
|
||||
// This listens for the next opened window and checks it is of the right url.
|
||||
// opencallback is called when the new window is fully loaded
|
||||
// closecallback is called when the window is closed
|
||||
function WindowOpenListener(url, opencallback, closecallback) {
|
||||
this.url = url;
|
||||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
function checkInstallConfirmation(...urls) {
|
||||
return new Promise(resolve => {
|
||||
let nurls = urls.length;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.addListener(this);
|
||||
}
|
||||
|
||||
WindowOpenListener.prototype = {
|
||||
url: null,
|
||||
opencallback: null,
|
||||
closecallback: null,
|
||||
window: null,
|
||||
domwindow: null,
|
||||
|
||||
handleEvent(event) {
|
||||
is(this.domwindow.document.location.href, this.url, "Should have opened the correct window");
|
||||
|
||||
this.domwindow.removeEventListener("load", this, false);
|
||||
// Allow any other load handlers to execute
|
||||
var self = this;
|
||||
executeSoon(function() { self.opencallback(self.domwindow); } );
|
||||
},
|
||||
|
||||
onWindowTitleChange(window, title) {
|
||||
},
|
||||
|
||||
onOpenWindow(window) {
|
||||
if (this.window)
|
||||
return;
|
||||
|
||||
this.window = window;
|
||||
this.domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
this.domwindow.addEventListener("load", this, false);
|
||||
},
|
||||
|
||||
onCloseWindow(window) {
|
||||
if (this.window != window)
|
||||
return;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.removeListener(this);
|
||||
this.opencallback = null;
|
||||
this.window = null;
|
||||
this.domwindow = null;
|
||||
|
||||
// Let the window close complete
|
||||
executeSoon(this.closecallback);
|
||||
this.closecallback = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var gInstallNotificationObserver = {
|
||||
observe(aSubject, aTopic, aData) {
|
||||
var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
|
||||
if (gTestInWindow)
|
||||
is(installInfo.browser, null, "Notification should have a null browser");
|
||||
else
|
||||
isnot(installInfo.browser, null, "Notification should have non-null browser");
|
||||
gSawInstallNotification = true;
|
||||
Services.obs.removeObserver(this, "addon-install-started");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function test_confirmation(aWindow, aExpectedURLs) {
|
||||
var list = aWindow.document.getElementById("itemList");
|
||||
is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
|
||||
|
||||
for (let url of aExpectedURLs) {
|
||||
let found = false;
|
||||
for (let node of list.children) {
|
||||
if (node.url == url) {
|
||||
found = true;
|
||||
break;
|
||||
let notificationCount = 0;
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
var installInfo = aSubject.wrappedJSObject;
|
||||
if (gTestInWindow)
|
||||
is(installInfo.browser, null, "Notification should have a null browser");
|
||||
else
|
||||
isnot(installInfo.browser, null, "Notification should have non-null browser");
|
||||
notificationCount++;
|
||||
}
|
||||
}
|
||||
ok(found, "Should have seen " + url + " in the list");
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "addon-install-started", false);
|
||||
|
||||
aWindow.document.documentElement.cancelDialog();
|
||||
let windows = new Set();
|
||||
|
||||
function handleDialog(window) {
|
||||
let list = window.document.getElementById("itemList");
|
||||
is(list.childNodes.length, 1, "Should be 1 install");
|
||||
let idx = urls.indexOf(list.children[0].url);
|
||||
isnot(idx, -1, "Install target is an expected url");
|
||||
urls.splice(idx, 1);
|
||||
|
||||
window.document.documentElement.cancelDialog();
|
||||
}
|
||||
|
||||
let listener = {
|
||||
handleEvent(event) {
|
||||
let window = event.currentTarget;
|
||||
is(window.document.location.href, INSTALL_URI, "Should have opened the correct window");
|
||||
|
||||
executeSoon(() => handleDialog(window));
|
||||
},
|
||||
|
||||
onWindowTitleChange() { },
|
||||
|
||||
onOpenWindow(window) {
|
||||
windows.add(window);
|
||||
let domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
domwindow.addEventListener("load", this, false, {once: true});
|
||||
},
|
||||
|
||||
onCloseWindow(window) {
|
||||
if (!windows.has(window)) {
|
||||
return;
|
||||
}
|
||||
windows.delete(window);
|
||||
|
||||
if (windows.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
is(urls.length, 0, "Saw install dialogs for all expected urls");
|
||||
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
wm.removeListener(listener);
|
||||
|
||||
is(notificationCount, nurls, `Saw ${nurls} addon-install-started notifications`);
|
||||
Services.obs.removeObserver(observer, "addon-install-started");
|
||||
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
wm.addListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test_install_from_file() {
|
||||
|
@ -110,27 +93,14 @@ add_task(function* test_install_from_file() {
|
|||
];
|
||||
MockFilePicker.returnFiles = filePaths.map(aPath => aPath.file);
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
// Set handler that executes the core test after the window opens,
|
||||
// and resolves the promise when the window closes
|
||||
let pInstallURIClosed = new Promise((resolve, reject) => {
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
try {
|
||||
test_confirmation(aWindow, filePaths.map(aPath => aPath.spec));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}, resolve);
|
||||
});
|
||||
let pInstallURIClosed = checkInstallConfirmation(...filePaths.map(path => path.spec));
|
||||
|
||||
gManagerWindow.gViewController.doCommand("cmd_installFromFile");
|
||||
|
||||
yield pInstallURIClosed;
|
||||
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
|
||||
MockFilePicker.cleanup();
|
||||
yield close_manager(gManagerWindow);
|
||||
});
|
||||
|
|
|
@ -16,79 +16,79 @@ this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
|||
getService(Ci.mozIJSSubScriptLoader);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
// This listens for the next opened window and checks it is of the right url.
|
||||
// opencallback is called when the new window is fully loaded
|
||||
// closecallback is called when the window is closed
|
||||
function WindowOpenListener(url, opencallback, closecallback) {
|
||||
this.url = url;
|
||||
this.opencallback = opencallback;
|
||||
this.closecallback = closecallback;
|
||||
function checkInstallConfirmation(...urls) {
|
||||
let nurls = urls.length;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.addListener(this);
|
||||
let notificationCount = 0;
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
var installInfo = aSubject.wrappedJSObject;
|
||||
if (gTestInWindow)
|
||||
is(installInfo.browser, null, "Notification should have a null browser");
|
||||
else
|
||||
isnot(installInfo.browser, null, "Notification should have non-null browser");
|
||||
notificationCount++;
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "addon-install-started", false);
|
||||
|
||||
let windows = new Set();
|
||||
|
||||
function handleDialog(window) {
|
||||
let list = window.document.getElementById("itemList");
|
||||
is(list.childNodes.length, 1, "Should be 1 install");
|
||||
let idx = urls.indexOf(list.children[0].url);
|
||||
isnot(idx, -1, "Install target is an expected url");
|
||||
urls.splice(idx, 1);
|
||||
|
||||
window.document.documentElement.cancelDialog();
|
||||
}
|
||||
|
||||
let listener = {
|
||||
handleEvent(event) {
|
||||
let window = event.currentTarget;
|
||||
is(window.document.location.href, INSTALL_URI, "Should have opened the correct window");
|
||||
|
||||
executeSoon(() => handleDialog(window));
|
||||
},
|
||||
|
||||
onWindowTitleChange() { },
|
||||
|
||||
onOpenWindow(window) {
|
||||
windows.add(window);
|
||||
let domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
domwindow.addEventListener("load", this, false, {once: true});
|
||||
},
|
||||
|
||||
onCloseWindow(window) {
|
||||
if (!windows.has(window)) {
|
||||
return;
|
||||
}
|
||||
windows.delete(window);
|
||||
|
||||
if (windows.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
is(urls.length, 0, "Saw install dialogs for all expected urls");
|
||||
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
wm.removeListener(listener);
|
||||
|
||||
is(notificationCount, nurls, `Saw ${nurls} addon-install-started notifications`);
|
||||
Services.obs.removeObserver(observer, "addon-install-started");
|
||||
|
||||
executeSoon(run_next_test);
|
||||
}
|
||||
};
|
||||
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
wm.addListener(listener);
|
||||
}
|
||||
|
||||
WindowOpenListener.prototype = {
|
||||
url: null,
|
||||
opencallback: null,
|
||||
closecallback: null,
|
||||
window: null,
|
||||
domwindow: null,
|
||||
|
||||
handleEvent(event) {
|
||||
is(this.domwindow.document.location.href, this.url, "Should have opened the correct window");
|
||||
|
||||
this.domwindow.removeEventListener("load", this, false);
|
||||
// Allow any other load handlers to execute
|
||||
var self = this;
|
||||
executeSoon(function() { self.opencallback(self.domwindow); } );
|
||||
},
|
||||
|
||||
onWindowTitleChange(window, title) {
|
||||
},
|
||||
|
||||
onOpenWindow(window) {
|
||||
if (this.window)
|
||||
return;
|
||||
|
||||
this.window = window;
|
||||
this.domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
this.domwindow.addEventListener("load", this, false);
|
||||
},
|
||||
|
||||
onCloseWindow(window) {
|
||||
if (this.window != window)
|
||||
return;
|
||||
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
wm.removeListener(this);
|
||||
this.opencallback = null;
|
||||
this.window = null;
|
||||
this.domwindow = null;
|
||||
|
||||
// Let the window close complete
|
||||
executeSoon(this.closecallback);
|
||||
this.closecallback = null;
|
||||
}
|
||||
};
|
||||
|
||||
var gSawInstallNotification = false;
|
||||
var gInstallNotificationObserver = {
|
||||
observe(aSubject, aTopic, aData) {
|
||||
var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
|
||||
if (gTestInWindow)
|
||||
is(installInfo.browser, null, "Notification should have a null browser");
|
||||
else
|
||||
isnot(installInfo.browser, null, "Notification should have non-null browser");
|
||||
gSawInstallNotification = true;
|
||||
Services.obs.removeObserver(this, "addon-install-started");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
|
@ -104,37 +104,11 @@ function end_test() {
|
|||
});
|
||||
}
|
||||
|
||||
function test_confirmation(aWindow, aExpectedURLs) {
|
||||
var list = aWindow.document.getElementById("itemList");
|
||||
is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
|
||||
|
||||
for (let url of aExpectedURLs) {
|
||||
let found = false;
|
||||
for (let node of list.children) {
|
||||
if (node.url == url) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(found, "Should have seen " + url + " in the list");
|
||||
}
|
||||
|
||||
aWindow.document.documentElement.cancelDialog();
|
||||
}
|
||||
|
||||
// Simulates dropping a URL onto the manager
|
||||
add_test(function() {
|
||||
var url = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
test_confirmation(aWindow, [url]);
|
||||
}, function() {
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
run_next_test();
|
||||
});
|
||||
checkInstallConfirmation(url);
|
||||
|
||||
var viewContainer = gManagerWindow.document.getElementById("view-port");
|
||||
var effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
|
||||
|
@ -147,15 +121,7 @@ add_test(function() {
|
|||
add_test(function() {
|
||||
var fileurl = get_addon_file_url("browser_dragdrop1.xpi");
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
test_confirmation(aWindow, [fileurl.spec]);
|
||||
}, function() {
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
run_next_test();
|
||||
});
|
||||
checkInstallConfirmation(fileurl.spec);
|
||||
|
||||
var viewContainer = gManagerWindow.document.getElementById("view-port");
|
||||
var effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
|
||||
|
@ -169,15 +135,7 @@ add_test(function() {
|
|||
var url1 = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
var url2 = TESTROOT2 + "addons/browser_dragdrop2.xpi";
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
test_confirmation(aWindow, [url1, url2]);
|
||||
}, function() {
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
run_next_test();
|
||||
});
|
||||
checkInstallConfirmation(url1, url2);
|
||||
|
||||
var viewContainer = gManagerWindow.document.getElementById("view-port");
|
||||
var effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
|
||||
|
@ -192,15 +150,7 @@ add_test(function() {
|
|||
var fileurl1 = get_addon_file_url("browser_dragdrop1.xpi");
|
||||
var fileurl2 = get_addon_file_url("browser_dragdrop2.xpi");
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
test_confirmation(aWindow, [fileurl1.spec, fileurl2.spec]);
|
||||
}, function() {
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
run_next_test();
|
||||
});
|
||||
checkInstallConfirmation(fileurl1.spec, fileurl2.spec);
|
||||
|
||||
var viewContainer = gManagerWindow.document.getElementById("view-port");
|
||||
var effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
|
||||
|
@ -215,15 +165,7 @@ add_test(function() {
|
|||
var url = TESTROOT + "addons/browser_dragdrop1.xpi";
|
||||
var fileurl = get_addon_file_url("browser_dragdrop2.xpi");
|
||||
|
||||
Services.obs.addObserver(gInstallNotificationObserver,
|
||||
"addon-install-started", false);
|
||||
|
||||
new WindowOpenListener(INSTALL_URI, function(aWindow) {
|
||||
test_confirmation(aWindow, [url, fileurl.spec]);
|
||||
}, function() {
|
||||
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
|
||||
run_next_test();
|
||||
});
|
||||
checkInstallConfirmation(url, fileurl.spec);
|
||||
|
||||
var viewContainer = gManagerWindow.document.getElementById("view-port");
|
||||
var effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
|
||||
|
|
|
@ -474,13 +474,9 @@ function Pmanual_update(aVersion) {
|
|||
onInstallEnded: resolve
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
// Use the default web installer to cancel/allow installs based on whether
|
||||
// the add-on is valid or not.
|
||||
let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"]
|
||||
.getService(Ci.amIWebInstallListener);
|
||||
webInstaller.onWebInstallRequested(null, null, installs);
|
||||
AddonManager.installAddonFromAOM(null, null, install);
|
||||
}
|
||||
|
||||
return Promise.all(completePromises);
|
||||
});
|
||||
|
|
|
@ -375,7 +375,7 @@ var Harness = {
|
|||
// nsIObserver
|
||||
|
||||
observe(subject, topic, data) {
|
||||
var installInfo = subject.QueryInterface(Components.interfaces.amIWebInstallInfo);
|
||||
var installInfo = subject.wrappedJSObject;
|
||||
switch (topic) {
|
||||
case "addon-install-started":
|
||||
is(this.runningInstalls.length, installInfo.installs.length,
|
||||
|
|
Загрузка…
Ссылка в новой задаче