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:
Andrew Swan 2017-01-04 10:13:16 -08:00
Родитель 090c727768
Коммит c6e46bcd64
15 изменённых файлов: 488 добавлений и 794 удалений

Просмотреть файл

@ -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,