Backed out changeset c0f94e1d6f11 (bug 1308295)

This commit is contained in:
Sebastian Hengst 2017-01-10 17:56:28 +01:00
Родитель 0c0f805af3
Коммит 22246e78b5
6 изменённых файлов: 275 добавлений и 281 удалений

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

@ -10,23 +10,15 @@ const ID = "permissions@test.mozilla.org";
const DEFAULT_EXTENSION_ICON = "chrome://browser/content/extension.svg";
Services.perms.add(makeURI("https://example.com/"), "install",
Services.perms.ALLOW_ACTION);
function promisePopupNotificationShown(name) {
return new Promise(resolve => {
function popupshown() {
PopupNotifications.panel.addEventListener("popupshown", () => {
let notification = PopupNotifications.getNotification(name);
if (!notification) { return; }
ok(notification, `${name} notification shown`);
ok(PopupNotifications.isPanelOpen, "notification panel open");
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
resolve(PopupNotifications.panel.firstChild);
}
PopupNotifications.panel.addEventListener("popupshown", popupshown);
}, {once: true});
});
}
@ -68,14 +60,8 @@ function checkNotification(panel, url) {
const INSTALL_FUNCTIONS = [
function installMozAM(url) {
ContentTask.spawn(gBrowser.selectedBrowser, url, function*(cUrl) {
content.wrappedJSObject.installMozAM(cUrl);
});
},
function installTrigger(url) {
ContentTask.spawn(gBrowser.selectedBrowser, url, function*(cUrl) {
content.wrappedJSObject.installTrigger(cUrl);
return ContentTask.spawn(gBrowser.selectedBrowser, url, function*(cUrl) {
return content.wrappedJSObject.installMozAM(cUrl);
});
},
];
@ -92,37 +78,7 @@ add_task(function* () {
function* runOnce(installFn, url, cancel) {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
let installPromise = new Promise(resolve => {
let listener = {
onDownloadCancelled() {
AddonManager.removeInstallListener(listener);
resolve(false);
},
onDownloadFailed() {
AddonManager.removeInstallListener(listener);
resolve(false);
},
onInstallCancelled() {
AddonManager.removeInstallListener(listener);
resolve(false);
},
onInstallEnded() {
AddonManager.removeInstallListener(listener);
resolve(true);
},
onInstallFailed() {
AddonManager.removeInstallListener(listener);
resolve(false);
},
};
AddonManager.addInstallListener(listener);
});
installFn(url);
let installPromise = installFn(url);
let panel = yield promisePopupNotificationShown("addon-webext-permissions");
checkNotification(panel, url);
@ -136,10 +92,10 @@ add_task(function* () {
let result = yield installPromise;
let addon = yield promiseGetAddonByID(ID);
if (cancel) {
ok(!result, "Installation was cancelled");
is(result, "onInstallCancelled", "Installation was cancelled");
is(addon, null, "Extension is not installed");
} else {
ok(result, "Installation completed");
is(result, "onInstallEnded", "Installation completed");
isnot(addon, null, "Extension is installed");
addon.uninstall();
}

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

@ -7,13 +7,19 @@
<body>
<script type="text/javascript">
function installMozAM(url) {
return navigator.mozAddonManager.createInstall({url}).then(install => {
return navigator.mozAddonManager.createInstall({url}).then(install => new Promise(resolve => {
const EVENTS = [
"onDownloadCancelled",
"onDownloadFailed",
"onInstallCancelled",
"onInstallEnded",
"onInstallFailed",
];
for (let event of EVENTS) {
install.addEventListener(event, () => { resolve(event); });
}
install.install();
});
}
function installTrigger(url) {
InstallTrigger.install({extension: url});
}));
}
</script>
</body>

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

@ -21,12 +21,6 @@ this.ExtensionsUI = {
observe(subject, topic, data) {
if (topic == "webextension-permission-prompt") {
let {target, info} = subject.wrappedJSObject;
let progressNotification = target.ownerGlobal.PopupNotifications.getNotification("addon-progress", target);
if (progressNotification) {
progressNotification.remove();
}
this.showPermissionsPrompt(target, info).then(answer => {
Services.obs.notifyObservers(subject, "webextension-permission-response",
JSON.stringify(answer));

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

@ -47,7 +47,7 @@ const UNKNOWN_XPCOM_ABI = "unknownABI";
const PREF_MIN_WEBEXT_PLATFORM_VERSION = "extensions.webExtensionsMinPlatformVersion";
const PREF_WEBAPI_TESTING = "extensions.webapi.testing";
const PREF_WEBEXT_PERM_PROMPTS = "extensions.webextPermissionPrompts";
const PREF_WEBEXT_PREF_PROMPTS = "extensions.webextPermissionPrompts";
const UPDATE_REQUEST_VERSION = 2;
const CATEGORY_UPDATE_PARAMS = "extension-update-params";
@ -73,8 +73,6 @@ const WEBAPI_TEST_INSTALL_HOSTS = [
"example.com",
];
const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
@ -102,9 +100,6 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
return certUtils;
});
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
PREF_WEBEXT_PERM_PROMPTS, false);
const INTEGER = /^[1-9]\d*$/;
this.EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
@ -453,6 +448,228 @@ BrowserListener.prototype = {
Ci.nsIObserver])
};
function installNotifyObservers(aTopic, aBrowser, aUri, aInstall, aInstallFn) {
let info = {
wrappedJSObject: {
browser: aBrowser,
originatingURI: aUri,
installs: [aInstall],
install: aInstallFn,
},
};
Services.obs.notifyObservers(info, aTopic, null);
}
/**
* Helper to monitor a download and prompt to install when ready
*/
class Installer {
/**
*
* @param aBrowser
* The browser that started the installations
* @param aUrl
* The URL that started the installations
* @param aInstall
* An AddonInstall
*/
constructor(aBrowser, aUrl, aInstall) {
this.browser = aBrowser;
this.url = aUrl;
this.install = aInstall;
this.isDownloading = true;
installNotifyObservers("addon-install-started", aBrowser, aUrl, aInstall);
this.install.addListener(this);
// Start downloading if it hasn't already begun
const READY_STATES = [
AddonManager.STATE_AVAILABLE,
AddonManager.STATE_DOWNLOAD_FAILED,
AddonManager.STATE_INSTALL_FAILED,
AddonManager.STATE_CANCELLED,
];
if (READY_STATES.includes(this.install.state)) {
this.install.install();
}
this.checkDownloaded();
}
get URI_XPINSTALL_DIALOG() {
return "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
}
/**
* Checks if the download is complete and if so prompts to install.
*/
checkDownloaded() {
// Prevent re-entrancy caused by the confirmation dialog cancelling
// unwanted installs.
if (!this.isDownloading)
return;
let failed = false;
switch (this.install.state) {
case AddonManager.STATE_AVAILABLE:
case AddonManager.STATE_DOWNLOADING:
// Exit early if the add-on hasn't started downloading yet or is
// still downloading
return;
case AddonManager.STATE_DOWNLOAD_FAILED:
failed = true;
break;
case AddonManager.STATE_DOWNLOADED:
// App disabled items are not compatible and so fail to install
failed = this.install.addon.appDisabled
break;
case AddonManager.STATE_CANCELLED:
// Just ignore cancelled downloads
return;
default:
logger.warn(`Download of ${this.install.sourceURI.spec} in unexpected state ${this.install.state}`);
return;
}
this.isDownloading = false;
if (failed) {
// Stop listening and cancel any installs that are failed because of
// compatibility reasons.
if (this.install.state == AddonManager.STATE_DOWNLOADED) {
this.install.removeListener(this);
this.install.cancel();
}
installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
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.install]);
return;
} catch (e) {}
}
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
installNotifyObservers("addon-install-confirmation", this.browser, this.url, this.install);
return;
}
let args = {};
args.url = this.url;
args.installs = [this.install];
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);
this.install.removeListener(this);
// Cancel the install, as currently there is no way to make it fail
// from here.
this.install.cancel();
installNotifyObservers("addon-install-cancelled", this.browser, this.url,
this.install);
}
}
/**
* Checks if install is now complete and if so notifies observers.
*/
checkInstalled() {
switch (this.install.state) {
case AddonManager.STATE_DOWNLOADED:
case AddonManager.STATE_INSTALLING:
// Exit early if the add-on hasn't started installing yet or is
// still installing
return;
case AddonManager.STATE_INSTALL_FAILED:
installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
break;
default:
installNotifyObservers("addon-install-complete", this.browser, this.url, this.install);
}
}
// InstallListener methods:
onDownloadCancelled(aInstall) {
aInstall.removeListener(this);
this.checkDownloaded();
}
onDownloadFailed(aInstall) {
aInstall.removeListener(this);
this.checkDownloaded();
}
onDownloadEnded(aInstall) {
this.checkDownloaded();
return false;
}
onInstallCancelled(aInstall) {
aInstall.removeListener(this);
this.checkInstalled();
}
onInstallFailed(aInstall) {
aInstall.removeListener(this);
this.checkInstalled();
}
onInstallEnded(aInstall) {
aInstall.removeListener(this);
// 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.checkInstalled();
}
}
const weblistener = {
onWebInstallDisabled(aBrowser, aUri, aInstall) {
installNotifyObservers("addon-install-disabled", aBrowser, aUri, aInstall);
},
onWebInstallOriginBlocked(aBrowser, aUri, aInstall) {
installNotifyObservers("addon-install-origin-blocked", aBrowser, aUri, aInstall);
return false;
},
onWebInstallBlocked(aBrowser, aUri, aInstall) {
installNotifyObservers("addon-install-blocked", aBrowser, aUri, aInstall,
function() { new Installer(this.browser, this.originatingURI, aInstall); });
return false;
},
onWebInstallRequested(aBrowser, aUri, aInstall) {
new Installer(aBrowser, aUri, aInstall);
},
};
/**
* This represents an author of an add-on (e.g. creator or developer)
*
@ -2023,70 +2240,6 @@ var AddonManagerInternal = {
return false;
},
installNotifyObservers(aTopic, aBrowser, aUri, aInstall, aInstallFn) {
let info = {
wrappedJSObject: {
browser: aBrowser,
originatingURI: aUri,
installs: [aInstall],
install: aInstallFn,
},
};
Services.obs.notifyObservers(info, aTopic, null);
},
startInstall(browser, url, install) {
this.installNotifyObservers("addon-install-started", browser, url, install);
let self = this;
let listener = {
onDownloadCancelled() {
install.removeListener(listener);
},
onDownloadFailed() {
install.removeListener(listener);
self.installNotifyObservers("addon-install-failed", browser, url, install);
},
onDownloadEnded() {
if (install.addon.appDisabled) {
// App disabled items are not compatible and so fail to install
install.removeListener(listener);
install.cancel();
self.installNotifyObservers("addon-install-failed", browser, url, install);
}
},
onInstallCancelled() {
install.removeListener(listener);
},
onInstallFailed() {
install.removeListener(listener);
self.installNotifyObservers("addon-install-failed", browser, url, install);
},
onInstallEnded() {
install.removeListener(listener);
// If installing a theme that is disabled and can be enabled
// then enable it
if (install.addon.type == "theme" &&
install.addon.userDisabled == true &&
install.addon.appDisabled == false) {
install.addon.userDisabled = false;
}
self.installNotifyObservers("addon-install-complete", browser, url, install);
},
};
install.addListener(listener);
// Start downloading if it hasn't already begun
install.install();
},
/**
* Starts installation of an AddonInstall notifying the registered
* web install listener of blocked or started installs.
@ -2135,14 +2288,14 @@ var AddonManagerInternal = {
if (!this.isInstallEnabled(aMimetype)) {
aInstall.cancel();
this.installNotifyObservers("addon-install-disabled", topBrowser,
aInstallingPrincipal.URI, aInstall);
weblistener.onWebInstallDisabled(topBrowser, aInstallingPrincipal.URI,
aInstall);
return;
} else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
aInstall.cancel();
this.installNotifyObservers("addon-install-origin-blocked", topBrowser,
aInstallingPrincipal.URI, aInstall);
weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
aInstall);
return;
}
@ -2151,17 +2304,12 @@ var AddonManagerInternal = {
// install in that case.
new BrowserListener(aBrowser, aInstallingPrincipal, aInstall);
AddonManagerInternal.setupPromptHandler(aBrowser, aInstallingPrincipal.URI, aInstall, true);
let startInstall = () => {
AddonManagerInternal.startInstall(aBrowser, aInstallingPrincipal.URI, aInstall);
};
if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
this.installNotifyObservers("addon-install-blocked", topBrowser,
aInstallingPrincipal.URI, aInstall,
startInstall);
weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
aInstall);
} else {
startInstall();
weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
aInstall);
}
} catch (e) {
// In the event that the weblistener throws during instantiation or when
@ -2188,8 +2336,7 @@ var AddonManagerInternal = {
throw Components.Exception("AddonManager is not initialized",
Cr.NS_ERROR_NOT_INITIALIZED);
AddonManagerInternal.setupPromptHandler(browser, uri, install, true);
AddonManagerInternal.startInstall(browser, uri, install);
weblistener.onWebInstallRequested(browser, uri, install);
},
/**
@ -2785,117 +2932,6 @@ var AddonManagerInternal = {
return gHotfixID;
},
setupPromptHandler(browser, url, install, requireConfirm) {
install.promptHandler = info => new Promise((resolve, _reject) => {
let reject = () => {
this.installNotifyObservers("addon-install-cancelled",
browser, url, install);
_reject();
};
// All installs end up in this callback when the add-on is available
// for installation. There are numerous different things that can
// happen from here though. For webextensions, if the application
// implements webextension permission prompts, those always take
// precedence.
// If this add-on is not a webextension or if the application does not
// implement permission prompts, no confirmation is displayed for
// installs created with mozAddonManager (in which case requireConfirm
// is false).
// In the remaining cases, a confirmation prompt is displayed but the
// application may override it either by implementing the
// "@mozilla.org/addons/web-install-prompt;1" contract or by setting
// the customConfirmationUI preference and responding to the
// "addon-install-confirmation" notification. If the application
// does not implement its own prompt, use the built-in xul dialog.
if (info.addon.userPermissions && WEBEXT_PERMISSION_PROMPTS) {
const observer = {
observe(subject, topic, data) {
if (topic == "webextension-permission-response"
&& subject.wrappedJSObject.info.addon == info.addon) {
let answer = JSON.parse(data);
Services.obs.removeObserver(observer, "webextension-permission-response");
if (answer) {
resolve();
} else {
reject();
}
}
}
};
Services.obs.addObserver(observer, "webextension-permission-response", false);
let subject = {wrappedJSObject: {target: browser, info}};
Services.obs.notifyObservers(subject, "webextension-permission-prompt", null);
} else if (requireConfirm) {
// The methods below all want to call the install() or cancel()
// method on the provided AddonInstall object to either accept
// or reject the confirmation. Fit that into our promise-based
// control flow by wrapping the install object. However,
// xpInstallConfirm.xul matches the install object it is passed
// with the argument passed to an InstallListener, so give it
// access to the underlying object through the .wrapped property.
let proxy = new Proxy(install, {
get(target, property) {
if (property == "install") {
return resolve;
} else if (property == "cancel") {
return reject;
} else if (property == "wrapped") {
return target;
}
let result = target[property];
return (typeof result == "function") ? result.bind(target) : result;
}
});
// 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(browser, url, [proxy]);
return;
} catch (e) {}
}
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
this.installNotifyObservers("addon-install-confirmation",
browser, url, proxy);
return;
}
let args = {};
args.url = url;
args.installs = [proxy];
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 (browser) {
parentWindow = browser.ownerDocument.defaultView;
PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", browser);
}
Services.ww.openWindow(parentWindow, URI_XPINSTALL_DIALOG,
null, "chrome,modal,centerscreen", args);
} catch (e) {
logger.warn("Exception showing install confirmation dialog", e);
// Cancel the install, as currently there is no way to make it fail
// from here.
reject();
}
} else {
resolve();
}
});
},
webAPI: {
// installs maps integer ids to AddonInstall instances.
installs: new Map(),
@ -2979,7 +3015,29 @@ var AddonManagerInternal = {
return AddonManagerInternal.getInstallForURL(options.url, "application/x-xpinstall",
options.hash).then(install => {
AddonManagerInternal.setupPromptHandler(target, null, install, false);
if (Preferences.get(PREF_WEBEXT_PREF_PROMPTS, false)) {
install.promptHandler = info => new Promise((resolve, reject) => {
const observer = {
observe(subject, topic, data) {
if (topic == "webextension-permission-response" &&
subject.wrappedJSObject.info.addon == info.addon) {
let answer = JSON.parse(data);
Services.obs.removeObserver(this, "webextension-permission-response");
if (answer) {
resolve();
} else {
reject();
}
}
}
};
Services.obs.addObserver(observer, "webextension-permission-response", false);
let subject = {wrappedJSObject: {target, info}};
Services.obs.notifyObservers(subject, "webextension-permission-prompt", null);
});
}
let id = this.nextInstall++;
let listener = this.makeListener(id, target.messageManager);

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

@ -67,7 +67,7 @@ XPInstallConfirm.init = function() {
}
installItem.signed = install.certName ? "true" : "false";
installMap.set(install.wrapped, installItem);
installMap.set(install, installItem);
install.addListener(installListener);
}

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

@ -108,8 +108,6 @@ var Harness = {
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
Services.obs.addObserver(this, "addon-install-started", false);
Services.obs.addObserver(this, "addon-install-disabled", false);
// XXX this breaks a bunch of stuff, see comment in onInstallCancelled
// Services.obs.addObserver(this, "addon-install-cancelled", false);
Services.obs.addObserver(this, "addon-install-origin-blocked", false);
Services.obs.addObserver(this, "addon-install-blocked", false);
Services.obs.addObserver(this, "addon-install-failed", false);
@ -125,7 +123,6 @@ var Harness = {
Services.prefs.clearUserPref(PREF_INSTALL_REQUIRESECUREORIGIN);
Services.obs.removeObserver(self, "addon-install-started");
Services.obs.removeObserver(self, "addon-install-disabled");
// Services.obs.removeObserver(self, "addon-install-cancelled");
Services.obs.removeObserver(self, "addon-install-origin-blocked");
Services.obs.removeObserver(self, "addon-install-blocked");
Services.obs.removeObserver(self, "addon-install-failed");
@ -370,23 +367,6 @@ var Harness = {
this.checkTestEnded();
},
onInstallCancelled(install) {
// This is ugly. We have a bunch of tests that cancel installs
// but don't expect this event to be raised (they also don't
// expecte addon-install-cancelled to be raised but even though
// we have code to handle that, it is never attached, see setup() above)
// For at least one test (browser_whitelist3.js), we used to generate
// onDownloadCancelled when the user cancelled the installation at the
// confirmation prompt. We're now generating onInstallCancelled instead
// of onDownloadCancelled but making this code unconditional breaks a
// bunch of other tests. Ugh.
let idx = this.runningInstalls.indexOf(install);
if (idx != -1) {
this.runningInstalls.splice(this.runningInstalls.indexOf(install), 1);
this.checkTestEnded();
}
},
checkTestEnded() {
if (--this.pendingCount == 0 && !this.waitingForEvent)
this.endTest();