Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/
3 */
5 // Test that we can cancel the add-on compatibility check while it is
6 // in progress (bug 772484).
7 // Test framework copied from browser_bug557956.js
9 const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
11 const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
12 const PREF_MIN_PLATFORM_COMPAT = "extensions.minCompatiblePlatformVersion";
14 let repo = {};
15 Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm", repo);
17 /**
18 * Test add-ons:
19 *
20 * Addon minVersion maxVersion Notes
21 * addon1 0 *
22 * addon2 0 0
23 * addon3 0 0
24 * addon4 1 *
25 * addon5 0 0 Made compatible by update check
26 * addon6 0 0 Made compatible by update check
27 * addon7 0 0 Has a broken update available
28 * addon8 0 0 Has an update available
29 * addon9 0 0 Has an update available
30 * addon10 0 0 Made incompatible by override check
31 */
33 // describe the addons
34 let ao1 = { file: "browser_bug557956_1", id: "addon1@tests.mozilla.org"};
35 let ao2 = { file: "browser_bug557956_2", id: "addon2@tests.mozilla.org"};
36 let ao3 = { file: "browser_bug557956_3", id: "addon3@tests.mozilla.org"};
37 let ao4 = { file: "browser_bug557956_4", id: "addon4@tests.mozilla.org"};
38 let ao5 = { file: "browser_bug557956_5", id: "addon5@tests.mozilla.org"};
39 let ao6 = { file: "browser_bug557956_6", id: "addon6@tests.mozilla.org"};
40 let ao7 = { file: "browser_bug557956_7", id: "addon7@tests.mozilla.org"};
41 let ao8 = { file: "browser_bug557956_8_1", id: "addon8@tests.mozilla.org"};
42 let ao9 = { file: "browser_bug557956_9_1", id: "addon9@tests.mozilla.org"};
43 let ao10 = { file: "browser_bug557956_10", id: "addon10@tests.mozilla.org"};
45 // Return a promise that resolves after the specified delay in MS
46 function delayMS(aDelay) {
47 let deferred = Promise.defer();
48 setTimeout(deferred.resolve, aDelay);
49 return deferred.promise;
50 }
52 // Return a promise that resolves when the specified observer topic is notified
53 function promise_observer(aTopic) {
54 let deferred = Promise.defer();
55 Services.obs.addObserver(function observe(aSubject, aObsTopic, aData) {
56 Services.obs.removeObserver(arguments.callee, aObsTopic);
57 deferred.resolve([aSubject, aData]);
58 }, aTopic, false);
59 return deferred.promise;
60 }
62 // Install a set of addons using a bogus update URL so that we can force
63 // the compatibility update to happen later
64 // @param aUpdateURL The real update URL to use after the add-ons are installed
65 function promise_install_test_addons(aAddonList, aUpdateURL) {
66 info("Starting add-on installs");
67 var installs = [];
68 let deferred = Promise.defer();
70 // Use a blank update URL
71 Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
73 for (let addon of aAddonList) {
74 AddonManager.getInstallForURL(TESTROOT + "addons/" + addon.file + ".xpi", function(aInstall) {
75 installs.push(aInstall);
76 }, "application/x-xpinstall");
77 }
79 var listener = {
80 installCount: 0,
82 onInstallEnded: function() {
83 this.installCount++;
84 if (this.installCount == installs.length) {
85 info("Done add-on installs");
86 // Switch to the test update URL
87 Services.prefs.setCharPref(PREF_UPDATEURL, aUpdateURL);
88 deferred.resolve();
89 }
90 }
91 };
93 for (let install of installs) {
94 install.addListener(listener);
95 install.install();
96 }
98 return deferred.promise;
99 }
101 function promise_addons_by_ids(aAddonIDs) {
102 info("promise_addons_by_ids " + aAddonIDs.toSource());
103 let deferred = Promise.defer();
104 AddonManager.getAddonsByIDs(aAddonIDs, deferred.resolve);
105 return deferred.promise;
106 }
108 function* promise_uninstall_test_addons() {
109 info("Starting add-on uninstalls");
110 let addons = yield promise_addons_by_ids([ao1.id, ao2.id, ao3.id, ao4.id, ao5.id,
111 ao6.id, ao7.id, ao8.id, ao9.id, ao10.id]);
112 let deferred = Promise.defer();
113 let uninstallCount = addons.length;
114 let listener = {
115 onUninstalled: function(aAddon) {
116 if (aAddon) {
117 info("Finished uninstalling " + aAddon.id);
118 }
119 if (--uninstallCount == 0) {
120 info("Done add-on uninstalls");
121 AddonManager.removeAddonListener(listener);
122 deferred.resolve();
123 }
124 }};
125 AddonManager.addAddonListener(listener);
126 for (let addon of addons) {
127 if (addon)
128 addon.uninstall();
129 else
130 listener.onUninstalled(null);
131 }
132 yield deferred.promise;
133 }
135 // Returns promise{window}, resolves with a handle to the compatibility
136 // check window
137 function promise_open_compatibility_window(aInactiveAddonIds) {
138 let deferred = Promise.defer();
139 // This will reset the longer timeout multiplier to 2 which will give each
140 // test that calls open_compatibility_window a minimum of 60 seconds to
141 // complete.
142 requestLongerTimeout(100 /* XXX was 2 */);
144 var variant = Cc["@mozilla.org/variant;1"].
145 createInstance(Ci.nsIWritableVariant);
146 variant.setFromVariant(aInactiveAddonIds);
148 // Cannot be modal as we want to interract with it, shouldn't cause problems
149 // with testing though.
150 var features = "chrome,centerscreen,dialog,titlebar";
151 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
152 getService(Ci.nsIWindowWatcher);
153 var win = ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
155 win.addEventListener("load", function() {
156 function page_shown(aEvent) {
157 if (aEvent.target.pageid)
158 info("Page " + aEvent.target.pageid + " shown");
159 }
161 win.removeEventListener("load", arguments.callee, false);
163 info("Compatibility dialog opened");
165 win.addEventListener("pageshow", page_shown, false);
166 win.addEventListener("unload", function() {
167 win.removeEventListener("unload", arguments.callee, false);
168 win.removeEventListener("pageshow", page_shown, false);
169 dump("Compatibility dialog closed\n");
170 }, false);
172 deferred.resolve(win);
173 }, false);
174 return deferred.promise;
175 }
177 function promise_window_close(aWindow) {
178 let deferred = Promise.defer();
179 aWindow.addEventListener("unload", function() {
180 aWindow.removeEventListener("unload", arguments.callee, false);
181 deferred.resolve(aWindow);
182 }, false);
183 return deferred.promise;
184 }
186 function promise_page(aWindow, aPageId) {
187 let deferred = Promise.defer();
188 var page = aWindow.document.getElementById(aPageId);
189 if (aWindow.document.getElementById("updateWizard").currentPage === page) {
190 deferred.resolve(aWindow);
191 } else {
192 page.addEventListener("pageshow", function() {
193 page.removeEventListener("pageshow", arguments.callee, false);
194 executeSoon(function() {
195 deferred.resolve(aWindow);
196 });
197 }, false);
198 }
199 return deferred.promise;
200 }
202 function get_list_names(aList) {
203 var items = [];
204 for (let listItem of aList.childNodes)
205 items.push(listItem.label);
206 items.sort();
207 return items;
208 }
210 // These add-ons were inactive in the old application
211 let inactiveAddonIds = [
212 ao2.id,
213 ao4.id,
214 ao5.id,
215 ao10.id
216 ];
218 // Make sure the addons in the list are not installed
219 function* check_addons_uninstalled(aAddonList) {
220 let foundList = yield promise_addons_by_ids([addon.id for (addon of aAddonList)]);
221 for (let i = 0; i < aAddonList.length; i++) {
222 ok(!foundList[i], "Addon " + aAddonList[i].id + " is not installed");
223 }
224 info("Add-on uninstall check complete");
225 yield true;
226 }
229 // Tests that the right add-ons show up in the mismatch dialog and updates can
230 // be installed
231 // This is a task-based rewrite of the first test in browser_bug557956.js
232 // kept here to show the whole process so that other tests in this file can
233 // pick and choose which steps to perform, but disabled since the logic is already
234 // tested in browser_bug557956.js.
235 // add_task(
236 function start_update() {
237 // Don't pull compatibility data during add-on install
238 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
239 let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
240 yield promise_install_test_addons(addonList, TESTROOT + "cancelCompatCheck.sjs");
243 // Check that the addons start out not compatible.
244 let [a5, a6, a8, a9] = yield promise_addons_by_ids([ao5.id, ao6.id, ao8.id, ao9.id]);
245 ok(!a5.isCompatible, "addon5 should not be compatible");
246 ok(!a6.isCompatible, "addon6 should not be compatible");
247 ok(!a8.isCompatible, "addon8 should not be compatible");
248 ok(!a9.isCompatible, "addon9 should not be compatible");
250 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
251 // Check that opening the compatibility window loads and applies
252 // the compatibility update
253 let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
254 var doc = compatWindow.document;
255 compatWindow = yield promise_page(compatWindow, "mismatch");
256 var items = get_list_names(doc.getElementById("mismatch.incompatible"));
257 is(items.length, 4, "Should have seen 4 still incompatible items");
258 is(items[0], "Addon3 1.0", "Should have seen addon3 still incompatible");
259 is(items[1], "Addon7 1.0", "Should have seen addon7 still incompatible");
260 is(items[2], "Addon8 1.0", "Should have seen addon8 still incompatible");
261 is(items[3], "Addon9 1.0", "Should have seen addon9 still incompatible");
263 ok(a5.isCompatible, "addon5 should be compatible");
264 ok(a6.isCompatible, "addon6 should be compatible");
266 // Click next to start finding updates for the addons that are still incompatible
267 var button = doc.documentElement.getButton("next");
268 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
270 compatWindow = yield promise_page(compatWindow, "found");
271 ok(doc.getElementById("xpinstallDisabledAlert").hidden,
272 "Install should be allowed");
274 var list = doc.getElementById("found.updates");
275 var items = get_list_names(list);
276 is(items.length, 3, "Should have seen 3 updates available");
277 is(items[0], "Addon7 2.0", "Should have seen update for addon7");
278 is(items[1], "Addon8 2.0", "Should have seen update for addon8");
279 is(items[2], "Addon9 2.0", "Should have seen update for addon9");
281 ok(!doc.documentElement.getButton("next").disabled,
282 "Next button should be enabled");
284 // Uncheck all
285 for (let listItem of list.childNodes)
286 EventUtils.synthesizeMouse(listItem, 2, 2, { }, compatWindow);
288 ok(doc.documentElement.getButton("next").disabled,
289 "Next button should not be enabled");
291 // Check the ones we want to install
292 for (let listItem of list.childNodes) {
293 if (listItem.label != "Addon7 2.0")
294 EventUtils.synthesizeMouse(listItem, 2, 2, { }, compatWindow);
295 }
297 var button = doc.documentElement.getButton("next");
298 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
300 compatWindow = yield promise_page(compatWindow, "finished");
301 var button = doc.documentElement.getButton("finish");
302 ok(!button.hidden, "Finish button should not be hidden");
303 ok(!button.disabled, "Finish button should not be disabled");
304 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
306 compatWindow = yield promise_window_close(compatWindow);
308 // Check that the appropriate add-ons have been updated
309 let [a8, a9] = yield promise_addons_by_ids(["addon8@tests.mozilla.org",
310 "addon9@tests.mozilla.org"]);
311 is(a8.version, "2.0", "addon8 should have updated");
312 is(a9.version, "2.0", "addon9 should have updated");
314 yield promise_uninstall_test_addons();
315 }
316 // );
318 // Test what happens when the user cancels during AddonRepository.repopulateCache()
319 // Add-ons that have updates available should not update if they were disabled before
320 // For this test, addon8 became disabled during update and addon9 was previously disabled,
321 // so addon8 should update and addon9 should not
322 add_task(function cancel_during_repopulate() {
323 Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
324 Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
325 Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
327 let installsDone = promise_observer("TEST:all-updates-done");
329 // Don't pull compatibility data during add-on install
330 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
331 // Set up our test addons so that the server-side JS has a 500ms delay to make
332 // sure we cancel the dialog before we get the data we want to refill our
333 // AddonRepository cache
334 let addonList = [ao5, ao8, ao9, ao10];
335 yield promise_install_test_addons(addonList,
336 TESTROOT + "cancelCompatCheck.sjs?500");
338 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
339 Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, TESTROOT + "browser_bug557956.xml");
341 let [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
342 ok(!a5.isCompatible, "addon5 should not be compatible");
343 ok(!a8.isCompatible, "addon8 should not be compatible");
344 ok(!a9.isCompatible, "addon9 should not be compatible");
346 let compatWindow = yield promise_open_compatibility_window([ao9.id, ...inactiveAddonIds]);
347 var doc = compatWindow.document;
348 yield promise_page(compatWindow, "versioninfo");
350 // Brief delay to let the update window finish requesting all add-ons and start
351 // reloading the addon repository
352 yield delayMS(50);
354 info("Cancel the compatibility check dialog");
355 var button = doc.documentElement.getButton("cancel");
356 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
358 info("Waiting for installs to complete");
359 yield installsDone;
360 ok(!repo.AddonRepository.isSearching, "Background installs are done");
362 // There should be no active updates
363 let getInstalls = Promise.defer();
364 AddonManager.getAllInstalls(getInstalls.resolve);
365 let installs = yield getInstalls.promise;
366 is (installs.length, 0, "There should be no active installs after background installs are done");
368 // addon8 should have updated in the background,
369 // addon9 was listed as previously disabled so it should not have updated
370 let [a5, a8, a9, a10] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id, ao10.id]);
371 ok(a5.isCompatible, "addon5 should be compatible");
372 ok(a8.isCompatible, "addon8 should have been upgraded");
373 ok(!a9.isCompatible, "addon9 should not have been upgraded");
374 ok(!a10.isCompatible, "addon10 should not be compatible");
376 info("Updates done");
377 yield promise_uninstall_test_addons();
378 info("done uninstalling add-ons");
379 });
381 // User cancels after repopulateCache, while we're waiting for the addon.findUpdates()
382 // calls in gVersionInfoPage_onPageShow() to complete
383 // For this test, both addon8 and addon9 were disabled by this update, but addon8
384 // is set to not auto-update, so only addon9 should update in the background
385 add_task(function cancel_during_findUpdates() {
386 Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
387 Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
389 let observeUpdateDone = promise_observer("TEST:addon-repository-data-updated");
390 let installsDone = promise_observer("TEST:all-updates-done");
392 // Don't pull compatibility data during add-on install
393 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
394 // No delay on the .sjs this time because we want the cache to repopulate
395 let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
396 yield promise_install_test_addons(addonList,
397 TESTROOT + "cancelCompatCheck.sjs");
399 let [a8] = yield promise_addons_by_ids([ao8.id]);
400 a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
402 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
403 let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
404 var doc = compatWindow.document;
405 yield promise_page(compatWindow, "versioninfo");
407 info("Waiting for repository-data-updated");
408 yield observeUpdateDone;
410 // Quick wait to make sure the findUpdates calls get queued
411 yield delayMS(5);
413 info("Cancel the compatibility check dialog");
414 var button = doc.documentElement.getButton("cancel");
415 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
417 info("Waiting for installs to complete 2");
418 yield installsDone;
419 ok(!repo.AddonRepository.isSearching, "Background installs are done 2");
421 // addon8 should have updated in the background,
422 // addon9 was listed as previously disabled so it should not have updated
423 let [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
424 ok(a5.isCompatible, "addon5 should be compatible");
425 ok(!a8.isCompatible, "addon8 should not have been upgraded");
426 ok(a9.isCompatible, "addon9 should have been upgraded");
428 let getInstalls = Promise.defer();
429 AddonManager.getAllInstalls(getInstalls.resolve);
430 let installs = yield getInstalls.promise;
431 is (installs.length, 0, "There should be no active installs after the dialog is cancelled 2");
433 info("findUpdates done");
434 yield promise_uninstall_test_addons();
435 });
437 // Cancelling during the 'mismatch' screen allows add-ons that can auto-update
438 // to continue updating in the background and cancels any other updates
439 // Same conditions as the previous test - addon8 and addon9 have updates available,
440 // addon8 is set to not auto-update so only addon9 should become compatible
441 add_task(function cancel_mismatch() {
442 Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
443 Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
445 let observeUpdateDone = promise_observer("TEST:addon-repository-data-updated");
446 let installsDone = promise_observer("TEST:all-updates-done");
448 // Don't pull compatibility data during add-on install
449 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
450 // No delay on the .sjs this time because we want the cache to repopulate
451 let addonList = [ao3, ao5, ao6, ao7, ao8, ao9];
452 yield promise_install_test_addons(addonList,
453 TESTROOT + "cancelCompatCheck.sjs");
455 let [a8] = yield promise_addons_by_ids([ao8.id]);
456 a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
458 // Check that the addons start out not compatible.
459 let [a3, a7, a8, a9] = yield promise_addons_by_ids([ao3.id, ao7.id, ao8.id, ao9.id]);
460 ok(!a3.isCompatible, "addon3 should not be compatible");
461 ok(!a7.isCompatible, "addon7 should not be compatible");
462 ok(!a8.isCompatible, "addon8 should not be compatible");
463 ok(!a9.isCompatible, "addon9 should not be compatible");
465 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
466 let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
467 var doc = compatWindow.document;
468 info("Wait for mismatch page");
469 yield promise_page(compatWindow, "mismatch");
470 info("Click the Don't Check button");
471 var button = doc.documentElement.getButton("cancel");
472 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
474 yield promise_window_close(compatWindow);
475 info("Waiting for installs to complete in cancel_mismatch");
476 yield installsDone;
478 // addon8 should not have updated in the background,
479 // addon9 was listed as previously disabled so it should not have updated
480 let [a5, a8, a9] = yield promise_addons_by_ids([ao5.id, ao8.id, ao9.id]);
481 ok(a5.isCompatible, "addon5 should be compatible");
482 ok(!a8.isCompatible, "addon8 should not have been upgraded");
483 ok(a9.isCompatible, "addon9 should have been upgraded");
485 // Make sure there are no pending addon installs
486 let pInstalls = Promise.defer();
487 AddonManager.getAllInstalls(pInstalls.resolve);
488 let installs = yield pInstalls.promise;
489 ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
491 yield promise_uninstall_test_addons();
492 yield check_addons_uninstalled(addonList);
493 });
495 // Cancelling during the 'mismatch' screen with only add-ons that have
496 // no updates available
497 add_task(function cancel_mismatch_no_updates() {
498 Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
499 Services.prefs.setCharPref(PREF_MIN_PLATFORM_COMPAT, "0");
501 let observeUpdateDone = promise_observer("TEST:addon-repository-data-updated");
503 // Don't pull compatibility data during add-on install
504 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
505 // No delay on the .sjs this time because we want the cache to repopulate
506 let addonList = [ao3, ao5, ao6];
507 yield promise_install_test_addons(addonList,
508 TESTROOT + "cancelCompatCheck.sjs");
510 // Check that the addons start out not compatible.
511 let [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
512 ok(!a3.isCompatible, "addon3 should not be compatible");
513 ok(!a5.isCompatible, "addon7 should not be compatible");
514 ok(!a6.isCompatible, "addon8 should not be compatible");
516 Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
517 let compatWindow = yield promise_open_compatibility_window(inactiveAddonIds);
518 var doc = compatWindow.document;
519 info("Wait for mismatch page");
520 yield promise_page(compatWindow, "mismatch");
521 info("Click the Don't Check button");
522 var button = doc.documentElement.getButton("cancel");
523 EventUtils.synthesizeMouse(button, 2, 2, { }, compatWindow);
525 yield promise_window_close(compatWindow);
527 let [a3, a5, a6] = yield promise_addons_by_ids([ao3.id, ao5.id, ao6.id]);
528 ok(!a3.isCompatible, "addon3 should not be compatible");
529 ok(a5.isCompatible, "addon5 should have become compatible");
530 ok(a6.isCompatible, "addon6 should have become compatible");
532 // Make sure there are no pending addon installs
533 let pInstalls = Promise.defer();
534 AddonManager.getAllInstalls(pInstalls.resolve);
535 let installs = yield pInstalls.promise;
536 ok(installs.length == 0, "No remaining add-on installs (" + installs.toSource() + ")");
538 yield promise_uninstall_test_addons();
539 yield check_addons_uninstalled(addonList);
540 });