Merge m-c to m-i

This commit is contained in:
Phil Ringnalda 2016-02-07 18:51:47 -08:00
commit 22b26ff040
27 changed files with 1723 additions and 77 deletions

View File

@ -14,4 +14,5 @@ support-files =
[browser_storage_dynamic_updates.js]
[browser_storage_overflow.js]
[browser_storage_sidebar.js]
skip-if = (os == 'win' && os_version == '6.1' && e10s && !debug) # bug 1229272
[browser_storage_values.js]

View File

@ -116,6 +116,9 @@ pref("network.predictor.enabled", true);
pref("network.predictor.max-db-size", 2097152); // bytes
pref("network.predictor.preserve", 50); // percentage of predictor data to keep when cleaning up
// Use JS mDNS as a fallback
pref("network.mdns.use_js_fallback", true);
/* history max results display */
pref("browser.display.history.maxresults", 100);
@ -976,3 +979,7 @@ pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com
// Token server used by Firefox Account-authenticated Sync.
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
// Enable Presentation API
pref("dom.presentation.enabled", true);
pref("dom.presentation.discovery.enabled", true);

View File

@ -59,6 +59,64 @@ var mediaPlayerDevice = {
}
};
var fxOSTVDevice = {
id: "app://fling-player.gaiamobile.org",
target: "app://fling-player.gaiamobile.org/index.html",
factory: function(aService) {
Cu.import("resource://gre/modules/PresentationApp.jsm");
let request = new window.PresentationRequest(this.target);
return new PresentationApp(aService, request);
},
init: function() {
Services.obs.addObserver(this, "presentation-device-change", false);
SimpleServiceDiscovery.addExternalDiscovery(this);
},
observe: function(subject, topic, data) {
let device = subject.QueryInterface(Ci.nsIPresentationDevice);
let service = this.toService(device);
switch (data) {
case "add":
SimpleServiceDiscovery.addService(service);
break;
case "update":
SimpleServiceDiscovery.updateService(service);
break;
case "remove":
if(SimpleServiceDiscovery.findServiceForID(device.id)) {
SimpleServiceDiscovery.removeService(device.id);
}
break;
}
},
toService: function(device) {
return {
location: device.id,
target: fxOSTVDevice.target,
friendlyName: device.name,
uuid: device.id,
manufacturer: "Firefox OS TV",
modelName: "Firefox OS TV",
};
},
startDiscovery: function() {
window.navigator.mozPresentationDeviceInfo.forceDiscovery();
// need to update the lastPing time for known device.
window.navigator.mozPresentationDeviceInfo.getAll()
.then(function(devices) {
for (let device of devices) {
let service = fxOSTVDevice.toService(device);
SimpleServiceDiscovery.addService(service);
}
});
},
stopDiscovery: function() {
// do nothing
},
types: ["video/mp4", "video/webm"],
extensions: ["mp4", "webm"],
};
var CastingApps = {
_castMenuId: -1,
mirrorStartMenuId: -1,
@ -79,6 +137,10 @@ var CastingApps = {
mediaPlayerDevice.init();
SimpleServiceDiscovery.registerDevice(mediaPlayerDevice);
// Presentation Device will notify us any time the available device list changes.
fxOSTVDevice.init();
SimpleServiceDiscovery.registerDevice(fxOSTVDevice);
// Search for devices continuously
SimpleServiceDiscovery.search(this._interval);

View File

@ -591,6 +591,7 @@ var BrowserApp = {
InitLater(() => Cu.import("resource://gre/modules/NotificationDB.jsm"));
InitLater(() => Cu.import("resource://gre/modules/Payment.jsm"));
InitLater(() => Cu.import("resource://gre/modules/PresentationDeviceInfoManager.jsm"));
InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", ""));
InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" }));

View File

@ -23,7 +23,7 @@
src="chrome://passwordmgr/locale/passwordmgr.properties"/>
<keyset>
<key keycode="VK_ESCAPE" oncommand="window.close();"/>
<key keycode="VK_ESCAPE" oncommand="escapeKeyHandler();"/>
<key key="&windowClose.key;" modifiers="accel" oncommand="escapeKeyHandler();"/>
<key key="&focusSearch1.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
<key key="&focusSearch2.key;" modifiers="accel" oncommand="FocusFilterBox();"/>

View File

@ -1,5 +1,4 @@
const { ContentTaskUtils } = Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
const TIME_INTERVAL = 500;
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
var doc;
@ -55,7 +54,7 @@ function* editUsernamePromises(site, oldUsername, newUsername) {
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
login = Services.logins.findLogins({}, site, "", "")[0];
is(login.username, newUsername, "Correct username updated");
is(getUsername(0), newUsername, "Correct username shown");
is(getUsername(0), newUsername, "Correct username shown after the update");
}
function* editPasswordPromises(site, oldPassword, newPassword) {
@ -77,7 +76,7 @@ function* editPasswordPromises(site, oldPassword, newPassword) {
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
login = Services.logins.findLogins({}, site, "", "")[0];
is(login.password, newPassword, "Correct password updated");
is(getPassword(0), newPassword, "Correct password shown");
is(getPassword(0), newPassword, "Correct password shown after the update");
}
add_task(function* test_setup() {

View File

@ -63,6 +63,7 @@ EXTRA_JS_MODULES += [
'RemoteSecurityUI.jsm',
'RemoteWebProgress.jsm',
'ResetProfile.jsm',
'secondscreen/PresentationApp.jsm',
'secondscreen/RokuApp.jsm',
'secondscreen/SimpleServiceDiscovery.jsm',
'SelectContentHelper.jsm',

View File

@ -0,0 +1,190 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["PresentationApp"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "sysInfo", () => {
return Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
});
const DEBUG = false;
const STATE_UNINIT = "uninitialized" // RemoteMedia status
const STATE_STARTED = "started"; // RemoteMedia status
const STATE_PAUSED = "paused"; // RemoteMedia status
const STATE_SHUTDOWN = "shutdown"; // RemoteMedia status
function debug(msg) {
Services.console.logStringMessage("PresentationApp: " + msg);
}
// PresentationApp is a wrapper for interacting with a Presentation Receiver Device.
function PresentationApp(service, request) {
this.service = service;
this.request = request;
}
PresentationApp.prototype = {
start: function start(callback) {
this.request.startWithDevice(this.service.uuid)
.then((session) => {
this._session = session;
if (callback) {
callback(true);
}
}, () => {
if (callback) {
callback(false);
}
});
},
stop: function stop(callback) {
if (this._session && this._session.state === "connected") {
this._session.terminate();
}
delete this._session;
if (callback) {
callback(true);
}
},
remoteMedia: function remoteMedia(callback, listener) {
if (callback) {
if (!this._session) {
callback();
return;
}
callback(new RemoteMedia(this._session, listener));
}
}
}
/* RemoteMedia provides a wrapper for using Presentation API to control Firefox TV app.
* The server implementation must be built into the Firefox TV receiver app.
* see https://github.com/mozilla-b2g/gaia/tree/master/tv_apps/fling-player
*/
function RemoteMedia(session, listener) {
this._session = session ;
this._listener = listener;
this._status = STATE_UNINIT;
this._session.addEventListener("message", this);
this._session.addEventListener("statechange", this);
if (this._listener && "onRemoteMediaStart" in this._listener) {
Services.tm.mainThread.dispatch((function() {
this._listener.onRemoteMediaStart(this);
}).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
}
}
RemoteMedia.prototype = {
_seq: 0,
handleEvent: function(e) {
switch (e.type) {
case "message":
this._onmessage(e);
break;
case "statechange":
this._onstatechange(e);
break;
}
},
_onmessage: function(e) {
DEBUG && debug("onmessage: " + e.data);
if (this.status === STATE_SHUTDOWN) {
return;
}
if (e.data.indexOf("stopped") > -1) {
if (this.status !== STATE_PAUSED) {
this._status = STATE_PAUSED;
if (this._listener && "onRemoteMediaStatus" in this._listener) {
this._listener.onRemoteMediaStatus(this);
}
}
} else if (e.data.indexOf("playing") > -1) {
if (this.status !== STATE_STARTED) {
this._status = STATE_STARTED;
if (this._listener && "onRemoteMediaStatus" in this._listener) {
this._listener.onRemoteMediaStatus(this);
}
}
}
},
_onstatechange: function(e) {
DEBUG && debug("onstatechange: " + this._session.state);
if (this._session.state !== "connected") {
this._status = STATE_SHUTDOWN;
if (this._listener && "onRemoteMediaStop" in this._listener) {
this._listener.onRemoteMediaStop(this);
}
}
},
_sendCommand: function(command, data) {
let msg = {
'type': command,
'seq': ++this._seq
};
if (data) {
for (var k in data) {
msg[k] = data[k];
}
}
let raw = JSON.stringify(msg);
DEBUG && debug("send command: " + raw);
this._session.send(raw);
},
shutdown: function shutdown() {
DEBUG && debug("RemoteMedia - shutdown");
this._sendCommand("close");
},
play: function play() {
DEBUG && debug("RemoteMedia - play");
this._sendCommand("play");
},
pause: function pause() {
DEBUG && debug("RemoteMedia - pause");
this._sendCommand("pause");
},
load: function load(data) {
DEBUG && debug("RemoteMedia - load: " + data);
this._sendCommand("load", { "url": data.source });
let deviceName;
if (Services.appinfo.widgetToolkit == "android") {
deviceName = sysInfo.get("device");
} else {
deviceName = sysInfo.get("host");
}
this._sendCommand("device-info", { "displayName": deviceName });
},
get status() {
return this._status;
}
}

View File

@ -57,6 +57,7 @@ var SimpleServiceDiscovery = {
_searchTimestamp: 0,
_searchTimeout: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
_searchRepeat: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
_discoveryMethods: [],
_forceTrailingSlash: function(aURL) {
// Cleanup the URL to make it consistent across devices
@ -141,6 +142,9 @@ var SimpleServiceDiscovery = {
// UDP broadcasts, so this is a way to skip discovery.
this._searchFixedDevices();
// Look for any devices via registered external discovery mechanism.
this._startExternalDiscovery();
// Perform a UDP broadcast to search for SSDP devices
let socket = Cc["@mozilla.org/network/udp-socket;1"].createInstance(Ci.nsIUDPSocket);
try {
@ -226,6 +230,8 @@ var SimpleServiceDiscovery = {
}
}
}
this._stopExternalDiscovery();
},
getSupportedExtensions: function() {
@ -409,5 +415,21 @@ var SimpleServiceDiscovery = {
// Make sure we remember this service is not stale
this._services.get(service.uuid).lastPing = this._searchTimestamp;
}
},
addExternalDiscovery: function(discovery) {
this._discoveryMethods.push(discovery);
},
_startExternalDiscovery: function() {
for (let discovery of this._discoveryMethods) {
discovery.startDiscovery();
}
},
_stopExternalDiscovery: function() {
for (let discovery of this._discoveryMethods) {
discovery.stopDiscovery();
}
},
}

View File

@ -3043,14 +3043,22 @@ this.AddonManager = {
// The combination of all scopes.
SCOPE_ALL: 31,
// 1-15 are different built-in views for the add-on type
// Add-on type is expected to be displayed in the UI in a list.
VIEW_TYPE_LIST: "list",
// Constants describing how add-on types behave.
// If no add-ons of a type are installed, then the category for that add-on
// type should be hidden in the UI.
TYPE_UI_HIDE_EMPTY: 16,
// Indicates that this add-on type supports the ask-to-activate state.
// That is, add-ons of this type can be set to be optionally enabled
// on a case-by-case basis.
TYPE_SUPPORTS_ASK_TO_ACTIVATE: 32,
// The add-on type natively supports undo for restartless uninstalls.
// If this flag is not specified, the UI is expected to handle this via
// disabling the add-on, and performing the actual uninstall at a later time.
TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL: 64,
// Constants for Addon.applyBackgroundUpdates.
// Indicates that the Addon should not update automatically.

View File

@ -1718,7 +1718,7 @@ function doPendingUninstalls(aListBox) {
var listitem = aListBox.firstChild;
while (listitem) {
if (listitem.getAttribute("pending") == "uninstall" &&
!listitem.isPending("uninstall"))
!(listitem.opRequiresRestart("UNINSTALL")))
items.push(listitem.mAddon);
listitem = listitem.nextSibling;
}

View File

@ -745,6 +745,16 @@
]]></body>
</method>
<method name="typeHasFlag">
<parameter name="aFlag"/>
<body><![CDATA[
let flag = AddonManager["TYPE_" + aFlag];
let type = AddonManager.addonTypes[this.mAddon.type];
return !!(type.flags & flag);
]]></body>
</method>
<method name="onUninstalled">
<body><![CDATA[
this.parentNode.removeChild(this);
@ -1306,8 +1316,7 @@
this._preferencesBtn.hidden = (!this.mAddon.optionsURL) ||
this.mAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO;
let addonType = AddonManager.addonTypes[this.mAddon.type];
if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) {
if (this.typeHasFlag("SUPPORTS_ASK_TO_ACTIVATE")) {
this._enableBtn.disabled = true;
this._disableBtn.disabled = true;
this._askToActivateMenuitem.disabled = !this.hasPermission("ask_to_activate");
@ -1525,9 +1534,11 @@
<method name="uninstall">
<body><![CDATA[
// If uninstalling does not require a restart then just disable it
// and show the undo UI.
if (!this.opRequiresRestart("uninstall")) {
// If uninstalling does not require a restart and the type doesn't
// support undoing of restartless uninstalls, then we fake it by
// just disabling it it, and doing the real uninstall later.
if (!this.opRequiresRestart("uninstall") &&
!this.typeHasFlag("SUPPORTS_UNDO_RESTARTLESS_UNINSTALL")) {
this.setAttribute("wasDisabled", this.mAddon.userDisabled);
// We must set userDisabled to true first, this will call
@ -1537,7 +1548,7 @@
// This won't update any other add-on manager views (bug 582002)
this.setAttribute("pending", "uninstall");
} else {
this.mAddon.uninstall();
this.mAddon.uninstall(true);
}
]]></body>
</method>
@ -1773,7 +1784,7 @@
[this.mAddon.name],
1);
if (!this.isPending("uninstall"))
if (!this.opRequiresRestart("uninstall"))
this._restartBtn.setAttribute("hidden", true);
gEventManager.registerAddonListener(this, this.mAddon.id);

View File

@ -3187,6 +3187,19 @@ this.XPIProvider = {
// install location.
if (!manifest) {
logger.debug("Processing uninstall of " + id + " in " + location.name);
try {
let addonFile = location.getLocationForID(id);
let addonToUninstall = syncLoadManifestFromFile(addonFile, location);
if (addonToUninstall.bootstrap) {
this.callBootstrapMethod(addonToUninstall, addonToUninstall._sourceBundle,
"uninstall", BOOTSTRAP_REASONS.ADDON_UNINSTALL);
}
}
catch (e) {
logger.warn("Failed to call uninstall for " + id, e);
}
try {
location.uninstallAddon(id);
seenFiles.push(stageDirEntry.leafName);
@ -4764,10 +4777,14 @@ this.XPIProvider = {
*
* @param aAddon
* The DBAddonInternal to uninstall
* @param aForcePending
* Force this addon into the pending uninstall state, even if
* it isn't marked as requiring a restart (used e.g. while the
* add-on manager is open and offering an "undo" button)
* @throws if the addon cannot be uninstalled because it is in an install
* location that does not allow it
*/
uninstallAddon: function(aAddon) {
uninstallAddon: function(aAddon, aForcePending) {
if (!(aAddon.inDatabase))
throw new Error("Cannot uninstall addon " + aAddon.id + " because it is not installed");
@ -4775,6 +4792,16 @@ this.XPIProvider = {
throw new Error("Cannot uninstall addon " + aAddon.id
+ " from locked install location " + aAddon._installLocation.name);
// Inactive add-ons don't require a restart to uninstall
let requiresRestart = this.uninstallRequiresRestart(aAddon);
// if makePending is true, we don't actually apply the uninstall,
// we just mark the addon as having a pending uninstall
let makePending = aForcePending || requiresRestart;
if (makePending && aAddon.pendingUninstall)
throw new Error("Add-on is already marked to be uninstalled");
aAddon._hasResourceCache.clear();
if (aAddon._updateCheck) {
@ -4782,16 +4809,19 @@ this.XPIProvider = {
aAddon._updateCheck.cancel();
}
// Inactive add-ons don't require a restart to uninstall
let requiresRestart = this.uninstallRequiresRestart(aAddon);
let wasPending = aAddon.pendingUninstall;
if (requiresRestart) {
// We create an empty directory in the staging directory to indicate that
// an uninstall is necessary on next startup.
let stage = aAddon._installLocation.getStagingDir();
stage.append(aAddon.id);
if (!stage.exists())
stage.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
if (makePending) {
// We create an empty directory in the staging directory to indicate
// that an uninstall is necessary on next startup. Temporary add-ons are
// automatically uninstalled on shutdown anyway so there is no need to
// do this for them.
if (aAddon._installLocation.name != KEY_APP_TEMPORARY) {
let stage = aAddon._installLocation.getStagingDir();
stage.append(aAddon.id);
if (!stage.exists())
stage.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
}
XPIDatabase.setAddonProperties(aAddon, {
pendingUninstall: true
@ -4811,8 +4841,16 @@ this.XPIProvider = {
return;
let wrapper = aAddon.wrapper;
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper,
requiresRestart);
// If the add-on wasn't already pending uninstall then notify listeners.
if (!wasPending) {
// Passing makePending as the requiresRestart parameter is a little
// strange as in some cases this operation can complete without a restart
// so really this is now saying that the uninstall isn't going to happen
// immediately but will happen later.
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper,
makePending);
}
// Reveal the highest priority add-on with the same ID
function revealAddon(aAddon) {
@ -4850,7 +4888,7 @@ this.XPIProvider = {
}
}
if (!requiresRestart) {
if (!makePending) {
if (aAddon.bootstrap) {
if (aAddon.active) {
this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
@ -4869,6 +4907,12 @@ this.XPIProvider = {
findAddonAndReveal(aAddon.id);
}
else if (aAddon.bootstrap && aAddon.active && !this.disableRequiresRestart(aAddon)) {
this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
BOOTSTRAP_REASONS.ADDON_UNINSTALL);
this.unloadBootstrapScope(aAddon.id);
XPIDatabase.updateAddonActive(aAddon, false);
}
// Notify any other providers that a new theme has been enabled
if (aAddon.type == "theme" && aAddon.active)
@ -4884,8 +4928,11 @@ this.XPIProvider = {
cancelUninstallAddon: function(aAddon) {
if (!(aAddon.inDatabase))
throw new Error("Can only cancel uninstall for installed addons.");
if (!aAddon.pendingUninstall)
throw new Error("Add-on is not marked to be uninstalled");
aAddon._installLocation.cleanStagingDir([aAddon.id]);
if (aAddon._installLocation.name != KEY_APP_TEMPORARY)
aAddon._installLocation.cleanStagingDir([aAddon.id]);
XPIDatabase.setAddonProperties(aAddon, {
pendingUninstall: false
@ -4900,6 +4947,12 @@ this.XPIProvider = {
let wrapper = aAddon.wrapper;
AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper);
if (aAddon.bootstrap && !aAddon.disabled && !this.enableRequiresRestart(aAddon)) {
this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup",
BOOTSTRAP_REASONS.ADDON_INSTALL);
XPIDatabase.updateAddonActive(aAddon, true);
}
// Notify any other providers that this theme is now enabled again.
if (aAddon.type == "theme" && aAddon.active)
AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
@ -5860,12 +5913,17 @@ AddonInstall.prototype = {
let installedUnpacked = 0;
yield this.installLocation.requestStagingDir();
// Remove any staged items for this add-on
stagedAddon.append(this.addon.id);
yield removeAsync(stagedAddon);
stagedAddon.leafName = this.addon.id + ".xpi";
yield removeAsync(stagedAddon);
// First stage the file regardless of whether restarting is necessary
if (this.addon.unpack || Preferences.get(PREF_XPI_UNPACK, false)) {
logger.debug("Addon " + this.addon.id + " will be installed as " +
"an unpacked directory");
stagedAddon.append(this.addon.id);
yield removeAsync(stagedAddon);
stagedAddon.leafName = this.addon.id;
yield OS.File.makeDir(stagedAddon.path);
yield ZipUtils.extractFilesAsync(this.file, stagedAddon);
installedUnpacked = 1;
@ -5873,8 +5931,7 @@ AddonInstall.prototype = {
else {
logger.debug("Addon " + this.addon.id + " will be installed as " +
"a packed xpi");
stagedAddon.append(this.addon.id + ".xpi");
yield removeAsync(stagedAddon);
stagedAddon.leafName = this.addon.id + ".xpi";
yield OS.File.copy(this.file.path, stagedAddon.path);
}
@ -7054,21 +7111,13 @@ AddonWrapper.prototype = {
return addonFor(this).isCompatibleWith(aAppVersion, aPlatformVersion);
},
uninstall: function() {
uninstall: function(alwaysAllowUndo) {
let addon = addonFor(this);
if (!(addon.inDatabase))
throw new Error("Cannot uninstall an add-on that isn't installed");
if (addon.pendingUninstall)
throw new Error("Add-on is already marked to be uninstalled");
XPIProvider.uninstallAddon(addon);
XPIProvider.uninstallAddon(addon, alwaysAllowUndo);
},
cancelUninstall: function() {
let addon = addonFor(this);
if (!(addon.inDatabase))
throw new Error("Cannot cancel uninstall for an add-on that isn't installed");
if (!addon.pendingUninstall)
throw new Error("Add-on is not marked to be uninstalled");
XPIProvider.cancelUninstallAddon(addon);
},
@ -8137,18 +8186,19 @@ WinRegInstallLocation.prototype = {
var addonTypes = [
new AddonManagerPrivate.AddonType("extension", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 4000),
AddonManager.VIEW_TYPE_LIST, 4000,
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
new AddonManagerPrivate.AddonType("theme", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 5000),
new AddonManagerPrivate.AddonType("dictionary", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 7000,
AddonManager.TYPE_UI_HIDE_EMPTY),
AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
new AddonManagerPrivate.AddonType("locale", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 8000,
AddonManager.TYPE_UI_HIDE_EMPTY),
AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
];
// We only register experiments support if the application supports them.
@ -8161,7 +8211,7 @@ if (Preferences.get("experiments.supported", false)) {
URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 11000,
AddonManager.TYPE_UI_HIDE_EMPTY));
AddonManager.TYPE_UI_HIDE_EMPTY | AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL));
}
AddonManagerPrivate.registerProvider(XPIProvider, addonTypes);

View File

@ -0,0 +1 @@
Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>incompatible@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Incompatible Addon</em:name>
<em:description>I am incompatible</em:description>
<em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
<em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
<em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>2</em:minVersion>
<em:maxVersion>2</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -0,0 +1 @@
Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>undouninstall1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Test Bootstrap 1</em:name>
<em:description>Test Description</em:description>
<em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
<em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
<em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>1</em:minVersion>
<em:maxVersion>1</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -110,8 +110,7 @@ add_task(function*() {
// Force XBL to apply
item.clientTop;
ok(aAddon.userDisabled, "Add-on should be disabled");
ok(!aAddon.pendingUninstall, "Add-on should not be pending uninstall");
ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
is_element_visible(get_class_node(item, "pending"), "Pending message should be visible");
yield install_addon("browser_bug596336_2");
@ -139,8 +138,7 @@ add_task(function*() {
// Force XBL to apply
item.clientTop;
ok(aAddon.userDisabled, "Add-on should be disabled");
ok(!aAddon.pendingUninstall, "Add-on should not be pending uninstall");
ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
is_element_visible(get_class_node(item, "pending"), "Pending message should be visible");
yield install_addon("browser_bug596336_2");

View File

@ -166,7 +166,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -221,7 +221,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -342,7 +342,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -405,7 +405,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -531,7 +531,7 @@ add_test(function() {
isnot(item, null, "Should have found the add-on in the list");
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
// Force XBL to apply
@ -598,7 +598,7 @@ add_test(function() {
isnot(item, null, "Should have found the add-on in the list");
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
// Force XBL to apply
@ -808,8 +808,7 @@ add_test(function() {
item.clientTop;
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -888,7 +887,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -964,7 +963,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");
@ -1046,7 +1045,7 @@ add_test(function() {
is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall");
ok(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL, "Add-on should be pending uninstall");
ok(!aAddon.isActive, "Add-on should be inactive");
button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn");

View File

@ -682,7 +682,8 @@ function MockProvider(aUseAsyncCallbacks, aTypes) {
id: "extension",
name: "Extensions",
uiPriority: 4000,
flags: AddonManager.TYPE_UI_VIEW_LIST
flags: AddonManager.TYPE_UI_VIEW_LIST |
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
}] : aTypes;
var self = this;
@ -1134,7 +1135,8 @@ function MockAddon(aId, aName, aType, aOperationsRequiringRestart) {
MockAddon.prototype = {
get shouldBeActive() {
return !this.appDisabled && !this._userDisabled;
return !this.appDisabled && !this._userDisabled &&
!(this.pendingOperations & AddonManager.PENDING_UNINSTALL);
},
get appDisabled() {
@ -1206,16 +1208,19 @@ MockAddon.prototype = {
// Tests can implement this if they need to
},
uninstall: function() {
if (this.pendingOperations & AddonManager.PENDING_UNINSTALL)
uninstall: function(aAlwaysAllowUndo = false) {
if ((this.operationsRequiringRestart & AddonManager.OP_NEED_RESTART_UNINSTALL)
&& this.pendingOperations & AddonManager.PENDING_UNINSTALL)
throw Components.Exception("Add-on is already pending uninstall");
var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL);
var needsRestart = aAlwaysAllowUndo || !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL);
this.pendingOperations |= AddonManager.PENDING_UNINSTALL;
AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart);
if (!needsRestart) {
this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
this._provider.removeAddon(this);
} else if (!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE)) {
this.isActive = false;
}
},
@ -1224,6 +1229,7 @@ MockAddon.prototype = {
throw Components.Exception("Add-on is not pending uninstall");
this.pendingOperations -= AddonManager.PENDING_UNINSTALL;
this.isActive = this.shouldBeActive;
AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
},

View File

@ -2015,7 +2015,7 @@ function callback_soon(aFunction) {
* its callback.
*/
function promiseAddonsByIDs(list) {
return new Promise((resolve, reject) => AddonManager.getAddonsByIDs(list, resolve));
return new Promise(resolve => AddonManager.getAddonsByIDs(list, resolve));
}
/**
@ -2026,7 +2026,20 @@ function promiseAddonsByIDs(list) {
* @resolve {AddonWrapper} The corresponding add-on, or null.
*/
function promiseAddonByID(aId) {
return new Promise((resolve, reject) => AddonManager.getAddonByID(aId, resolve));
return new Promise(resolve => AddonManager.getAddonByID(aId, resolve));
}
/**
* A promise-based variant of AddonManager.getAddonsWithOperationsByTypes
*
* @param {array} aTypes The first argument to
* AddonManager.getAddonsWithOperationsByTypes
* @return {promise}
* @resolve {array} The list of add-ons sent by
* AddonManaget.getAddonsWithOperationsByTypes to its callback.
*/
function promiseAddonsWithOperationsByTypes(aTypes) {
return new Promise(resolve => AddonManager.getAddonsWithOperationsByTypes(aTypes, resolve));
}
/**

View File

@ -0,0 +1,421 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This verifies that forcing undo for uninstall works for themes
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
var defaultTheme = {
id: "default@tests.mozilla.org",
version: "1.0",
name: "Test 1",
internalName: "classic/1.0",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}]
};
var theme1 = {
id: "theme1@tests.mozilla.org",
version: "1.0",
name: "Test 1",
internalName: "theme1",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}]
};
const profileDir = gProfD.clone();
profileDir.append("extensions");
function dummyLWTheme(id) {
return {
id: id || Math.random().toString(),
name: Math.random().toString(),
headerURL: "http://lwttest.invalid/a.png",
footerURL: "http://lwttest.invalid/b.png",
textcolor: Math.random().toString(),
accentcolor: Math.random().toString()
};
}
// Sets up the profile by installing an add-on.
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager();
do_register_cleanup(promiseShutdownManager);
run_next_test();
}
add_task(function* checkDefault() {
writeInstallRDFForExtension(defaultTheme, profileDir);
yield promiseRestartManager();
let d = yield promiseAddonByID("default@tests.mozilla.org");
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
});
// Tests that uninstalling an enabled theme offers the option to undo
add_task(function* uninstallEnabledOffersUndo() {
writeInstallRDFForExtension(theme1, profileDir);
yield promiseRestartManager();
let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
do_check_neq(t1, null);
do_check_true(t1.userDisabled);
t1.userDisabled = false;
yield promiseRestartManager();
let d = null;
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_true(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
prepare_test({
"default@tests.mozilla.org": [
"onEnabling"
],
"theme1@tests.mozilla.org": [
"onUninstalling"
]
});
t1.uninstall(true);
ensure_test_completed();
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
yield promiseRestartManager();
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(t1, null);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
});
//Tests that uninstalling an enabled theme can be undone
add_task(function* canUndoUninstallEnabled() {
writeInstallRDFForExtension(theme1, profileDir);
yield promiseRestartManager();
let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
do_check_neq(t1, null);
do_check_true(t1.userDisabled);
t1.userDisabled = false;
yield promiseRestartManager();
let d = null;
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_true(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
prepare_test({
"default@tests.mozilla.org": [
"onEnabling"
],
"theme1@tests.mozilla.org": [
"onUninstalling"
]
});
t1.uninstall(true);
ensure_test_completed();
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
prepare_test({
"default@tests.mozilla.org": [
"onOperationCancelled"
],
"theme1@tests.mozilla.org": [
"onOperationCancelled"
]
});
t1.cancelUninstall();
ensure_test_completed();
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_true(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
yield promiseRestartManager();
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_true(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
t1.uninstall();
yield promiseRestartManager();
});
//Tests that uninstalling a disabled theme offers the option to undo
add_task(function* uninstallDisabledOffersUndo() {
writeInstallRDFForExtension(theme1, profileDir);
yield promiseRestartManager();
let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
prepare_test({
"theme1@tests.mozilla.org": [
"onUninstalling"
]
});
t1.uninstall(true);
ensure_test_completed();
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
yield promiseRestartManager();
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(t1, null);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
});
//Tests that uninstalling a disabled theme can be undone
add_task(function* canUndoUninstallDisabled() {
writeInstallRDFForExtension(theme1, profileDir);
yield promiseRestartManager();
let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
prepare_test({
"theme1@tests.mozilla.org": [
"onUninstalling"
]
});
t1.uninstall(true);
ensure_test_completed();
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
prepare_test({
"theme1@tests.mozilla.org": [
"onOperationCancelled"
]
});
t1.cancelUninstall();
ensure_test_completed();
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
yield promiseRestartManager();
[ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_false(t1.isActive);
do_check_true(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
t1.uninstall();
yield promiseRestartManager();
});
//Tests that uninstalling an enabled lightweight theme offers the option to undo
add_task(function* uninstallLWTOffersUndo() {
// skipped since lightweight themes don't support undoable uninstall yet
return;
LightweightThemeManager.currentTheme = dummyLWTheme("theme1");
let [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_true(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_neq(t1, null);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
prepare_test({
"default@tests.mozilla.org": [
"onEnabling"
],
"theme1@personas.mozilla.org": [
"onUninstalling"
]
});
t1.uninstall(true);
ensure_test_completed();
do_check_neq(d, null);
do_check_false(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
do_check_true(t1.isActive);
do_check_false(t1.userDisabled);
do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
yield promiseRestartManager();
[ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
"default@tests.mozilla.org"]);
do_check_neq(d, null);
do_check_true(d.isActive);
do_check_false(d.userDisabled);
do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
do_check_eq(t1, null);
do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
});

View File

@ -0,0 +1,792 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This verifies that forcing undo for uninstall works
const APP_STARTUP = 1;
const APP_SHUTDOWN = 2;
const ADDON_ENABLE = 3;
const ADDON_DISABLE = 4;
const ADDON_INSTALL = 5;
const ADDON_UNINSTALL = 6;
const ADDON_UPGRADE = 7;
const ADDON_DOWNGRADE = 8;
const ID = "undouninstall1@tests.mozilla.org";
const INCOMPAT_ID = "incompatible@tests.mozilla.org";
var addon1 = {
id: "addon1@tests.mozilla.org",
version: "1.0",
name: "Test 1",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}]
};
const profileDir = gProfD.clone();
profileDir.append("extensions");
BootstrapMonitor.init();
function getStartupReason(id) {
let info = BootstrapMonitor.started.get(id);
return info ? info.reason : undefined;
}
function getShutdownReason(id) {
let info = BootstrapMonitor.stopped.get(id);
return info ? info.reason : undefined;
}
function getInstallReason(id) {
let info = BootstrapMonitor.installed.get(id);
return info ? info.reason : undefined;
}
function getUninstallReason(id) {
let info = BootstrapMonitor.uninstalled.get(id);
return info ? info.reason : undefined;
}
function getStartupOldVersion(id) {
let info = BootstrapMonitor.started.get(id);
return info ? info.data.oldVersion : undefined;
}
function getShutdownNewVersion(id) {
let info = BootstrapMonitor.stopped.get(id);
return info ? info.data.newVersion : undefined;
}
function getInstallOldVersion(id) {
let info = BootstrapMonitor.installed.get(id);
return info ? info.data.oldVersion : undefined;
}
function getUninstallNewVersion(id) {
let info = BootstrapMonitor.uninstalled.get(id);
return info ? info.data.newVersion : undefined;
}
// Sets up the profile by installing an add-on.
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager();
do_register_cleanup(promiseShutdownManager);
run_next_test();
}
add_task(function* installAddon() {
let olda1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_eq(olda1, null);
writeInstallRDFForExtension(addon1, profileDir);
yield promiseRestartManager();
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
do_check_true(isExtensionInAddonsList(profileDir, a1.id));
do_check_eq(a1.pendingOperations, 0);
do_check_in_crash_annotation(addon1.id, addon1.version);
});
// Uninstalling an add-on should work.
add_task(function* uninstallAddon() {
prepare_test({
"addon1@tests.mozilla.org": [
"onUninstalling"
]
});
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_eq(a1.pendingOperations, 0);
do_check_neq(a1.operationsRequiringRestart &
AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
a1.uninstall(true);
do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
do_check_in_crash_annotation(addon1.id, addon1.version);
ensure_test_completed();
let list = yield promiseAddonsWithOperationsByTypes(null);
do_check_eq(list.length, 1);
do_check_eq(list[0].id, "addon1@tests.mozilla.org");
yield promiseRestartManager();
a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_eq(a1, null);
do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
do_check_not_in_crash_annotation(addon1.id, addon1.version);
var dest = profileDir.clone();
dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
do_check_false(dest.exists());
writeInstallRDFForExtension(addon1, profileDir);
yield promiseRestartManager();
});
// Cancelling the uninstall should send onOperationCancelled
add_task(function* cancelUninstall() {
prepare_test({
"addon1@tests.mozilla.org": [
"onUninstalling"
]
});
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
do_check_true(isExtensionInAddonsList(profileDir, a1.id));
do_check_eq(a1.pendingOperations, 0);
a1.uninstall(true);
do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
ensure_test_completed();
prepare_test({
"addon1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
do_check_eq(a1.pendingOperations, 0);
ensure_test_completed();
yield promiseRestartManager();
a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
do_check_true(isExtensionInAddonsList(profileDir, a1.id));
});
// Uninstalling an item pending disable should still require a restart
add_task(function* pendingDisableRequestRestart() {
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
prepare_test({
"addon1@tests.mozilla.org": [
"onDisabling"
]
});
a1.userDisabled = true;
ensure_test_completed();
do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
do_check_true(a1.isActive);
prepare_test({
"addon1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
prepare_test({
"addon1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
yield promiseRestartManager();
});
// Test that uninstalling an inactive item should still allow cancelling
add_task(function* uninstallInactiveIsCancellable() {
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
do_check_false(isExtensionInAddonsList(profileDir, a1.id));
prepare_test({
"addon1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
prepare_test({
"addon1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
yield promiseRestartManager();
});
//Test that an inactive item can be uninstalled
add_task(function* uninstallInactive() {
let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_neq(a1, null);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
do_check_false(isExtensionInAddonsList(profileDir, a1.id));
prepare_test({
"addon1@tests.mozilla.org": [
[ "onUninstalling", false ],
"onUninstalled"
]
});
a1.uninstall();
ensure_test_completed();
a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
do_check_eq(a1, null);
});
// Tests that an enabled restartless add-on can be uninstalled and goes away
// when the uninstall is committed
add_task(function* uninstallRestartless() {
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onNewInstall",
"onInstallStarted",
"onInstallEnded"
]);
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
ensure_test_completed();
let a1 = yield promiseAddonByID(ID);
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID(ID);
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID);
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_false(a1.userDisabled);
// complete the uinstall
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalled"
]
});
a1.uninstall();
ensure_test_completed();
a1 = yield promiseAddonByID(ID);
do_check_eq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
});
//Tests that an enabled restartless add-on can be uninstalled and then cancelled
add_task(function* cancelUninstallOfRestartless() {
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onNewInstall",
"onInstallStarted",
"onInstallEnded"
]);
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
ensure_test_completed();
a1 = yield promiseAddonByID(ID);
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID);
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
shutdownManager();
do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
do_check_eq(getShutdownNewVersion(ID), undefined);
startupManager(false);
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getStartupReason(ID), APP_STARTUP);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
a1.uninstall();
});
// Tests that reinstalling an enabled restartless add-on waiting to be
// uninstalled aborts the uninstall and leaves the add-on enabled
add_task(function* reinstallAddonAwaitingUninstall() {
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID);
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onNewInstall",
"onInstallStarted",
"onInstallEnded"
]);
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
ensure_test_completed();
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
do_check_eq(getStartupReason(ID), ADDON_DOWNGRADE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
shutdownManager();
do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
startupManager(false);
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getStartupReason(ID), APP_STARTUP);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
a1.uninstall();
});
// Tests that a disabled restartless add-on can be uninstalled and goes away
// when the uninstall is committed
add_task(function* uninstallDisabledRestartless() {
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
a1.userDisabled = true;
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
// commit the uninstall
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalled"
]
});
a1.uninstall();
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_eq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
BootstrapMonitor.checkAddonNotInstalled(ID);
do_check_eq(getUninstallReason(ID), ADDON_UNINSTALL);
});
//Tests that a disabled restartless add-on can be uninstalled and then cancelled
add_task(function* cancelUninstallDisabledRestartless() {
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onNewInstall",
"onInstallStarted",
"onInstallEnded"
]);
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
ensure_test_completed();
let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onDisabling", false],
"onDisabled"
]
});
a1.userDisabled = true;
ensure_test_completed();
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
BootstrapMonitor.checkAddonInstalled(ID);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
BootstrapMonitor.checkAddonNotStarted(ID);
BootstrapMonitor.checkAddonInstalled(ID);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
yield promiseRestartManager();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
BootstrapMonitor.checkAddonInstalled(ID);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
a1.uninstall();
});
//Tests that reinstalling a disabled restartless add-on waiting to be
//uninstalled aborts the uninstall and leaves the add-on disabled
add_task(function* reinstallDisabledAddonAwaitingUninstall() {
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_INSTALL);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
a1.userDisabled = true;
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
["onInstalling", false],
"onInstalled"
]
}, [
"onNewInstall",
"onInstallStarted",
"onInstallEnded"
]);
yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
ensure_test_completed();
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
yield promiseRestartManager();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_false(a1.isActive);
do_check_true(a1.userDisabled);
a1.uninstall();
});
// Test that uninstalling a temporary addon can be canceled
add_task(function* cancelUninstallTemporary() {
yield AddonManager.installTemporaryAddon(do_get_addon("test_undouninstall1"));
let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(getInstallReason(ID), ADDON_INSTALL);
do_check_eq(getStartupReason(ID), ADDON_ENABLE);
do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
do_check_true(a1.isActive);
do_check_false(a1.userDisabled);
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
prepare_test({
"undouninstall1@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
do_check_neq(a1, null);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
do_check_eq(a1.pendingOperations, 0);
yield promiseRestartManager();
});
// Tests that cancelling the uninstall of an incompatible restartless addon
// does not start the addon
add_task(function* cancelUninstallIncompatibleRestartless() {
yield promiseInstallAllFiles([do_get_addon("test_undoincompatible")]);
let a1 = yield promiseAddonByID(INCOMPAT_ID);
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
do_check_false(a1.isActive);
prepare_test({
"incompatible@tests.mozilla.org": [
"onUninstalling"
]
});
a1.uninstall(true);
ensure_test_completed();
a1 = yield promiseAddonByID(INCOMPAT_ID);
do_check_neq(a1, null);
do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
do_check_false(a1.isActive);
prepare_test({
"incompatible@tests.mozilla.org": [
"onOperationCancelled"
]
});
a1.cancelUninstall();
ensure_test_completed();
a1 = yield promiseAddonByID(INCOMPAT_ID);
do_check_neq(a1, null);
BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
do_check_eq(a1.pendingOperations, 0);
do_check_false(a1.isActive);
});

View File

@ -201,6 +201,7 @@ function run_test_4() {
]
});
a1.uninstall();
ensure_test_completed();
check_test_4();
});

View File

@ -262,6 +262,8 @@ fail-if = os == "android"
# Bug 676992: test consistently fails on Android
fail-if = os == "android"
[test_types.js]
[test_undothemeuninstall.js]
[test_undouninstall.js]
[test_uninstall.js]
[test_update.js]
# Bug 676992: test consistently hangs on Android

View File

@ -33,5 +33,4 @@ skip-if = appname != "firefox"
[test_temporary.js]
[test_proxy.js]
[include:xpcshell-shared.ini]

View File

@ -27,6 +27,8 @@ body {
vertical-align: middle;
}
/** Categories **/
.category {
cursor: pointer;
/* Center category names */
@ -38,6 +40,12 @@ body {
pointer-events: none;
}
#categories hr {
border-top-color: rgba(255,255,255,0.15);
}
/** Warning container **/
/* XXX: a lot of this is duplicated from info-pages.css since that stylesheet
is incompatible with this type of layout */
.warningBackground:not([hidden]) {
@ -84,6 +92,8 @@ body {
min-width: 100px;
}
/** Content area **/
.main-content {
flex: 1;
}
@ -97,9 +107,10 @@ body {
width: 100%;
}
hr {
th, td, table {
border-collapse: collapse;
border: none;
border-bottom: 1px solid rgba(255,255,255,0.15);
text-align: start;
}
th {
@ -107,12 +118,6 @@ th {
font-size: larger;
}
th, td, table {
border-collapse: collapse;
border: none;
text-align: start;
}
td {
padding-bottom: 0.25em;
border-bottom: 1px solid var(--in-content-box-border-color);