diff options
Diffstat (limited to 'browser/base/content/test/general/browser_bug553455.js')
-rw-r--r-- | browser/base/content/test/general/browser_bug553455.js | 1200 |
1 files changed, 1200 insertions, 0 deletions
diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js new file mode 100644 index 000000000..c29a810de --- /dev/null +++ b/browser/base/content/test/general/browser_bug553455.js @@ -0,0 +1,1200 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; +const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/"; +const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; +const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; +const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; +const PROGRESS_NOTIFICATION = "addon-progress"; + +const { REQUIRE_SIGNING } = Cu.import("resource://gre/modules/addons/AddonConstants.jsm", {}); +const { Task } = Cu.import("resource://gre/modules/Task.jsm"); + +var rootDir = getRootDirectory(gTestPath); +var rootPath = rootDir.split('/'); +var chromeName = rootPath[0] + '//' + rootPath[2]; +var croot = chromeName + "/content/browser/toolkit/mozapps/extensions/test/xpinstall/"; +var jar = getJar(croot); +if (jar) { + var tmpdir = extractJarToTmp(jar); + croot = 'file://' + tmpdir.path + '/'; +} +const CHROMEROOT = croot; + +var gApp = document.getElementById("bundle_brand").getString("brandShortName"); +var gVersion = Services.appinfo.version; + +function getObserverTopic(aNotificationId) { + let topic = aNotificationId; + if (topic == "xpinstall-disabled") + topic = "addon-install-disabled"; + else if (topic == "addon-progress") + topic = "addon-install-started"; + else if (topic == "addon-install-restart") + topic = "addon-install-complete"; + return topic; +} + +function waitForProgressNotification(aPanelOpen = false, aExpectedCount = 1) { + return Task.spawn(function* () { + let notificationId = PROGRESS_NOTIFICATION; + info("Waiting for " + notificationId + " notification"); + + let topic = getObserverTopic(notificationId); + + let observerPromise = new Promise(resolve => { + Services.obs.addObserver(function observer(aSubject, aTopic, aData) { + // Ignore the progress notification unless that is the notification we want + if (notificationId != PROGRESS_NOTIFICATION && + aTopic == getObserverTopic(PROGRESS_NOTIFICATION)) { + return; + } + Services.obs.removeObserver(observer, topic); + resolve(); + }, topic, false); + }); + + let panelEventPromise; + if (aPanelOpen) { + panelEventPromise = Promise.resolve(); + } else { + panelEventPromise = new Promise(resolve => { + PopupNotifications.panel.addEventListener("popupshowing", function eventListener() { + PopupNotifications.panel.removeEventListener("popupshowing", eventListener); + resolve(); + }); + }); + } + + yield observerPromise; + yield panelEventPromise; + + info("Saw a notification"); + ok(PopupNotifications.isPanelOpen, "Panel should be open"); + is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications"); + if (PopupNotifications.panel.childNodes.length) { + let nodes = Array.from(PopupNotifications.panel.childNodes); + let notification = nodes.find(n => n.id == notificationId + "-notification"); + ok(notification, `Should have seen the right notification`); + } + + return PopupNotifications.panel; + }); +} + +function waitForNotification(aId, aExpectedCount = 1) { + return Task.spawn(function* () { + info("Waiting for " + aId + " notification"); + + let topic = getObserverTopic(aId); + + let observerPromise = new Promise(resolve => { + Services.obs.addObserver(function observer(aSubject, aTopic, aData) { + // Ignore the progress notification unless that is the notification we want + if (aId != PROGRESS_NOTIFICATION && + aTopic == getObserverTopic(PROGRESS_NOTIFICATION)) { + return; + } + Services.obs.removeObserver(observer, topic); + resolve(); + }, topic, false); + }); + + let panelEventPromise = new Promise(resolve => { + PopupNotifications.panel.addEventListener("PanelUpdated", function eventListener(e) { + // Skip notifications that are not the one that we are supposed to be looking for + if (e.detail.indexOf(aId) == -1) { + return; + } + PopupNotifications.panel.removeEventListener("PanelUpdated", eventListener); + resolve(); + }); + }); + + yield observerPromise; + yield panelEventPromise; + + info("Saw a notification"); + ok(PopupNotifications.isPanelOpen, "Panel should be open"); + is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications"); + if (PopupNotifications.panel.childNodes.length) { + let nodes = Array.from(PopupNotifications.panel.childNodes); + let notification = nodes.find(n => n.id == aId + "-notification"); + ok(notification, `Should have seen the right notification`); + } + + return PopupNotifications.panel; + }); +} + +function waitForNotificationClose() { + return new Promise(resolve => { + info("Waiting for notification to close"); + PopupNotifications.panel.addEventListener("popuphidden", function listener() { + PopupNotifications.panel.removeEventListener("popuphidden", listener, false); + resolve(); + }, false); + }); +} + +function waitForInstallDialog() { + return Task.spawn(function* () { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + yield waitForNotification("addon-install-confirmation"); + return; + } + + info("Waiting for install dialog"); + + let window = yield new Promise(resolve => { + Services.wm.addListener({ + onOpenWindow: function(aXULWindow) { + Services.wm.removeListener(this); + resolve(aXULWindow); + }, + onCloseWindow: function(aXULWindow) { + }, + onWindowTitleChange: function(aXULWindow, aNewTitle) { + } + }); + }); + info("Install dialog opened, waiting for focus"); + + let domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + yield new Promise(resolve => { + waitForFocus(function() { + resolve(); + }, domwindow); + }); + info("Saw install dialog"); + is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open"); + + // Override the countdown timer on the accept button + let button = domwindow.document.documentElement.getButton("accept"); + button.disabled = false; + + return; + }); +} + +function removeTab() { + return Promise.all([ + waitForNotificationClose(), + BrowserTestUtils.removeTab(gBrowser.selectedTab) + ]); +} + +function acceptInstallDialog() { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + document.getElementById("addon-install-confirmation-accept").click(); + } else { + let win = Services.wm.getMostRecentWindow("Addons:Install"); + win.document.documentElement.acceptDialog(); + } +} + +function cancelInstallDialog() { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + document.getElementById("addon-install-confirmation-cancel").click(); + } else { + let win = Services.wm.getMostRecentWindow("Addons:Install"); + win.document.documentElement.cancelDialog(); + } +} + +function waitForSingleNotification(aCallback) { + return Task.spawn(function* () { + while (PopupNotifications.panel.childNodes.length == 2) { + yield new Promise(resolve => executeSoon(resolve)); + + info("Waiting for single notification"); + // Notification should never close while we wait + ok(PopupNotifications.isPanelOpen, "Notification should still be open"); + } + }); +} + +function setupRedirect(aSettings) { + var url = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs?mode=setup"; + for (var name in aSettings) { + url += "&" + name + "=" + aSettings[name]; + } + + var req = new XMLHttpRequest(); + req.open("GET", url, false); + req.send(null); +} + +function getInstalls() { + return new Promise(resolve => { + AddonManager.getAllInstalls(installs => resolve(installs)); + }); +} + +var TESTS = [ +function test_disabledInstall() { + return Task.spawn(function* () { + Services.prefs.setBoolPref("xpinstall.enabled", false); + + let notificationPromise = waitForNotification("xpinstall-disabled"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Enable", "Should have seen the right button"); + is(notification.getAttribute("label"), + "Software installation is currently disabled. Click Enable and try again."); + + let closePromise = waitForNotificationClose(); + // Click on Enable + EventUtils.synthesizeMouseAtCenter(notification.button, {}); + yield closePromise; + + try { + ok(Services.prefs.getBoolPref("xpinstall.enabled"), "Installation should be enabled"); + } + catch (e) { + ok(false, "xpinstall.enabled should be set"); + } + + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + let installs = yield getInstalls(); + is(installs.length, 0, "Shouldn't be any pending installs"); + }); +}, + +function test_blockedInstall() { + return Task.spawn(function* () { + let notificationPromise = waitForNotification("addon-install-blocked"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Allow", "Should have seen the right button"); + is(notification.getAttribute("origin"), "example.com", + "Should have seen the right origin host"); + is(notification.getAttribute("label"), + gApp + " prevented this site from asking you to install software on your computer.", + "Should have seen the right message"); + + let dialogPromise = waitForInstallDialog(); + // Click on Allow + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); + // Notification should have changed to progress notification + ok(PopupNotifications.isPanelOpen, "Notification should still be open"); + notification = panel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + yield dialogPromise; + + notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + panel = yield notificationPromise; + + notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + yield removeTab(); + }); +}, + +function test_whitelistedInstall() { + return Task.spawn(function* () { + let originalTab = gBrowser.selectedTab; + let tab; + gBrowser.selectedTab = originalTab; + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + + triggers).then(newTab => tab = newTab); + yield progressPromise; + yield dialogPromise; + yield BrowserTestUtils.waitForCondition(() => !!tab, "tab should be present"); + + is(gBrowser.selectedTab, tab, + "tab selected in response to the addon-install-confirmation notification"); + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_failedDownload() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let failPromise = waitForNotification("addon-install-failed"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "missing.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + let panel = yield failPromise; + + let notification = panel.childNodes[0]; + is(notification.getAttribute("label"), + "The add-on could not be downloaded because of a connection failure.", + "Should have seen the right message"); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_corruptFile() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let failPromise = waitForNotification("addon-install-failed"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "corrupt.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + let panel = yield failPromise; + + let notification = panel.childNodes[0]; + is(notification.getAttribute("label"), + "The add-on downloaded from this site could not be installed " + + "because it appears to be corrupt.", + "Should have seen the right message"); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_incompatible() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let failPromise = waitForNotification("addon-install-failed"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "incompatible.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + let panel = yield failPromise; + + let notification = panel.childNodes[0]; + is(notification.getAttribute("label"), + "XPI Test could not be installed because it is not compatible with " + + gApp + " " + gVersion + ".", + "Should have seen the right message"); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_restartless() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "restartless.xpi" + })); + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-complete"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.getAttribute("label"), + "XPI Test has been installed successfully.", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 0, "Should be no pending installs"); + + let addon = yield new Promise(resolve => { + AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", result => { + resolve(result); + }); + }); + addon.uninstall(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + + let closePromise = waitForNotificationClose(); + gBrowser.removeTab(gBrowser.selectedTab); + yield closePromise; + }); +}, + +function test_multiple() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Unsigned XPI": "amosigned.xpi", + "Restartless XPI": "restartless.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + let panel = yield progressPromise; + yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "2 add-ons will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + + let addon = yield new Promise(resolve => { + AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function (result) { + resolve(result); + }); + }); + addon.uninstall(); + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_sequential() { + return Task.spawn(function* () { + // This test is only relevant if using the new doorhanger UI + if (!Preferences.get("xpinstall.customConfirmationUI", false)) { + return; + } + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Restartless XPI": "restartless.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + // Should see the right add-on + let container = document.getElementById("addon-install-confirmation-content"); + is(container.childNodes.length, 1, "Should be one item listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on"); + + progressPromise = waitForProgressNotification(true, 2); + triggers = encodeURIComponent(JSON.stringify({ + "Theme XPI": "theme.xpi" + })); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + + // Should still have the right add-on in the confirmation notification + is(container.childNodes.length, 1, "Should be one item listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on"); + + // Wait for the install to complete, we won't see a new confirmation + // notification + yield new Promise(resolve => { + Services.obs.addObserver(function observer() { + Services.obs.removeObserver(observer, "addon-install-confirmation"); + resolve(); + }, "addon-install-confirmation", false); + }); + + // Make sure browser-addons.js executes first + yield new Promise(resolve => executeSoon(resolve)); + + // Should have dropped the progress notification + is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications"); + is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification", + "Should only be showing one install confirmation"); + + // Should still have the right add-on in the confirmation notification + is(container.childNodes.length, 1, "Should be one item listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on"); + + cancelInstallDialog(); + + ok(PopupNotifications.isPanelOpen, "Panel should still be open"); + is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications"); + is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification", + "Should still have an install confirmation open"); + + // Should have the next add-on's confirmation dialog + is(container.childNodes.length, 1, "Should be one item listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on"); + + Services.perms.remove(makeURI("http://example.com"), "install"); + let closePromise = waitForNotificationClose(); + cancelInstallDialog(); + yield closePromise; + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + }); +}, + +function test_someUnverified() { + return Task.spawn(function* () { + // This test is only relevant if using the new doorhanger UI and allowing + // unsigned add-ons + if (!Preferences.get("xpinstall.customConfirmationUI", false) || + Preferences.get("xpinstall.signatures.required", true) || + REQUIRE_SIGNING) { + return; + } + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Extension XPI": "restartless-unsigned.xpi", + "Theme XPI": "theme.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let notification = document.getElementById("addon-install-confirmation-notification"); + let message = notification.getAttribute("label"); + is(message, "Caution: This site would like to install 2 add-ons in " + gApp + + ", some of which are unverified. Proceed at your own risk.", + "Should see the right message"); + + let container = document.getElementById("addon-install-confirmation-content"); + is(container.childNodes.length, 2, "Should be two items listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on"); + is(container.childNodes[0].lastChild.getAttribute("class"), + "addon-install-confirmation-unsigned", "Should have the unverified marker"); + is(container.childNodes[1].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on"); + is(container.childNodes[1].childNodes.length, 1, "Shouldn't have the unverified marker"); + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + yield notificationPromise; + + let [addon, theme] = yield new Promise(resolve => { + AddonManager.getAddonsByIDs(["restartless-xpi@tests.mozilla.org", + "theme-xpi@tests.mozilla.org"], + function(addons) { + resolve(addons); + }); + }); + addon.uninstall(); + // Installing a new theme tries to switch to it, switch back to the + // default theme. + theme.userDisabled = true; + theme.uninstall(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_allUnverified() { + return Task.spawn(function* () { + // This test is only relevant if using the new doorhanger UI and allowing + // unsigned add-ons + if (!Preferences.get("xpinstall.customConfirmationUI", false) || + Preferences.get("xpinstall.signatures.required", true) || + REQUIRE_SIGNING) { + return; + } + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Extension XPI": "restartless-unsigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let notification = document.getElementById("addon-install-confirmation-notification"); + let message = notification.getAttribute("label"); + is(message, "Caution: This site would like to install an unverified add-on in " + gApp + ". Proceed at your own risk."); + + let container = document.getElementById("addon-install-confirmation-content"); + is(container.childNodes.length, 1, "Should be one item listed"); + is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on"); + is(container.childNodes[0].childNodes.length, 1, "Shouldn't have the unverified marker"); + + let notificationPromise = waitForNotification("addon-install-complete"); + acceptInstallDialog(); + yield notificationPromise; + + let addon = yield new Promise(resolve => { + AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(result) { + resolve(result); + }); + }); + addon.uninstall(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_url() { + return Task.spawn(function* () { + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gBrowser.loadURI(TESTROOT + "amosigned.xpi"); + yield progressPromise; + yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + + yield removeTab(); + }); +}, + +function test_localFile() { + return Task.spawn(function* () { + let cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"] + .getService(Components.interfaces.nsIChromeRegistry); + let path; + try { + path = cr.convertChromeURL(makeURI(CHROMEROOT + "corrupt.xpi")).spec; + } catch (ex) { + path = CHROMEROOT + "corrupt.xpi"; + } + + let failPromise = new Promise(resolve => { + Services.obs.addObserver(function observer() { + Services.obs.removeObserver(observer, "addon-install-failed"); + resolve(); + }, "addon-install-failed", false); + }); + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gBrowser.loadURI(path); + yield failPromise; + + // Wait for the browser code to add the failure notification + yield waitForSingleNotification(); + + let notification = PopupNotifications.panel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + is(notification.getAttribute("label"), + "This add-on could not be installed because it appears to be corrupt.", + "Should have seen the right message"); + + yield removeTab(); + }); +}, + +function test_tabClose() { + return Task.spawn(function* () { + if (!Preferences.get("xpinstall.customConfirmationUI", false)) { + runNextTest(); + return; + } + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gBrowser.loadURI(TESTROOT + "amosigned.xpi"); + yield progressPromise; + yield dialogPromise; + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + + let closePromise = waitForNotificationClose(); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + yield closePromise; + + installs = yield getInstalls(); + is(installs.length, 0, "Should be no pending install since the tab is closed"); + }); +}, + +// Add-ons should be cancelled and the install notification destroyed when +// navigating to a new origin +function test_tabNavigate() { + return Task.spawn(function* () { + if (!Preferences.get("xpinstall.customConfirmationUI", false)) { + return; + } + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Extension XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let closePromise = waitForNotificationClose(); + let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gBrowser.loadURI("about:blank"); + yield closePromise; + + let installs = yield getInstalls(); + is(installs.length, 0, "Should be no pending install"); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield loadPromise; + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + }); +}, + +function test_urlBar() { + return Task.spawn(function* () { + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + gURLBar.value = TESTROOT + "amosigned.xpi"; + gURLBar.focus(); + EventUtils.synthesizeKey("VK_RETURN", {}); + + yield progressPromise; + let installDialog = yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(installDialog); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + + yield removeTab(); + }); +}, + +function test_wrongHost() { + return Task.spawn(function* () { + let requestedUrl = TESTROOT2 + "enabled.html"; + gBrowser.selectedTab = gBrowser.addTab(); + + let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, requestedUrl); + gBrowser.loadURI(TESTROOT2 + "enabled.html"); + yield loadedPromise; + + let progressPromise = waitForProgressNotification(); + let notificationPromise = waitForNotification("addon-install-failed"); + gBrowser.loadURI(TESTROOT + "corrupt.xpi"); + yield progressPromise; + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.getAttribute("label"), + "The add-on downloaded from this site could not be installed " + + "because it appears to be corrupt.", + "Should have seen the right message"); + + yield removeTab(); + }); +}, + +function test_reload() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Unsigned XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "XPI Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + function testFail() { + ok(false, "Reloading should not have hidden the notification"); + } + PopupNotifications.panel.addEventListener("popuphiding", testFail, false); + let requestedUrl = TESTROOT2 + "enabled.html"; + let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, requestedUrl); + gBrowser.loadURI(TESTROOT2 + "enabled.html"); + yield loadedPromise; + PopupNotifications.panel.removeEventListener("popuphiding", testFail, false); + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending install"); + installs[0].cancel(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_theme() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "Theme XPI": "theme.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + is(notification.button.label, "Restart Now", "Should have seen the right button"); + is(notification.getAttribute("label"), + "Theme Test will be installed after you restart " + gApp + ".", + "Should have seen the right message"); + + let addon = yield new Promise(resolve => { + AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(result) { + resolve(result); + }); + }); + ok(addon.userDisabled, "Should be switching away from the default theme."); + // Undo the pending theme switch + addon.userDisabled = false; + + addon = yield new Promise(resolve => { + AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(result) { + resolve(result); + }); + }); + isnot(addon, null, "Test theme will have been installed"); + addon.uninstall(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_renotifyBlocked() { + return Task.spawn(function* () { + let notificationPromise = waitForNotification("addon-install-blocked"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + let panel = yield notificationPromise; + + let closePromise = waitForNotificationClose(); + // hide the panel (this simulates the user dismissing it) + panel.hidePopup(); + yield closePromise; + + info("Timeouts after this probably mean bug 589954 regressed"); + + yield new Promise(resolve => executeSoon(resolve)); + + notificationPromise = waitForNotification("addon-install-blocked"); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); + yield notificationPromise; + + let installs = yield getInstalls(); + is(installs.length, 2, "Should be two pending installs"); + + closePromise = waitForNotificationClose(); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + yield closePromise; + + installs = yield getInstalls(); + is(installs.length, 0, "Should have cancelled the installs"); + }); +}, + +function test_renotifyInstalled() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let progressPromise = waitForProgressNotification(); + let dialogPromise = waitForInstallDialog(); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + // Wait for the complete notification + let notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + let panel = yield notificationPromise; + + let closePromise = waitForNotificationClose(); + // hide the panel (this simulates the user dismissing it) + panel.hidePopup(); + yield closePromise; + + // Install another + yield new Promise(resolve => executeSoon(resolve)); + + progressPromise = waitForProgressNotification(); + dialogPromise = waitForInstallDialog(); + gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers); + yield progressPromise; + yield dialogPromise; + + info("Timeouts after this probably mean bug 589954 regressed"); + + // Wait for the complete notification + notificationPromise = waitForNotification("addon-install-restart"); + acceptInstallDialog(); + yield notificationPromise; + + let installs = yield getInstalls(); + is(installs.length, 1, "Should be one pending installs"); + installs[0].cancel(); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield removeTab(); + }); +}, + +function test_cancel() { + return Task.spawn(function* () { + let pm = Services.perms; + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + let notificationPromise = waitForNotification(PROGRESS_NOTIFICATION); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "slowinstall.sjs?file=amosigned.xpi" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + // Close the notification + let anchor = document.getElementById("addons-notification-icon"); + anchor.click(); + // Reopen the notification + anchor.click(); + + ok(PopupNotifications.isPanelOpen, "Notification should still be open"); + is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification"); + notification = panel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + let button = document.getElementById("addon-progress-cancel"); + + // Cancel the download + let install = notification.notification.options.installs[0]; + let cancelledPromise = new Promise(resolve => { + install.addListener({ + onDownloadCancelled: function() { + install.removeListener(this); + resolve(); + } + }); + }); + EventUtils.synthesizeMouseAtCenter(button, {}); + yield cancelledPromise; + + yield new Promise(resolve => executeSoon(resolve)); + + ok(!PopupNotifications.isPanelOpen, "Notification should be closed"); + + let installs = yield getInstalls(); + is(installs.length, 0, "Should be no pending install"); + + Services.perms.remove(makeURI("http://example.com/"), "install"); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + }); +}, + +function test_failedSecurity() { + return Task.spawn(function* () { + Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false); + setupRedirect({ + "Location": TESTROOT + "amosigned.xpi" + }); + + let notificationPromise = waitForNotification("addon-install-blocked"); + let triggers = encodeURIComponent(JSON.stringify({ + "XPI": "redirect.sjs?mode=redirect" + })); + BrowserTestUtils.openNewForegroundTab(gBrowser, SECUREROOT + "installtrigger.html?" + triggers); + let panel = yield notificationPromise; + + let notification = panel.childNodes[0]; + // Click on Allow + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); + + // Notification should have changed to progress notification + ok(PopupNotifications.isPanelOpen, "Notification should still be open"); + is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification"); + notification = panel.childNodes[0]; + is(notification.id, "addon-progress-notification", "Should have seen the progress notification"); + + // Wait for it to fail + yield new Promise(resolve => { + Services.obs.addObserver(function observer() { + Services.obs.removeObserver(observer, "addon-install-failed"); + resolve(); + }, "addon-install-failed", false); + }); + + // Allow the browser code to add the failure notification and then wait + // for the progress notification to dismiss itself + yield waitForSingleNotification(); + is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification"); + notification = panel.childNodes[0]; + is(notification.id, "addon-install-failed-notification", "Should have seen the install fail"); + + Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, true); + yield removeTab(); + }); +} +]; + +var gTestStart = null; + +var XPInstallObserver = { + observe: function (aSubject, aTopic, aData) { + var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo); + info("Observed " + aTopic + " for " + installInfo.installs.length + " installs"); + installInfo.installs.forEach(function(aInstall) { + info("Install of " + aInstall.sourceURI.spec + " was in state " + aInstall.state); + }); + } +}; + +add_task(function* () { + requestLongerTimeout(4); + + Services.prefs.setBoolPref("extensions.logging.enabled", true); + Services.prefs.setBoolPref("extensions.strictCompatibility", true); + Services.prefs.setBoolPref("extensions.install.requireSecureOrigin", false); + Services.prefs.setIntPref("security.dialog_enable_delay", 0); + + Services.obs.addObserver(XPInstallObserver, "addon-install-started", false); + Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false); + Services.obs.addObserver(XPInstallObserver, "addon-install-failed", false); + Services.obs.addObserver(XPInstallObserver, "addon-install-complete", false); + + registerCleanupFunction(function() { + // Make sure no more test parts run in case we were timed out + TESTS = []; + + AddonManager.getAllInstalls(function(aInstalls) { + aInstalls.forEach(function(aInstall) { + aInstall.cancel(); + }); + }); + + Services.prefs.clearUserPref("extensions.logging.enabled"); + Services.prefs.clearUserPref("extensions.strictCompatibility"); + Services.prefs.clearUserPref("extensions.install.requireSecureOrigin"); + Services.prefs.clearUserPref("security.dialog_enable_delay"); + + Services.obs.removeObserver(XPInstallObserver, "addon-install-started"); + Services.obs.removeObserver(XPInstallObserver, "addon-install-blocked"); + Services.obs.removeObserver(XPInstallObserver, "addon-install-failed"); + Services.obs.removeObserver(XPInstallObserver, "addon-install-complete"); + }); + + for (let i = 0; i < TESTS.length; ++i) { + if (gTestStart) + info("Test part took " + (Date.now() - gTestStart) + "ms"); + + ok(!PopupNotifications.isPanelOpen, "Notification should be closed"); + + let installs = yield new Promise(resolve => { + AddonManager.getAllInstalls(function(aInstalls) { + resolve(aInstalls); + }); + }); + + is(installs.length, 0, "Should be no active installs"); + info("Running " + TESTS[i].name); + gTestStart = Date.now(); + yield TESTS[i](); + } +}); |