summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-10 04:00:58 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-10 04:00:58 -0500
commitdeea787c2efbb9c89caec8d9efc023ffafe75613 (patch)
tree6dbe55f7d24e67ecdcc821b8c5492f6c17217852 /toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
parent37d5300335d81cecbecc99812747a657588c63eb (diff)
downloadUXP-deea787c2efbb9c89caec8d9efc023ffafe75613.tar
UXP-deea787c2efbb9c89caec8d9efc023ffafe75613.tar.gz
UXP-deea787c2efbb9c89caec8d9efc023ffafe75613.tar.lz
UXP-deea787c2efbb9c89caec8d9efc023ffafe75613.tar.xz
UXP-deea787c2efbb9c89caec8d9efc023ffafe75613.zip
Import Tycho's Add-on Manager
Diffstat (limited to 'toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js')
-rw-r--r--toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js462
1 files changed, 462 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js b/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
new file mode 100644
index 000000000..1799adcdd
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_cancelCompatCheck.js
@@ -0,0 +1,462 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that we can cancel the add-on compatibility check while it is
+// in progress (bug 772484).
+// Test framework copied from browser_bug557956.js
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+
+let repo = {};
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", repo);
+Components.utils.import("resource://gre/modules/Promise.jsm", this);
+
+/**
+ * Test add-ons:
+ *
+ * Addon minVersion maxVersion Notes
+ * addon1 0 *
+ * addon2 0 0
+ * addon3 0 0
+ * addon4 1 *
+ * addon5 0 0 Made compatible by update check
+ * addon6 0 0 Made compatible by update check
+ * addon7 0 0 Has a broken update available
+ * addon8 0 0 Has an update available
+ * addon9 0 0 Has an update available
+ * addon10 0 0 Made incompatible by override check
+ */
+
+// describe the addons
+let ao1 = { file: "browser_bug557956_1", id: "addon1@tests.mozilla.org"};
+let ao2 = { file: "browser_bug557956_2", id: "addon2@tests.mozilla.org"};
+let ao3 = { file: "browser_bug557956_3", id: "addon3@tests.mozilla.org"};
+let ao4 = { file: "browser_bug557956_4", id: "addon4@tests.mozilla.org"};
+let ao5 = { file: "browser_bug557956_5", id: "addon5@tests.mozilla.org"};
+let ao6 = { file: "browser_bug557956_6", id: "addon6@tests.mozilla.org"};
+let ao7 = { file: "browser_bug557956_7", id: "addon7@tests.mozilla.org"};
+let ao8 = { file: "browser_bug557956_8_1", id: "addon8@tests.mozilla.org"};
+let ao9 = { file: "browser_bug557956_9_1", id: "addon9@tests.mozilla.org"};
+let ao10 = { file: "browser_bug557956_10", id: "addon10@tests.mozilla.org"};
+
+// Return a promise that resolves after the specified delay in MS
+function delayMS(aDelay) {
+ let deferred = Promise.defer();
+ setTimeout(deferred.resolve, aDelay);
+ return deferred.promise;
+}
+
+// Return a promise that resolves when the specified observer topic is notified
+function promise_observer(aTopic) {
+ let deferred = Promise.defer();
+ Services.obs.addObserver(function observe(aSubject, aObsTopic, aData) {
+ Services.obs.removeObserver(arguments.callee, aObsTopic);
+ deferred.resolve([aSubject, aData]);
+ }, aTopic, false);
+ return deferred.promise;
+}
+
+// Install a set of addons using a bogus update URL so that we can force
+// the compatibility update to happen later
+// @param aUpdateURL The real update URL to use after the add-ons are installed
+function promise_install_test_addons(aAddonList, aUpdateURL) {
+ info("Starting add-on installs");
+ var installs = [];
+ let deferred = Promise.defer();
+
+ // Use a blank update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ for (let addon of aAddonList) {
+ AddonManager.getInstallForURL(TESTROOT + "addons/" + addon.file + ".xpi", function(aInstall) {
+ installs.push(aInstall);
+ }, "application/x-xpinstall");
+ }
+
+ var listener = {
+ installCount: 0,
+
+ onInstallEnded: function() {
+ this.installCount++;
+ if (this.installCount == installs.length) {
+ info("Done add-on installs");
+ // Switch to the test update URL
+ Services.prefs.setCharPref(PREF_UPDATEURL, aUpdateURL);
+ deferred.resolve();
+ }
+ }
+ };
+
+ for (let install of installs) {
+ install.addListener(listener);
+ install.install();
+ }
+
+ return deferred.promise;
+}
+
+function promise_addons_by_ids(aAddonIDs) {
+ info("promise_addons_by_ids " + aAddonIDs.toSource());
+ let deferred = Promise.defer();
+ AddonManager.getAddonsByIDs(aAddonIDs, deferred.resolve);
+ return deferred.promise;
+}
+
+function* promise_uninstall_test_addons() {
+ info("Starting add-on uninstalls");
+ let addons = yield promise_addons_by_ids([ao1.id, ao2.id, ao3.id, ao4.id, ao5.id,
+ ao6.id, ao7.id, ao8.id, ao9.id, ao10.id]);
+ let deferred = Promise.defer();
+ let uninstallCount = addons.length;
+ let listener = {
+ onUninstalled: function(aAddon) {
+ if (aAddon) {
+ info("Finished uninstalling " + aAddon.id);
+ }
+ if (--uninstallCount == 0) {
+ info("Done add-on uninstalls");
+ AddonManager.removeAddonListener(listener);
+ deferred.resolve();
+ }
+ }};
+ AddonManager.addAddonListener(listener);
+ for (let addon of addons) {
+ if (addon)
+ addon.uninstall();
+ else
+ listener.onUninstalled(null);
+ }
+ yield deferred.promise;
+}
+
+// Returns promise{window}, resolves with a handle to the compatibility
+// check window
+function promise_open_compatibility_window(aInactiveAddonIds) {
+ let deferred = Promise.defer();
+ // This will reset the longer timeout multiplier to 2 which will give each
+ // test that calls open_compatibility_window a minimum of 60 seconds to
+ // complete.
+ requestLongerTimeout(2);
+
+ var variant = Cc["@mozilla.org/variant;1"].
+ createInstance(Ci.nsIWritableVariant);
+ variant.setFromVariant(aInactiveAddonIds);
+
+ // Cannot be modal as we want to interract with it, shouldn't cause problems
+ // with testing though.
+ var features = "chrome,centerscreen,dialog,titlebar";
+ var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+ getService(Ci.nsIWindowWatcher);
+ var win = ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
+
+ win.addEventListener("load", function() {
+ function page_shown(aEvent) {
+ if (aEvent.target.pageid)
+ info("Page " + aEvent.target.pageid + " shown");
+ }
+
+ win.removeEventListener("load", arguments.callee, false);
+
+ info("Compatibility dialog opened");
+
+ win.addEventListener("pageshow", page_shown, false);
+ win.addEventListener("unload", function() {
+ win.removeEventListener("unload", arguments.callee, false);
+ win.removeEventListener("pageshow", page_shown, false);
+ dump("Compatibility dialog closed\n");
+ }, false);
+
+ deferred.resolve(win);
+ }, false);
+ return deferred.promise;
+}
+
+function promise_window_close(aWindow) {
+ let deferred = Promise.defer();
+ aWindow.addEventListener("unload", function() {
+ aWindow.removeEventListener("unload", arguments.callee, false);
+ deferred.resolve(aWindow);
+ }, false);
+ return deferred.promise;
+}
+
+function promise_page(aWindow, aPageId) {
+ let deferred = Promise.defer();
+ var page = aWindow.document.getElementById(aPageId);
+ if (aWindow.document.getElementById("updateWizard").currentPage === page) {
+ deferred.resolve(aWindow);
+ } else {
+ page.addEventListener("pageshow", function() {
+ page.removeEventListener("pageshow", arguments.callee, false);
+ executeSoon(function() {
+ deferred.resolve(aWindow);
+ });
+ }, false);
+ }
+ return deferred.promise;
+}
+
+function get_list_names(aList) {
+ var items = [];
+ for (let listItem of aList.childNodes)
+ items.push(listItem.label);
+ items.sort();
+ return items;
+}
+
+// These add-ons became inactive during the upgrade
+let inactiveAddonIds = [
+ ao5.id,
+ ao6.id,
+ ao7.id,
+ ao8.id,
+ ao9.id
+];
+
+// Make sure the addons in the list are not installed
+function* check_addons_uninstalled(aAddonList) {
+ let foundList = yield promise_addons_by_ids([addon.id for (addon of aAddonList)]);
+ for (let i = 0; i < aAddonList.length; i++) {
+ ok(!foundList[i], "Addon " + aAddonList[i].id + " is not installed");
+ }
+ info("Add-on uninstall check complete");
+ yield true;
+}
+
+// Test what happens when the user cancels during AddonRepository.repopulateCache()
+// Add-ons that have updates available should not update if they were disabled before
+// For this test, addon8 became disabled during update and addon9 was previously disabled,
+// so addon8 should update and addon9 should not
+add_task(function cancel_during_repopulate() {
+ let a5, a8, a9, a10;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+ Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
+
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // Set up our test addons so that the server-side JS has a 500ms delay to make
+ // sure we cancel the dialog before we get the data we want to refill our
+ // AddonRepository cache
+ let addonList = [ao5, ao8, ao9, ao10];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs?500");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_bug557956.xml");
+
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(!a8.isCompatible, "addon8 should not be compatible");
+ ok(!a9.isCompatible, "addon9 should not be compatible");
+
+ let compatWindow = yield promise_open_compatibility_window([ao5.id, ao8.id]);
+ var doc = compatWindow.document;
+ yield promise_page(compatWindow, "versioninfo");
+
+ // Brief delay to let the update window finish requesting all add-ons and start
+ // reloading the addon repository
+ yield delayMS(50);
+
+ info("Cancel the compatibility check dialog");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ info("Waiting for installs to complete");
+ yield installsDone;
+ ok(!repo.AddonRepository.isSearching, "Background installs are done");
+
+ // There should be no active updates
+ let getInstalls = Promise.defer();
+ AddonManager.getAllInstalls(getInstalls.resolve);
+ let installs = yield getInstalls.promise;
+ is (installs.length, 0, "There should be no active installs after background installs are done");
+
+ // addon8 should have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9, a10] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id, ao10.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(a8.isCompatible, "addon8 should have been upgraded");
+ ok(!a9.isCompatible, "addon9 should not have been upgraded");
+ ok(!a10.isCompatible, "addon10 should not be compatible");
+
+ info("Updates done");
+ yield promise_uninstall_test_addons();
+ info("done uninstalling add-ons");
+});
+
+// User cancels after repopulateCache, while we're waiting for the addon.findUpdates()
+// calls in gVersionInfoPage_onPageShow() to complete
+// For this test, both addon8 and addon9 were disabled by this update, but addon8
+// is set to not auto-update, so only addon9 should update in the background
+add_task(function cancel_during_findUpdates() {
+ let a5, a8, a9;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Clear the AddonRepository-last-updated preference to ensure that it reloads
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+ let observeUpdateDone = promise_observer("TEST:addon-repository-data-updated");
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ [a8] = yield promise_addons_by_ids([ao8.id]);
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
+ var doc = compatWindow.document;
+ yield promise_page(compatWindow, "versioninfo");
+
+ info("Waiting for repository-data-updated");
+ yield observeUpdateDone;
+
+ // Quick wait to make sure the findUpdates calls get queued
+ yield delayMS(5);
+
+ info("Cancel the compatibility check dialog");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ info("Waiting for installs to complete 2");
+ yield installsDone;
+ ok(!repo.AddonRepository.isSearching, "Background installs are done 2");
+
+ // addon8 should have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(!a8.isCompatible, "addon8 should not have been upgraded");
+ ok(a9.isCompatible, "addon9 should have been upgraded");
+
+ let getInstalls = Promise.defer();
+ AddonManager.getAllInstalls(getInstalls.resolve);
+ let installs = yield getInstalls.promise;
+ is (installs.length, 0, "There should be no active installs after the dialog is cancelled 2");
+
+ info("findUpdates done");
+ yield promise_uninstall_test_addons();
+});
+
+// Cancelling during the 'mismatch' screen allows add-ons that can auto-update
+// to continue updating in the background and cancels any other updates
+// Same conditions as the previous test - addon8 and addon9 have updates available,
+// addon8 is set to not auto-update so only addon9 should become compatible
+add_task(function cancel_mismatch() {
+ let a3, a5, a7, a8, a9;
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Clear the AddonRepository-last-updated preference to ensure that it reloads
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+ let installsDone = promise_observer("TEST:all-updates-done");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ [a8] = yield promise_addons_by_ids([ao8.id]);
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // Check that the addons start out not compatible.
+ [a3, a7, a8, a9] = yield promise_addons_by_ids([ao3.id, ao7.id, ao8.id, ao9.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(!a7.isCompatible, "addon7 should not be compatible");
+ ok(!a8.isCompatible, "addon8 should not be compatible");
+ ok(!a9.isCompatible, "addon9 should not be compatible");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
+ var doc = compatWindow.document;
+ info("Wait for mismatch page");
+ yield promise_page(compatWindow, "mismatch");
+ info("Click the Don't Check button");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ yield promise_window_close(compatWindow);
+ info("Waiting for installs to complete in cancel_mismatch");
+ yield installsDone;
+
+ // addon8 should not have updated in the background,
+ // addon9 was listed as previously disabled so it should not have updated
+ [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
+ ok(a5.isCompatible, "addon5 should be compatible");
+ ok(!a8.isCompatible, "addon8 should not have been upgraded");
+ ok(a9.isCompatible, "addon9 should have been upgraded");
+
+ // Make sure there are no pending addon installs
+ let pInstalls = Promise.defer();
+ AddonManager.getAllInstalls(pInstalls.resolve);
+ let installs = yield pInstalls.promise;
+ ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
+
+ yield promise_uninstall_test_addons();
+ yield check_addons_uninstalled(addonList);
+});
+
+// Cancelling during the 'mismatch' screen with only add-ons that have
+// no updates available
+add_task(function cancel_mismatch_no_updates() {
+ let a3, a5, a6
+
+ Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+ Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
+
+ // Don't pull compatibility data during add-on install
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ // No delay on the .sjs this time because we want the cache to repopulate
+ let addonList = [ao3, ao5, ao6];
+ yield promise_install_test_addons(addonList,
+ TESTROOT + "cancelCompatCheck.sjs");
+
+ // Check that the addons start out not compatible.
+ [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(!a5.isCompatible, "addon5 should not be compatible");
+ ok(!a6.isCompatible, "addon6 should not be compatible");
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ let compatWindow = yield promise_open_compatibility_window([ao3.id, ao5.id, ao6.id]);
+ var doc = compatWindow.document;
+ info("Wait for mismatch page");
+ yield promise_page(compatWindow, "mismatch");
+ info("Click the Don't Check button");
+ var button = doc.documentElement.getButton("cancel");
+ EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
+
+ yield promise_window_close(compatWindow);
+
+ [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
+ ok(!a3.isCompatible, "addon3 should not be compatible");
+ ok(a5.isCompatible, "addon5 should have become compatible");
+ ok(a6.isCompatible, "addon6 should have become compatible");
+
+ // Make sure there are no pending addon installs
+ let pInstalls = Promise.defer();
+ AddonManager.getAllInstalls(pInstalls.resolve);
+ let installs = yield pInstalls.promise;
+ ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
+
+ yield promise_uninstall_test_addons();
+ yield check_addons_uninstalled(addonList);
+});