--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -133,16 +133,20 @@ pref("app.update.log", false);
pref("app.update.backgroundMaxErrors", 10);
// Whether or not app updates are enabled
pref("app.update.enabled", true);
// Whether or not to use the doorhanger application update UI.
pref("app.update.doorhanger", true);
+// Ids of the links to the "What's new" update documentation
+pref("app.update.link.updateAvailableWhatsNew", "update-available-whats-new");
+pref("app.update.link.updateManualWhatsNew", "update-manual-whats-new");
+
// How many times we should let downloads fail before prompting the user to
// download a fresh installer.
pref("app.update.download.promptMaxAttempts", 2);
// How many times we should let an elevation prompt fail before prompting the user to
// download a fresh installer.
pref("app.update.elevation.promptMaxAttempts", 2);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1632,18 +1632,16 @@ var gBrowserInit = {
// initialize the sync UI
gSync.init();
if (AppConstants.MOZ_DATA_REPORTING)
gDataNotificationInfoBar.init();
gBrowserThumbnails.init();
- gMenuButtonUpdateBadge.init();
-
gExtensionsNotifications.init();
let wasMinimized = window.windowState == window.STATE_MINIMIZED;
window.addEventListener("sizemodechange", () => {
let isMinimized = window.windowState == window.STATE_MINIMIZED;
if (wasMinimized != isMinimized) {
wasMinimized = isMinimized;
UpdatePopupNotificationsVisibility();
@@ -1795,18 +1793,16 @@ var gBrowserInit = {
CompactTheme.uninit();
TrackingProtection.uninit();
RefreshBlocker.uninit();
CaptivePortalWatcher.uninit();
- gMenuButtonUpdateBadge.uninit();
-
SidebarUI.uninit();
// Now either cancel delayedStartup, or clean up the services initialized from
// it.
if (this._boundDelayedStartup) {
this._cancelDelayedStartup();
} else {
if (Win7Features)
@@ -2852,234 +2848,16 @@ function UpdatePopupNotificationsVisibil
PopupNotifications.anchorVisibilityChange();
}
function PageProxyClickHandler(aEvent) {
if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
middleMousePaste(aEvent);
}
-// Setup the hamburger button badges for updates, if enabled.
-var gMenuButtonUpdateBadge = {
- kTopics: [
- "update-staged",
- "update-downloaded",
- "update-available",
- "update-error",
- ],
-
- timeouts: [],
-
- get enabled() {
- return Services.prefs.getBoolPref("app.update.doorhanger", false);
- },
-
- get badgeWaitTime() {
- return Services.prefs.getIntPref("app.update.badgeWaitTime", 4 * 24 * 3600); // 4 days
- },
-
- init() {
- if (this.enabled) {
- this.kTopics.forEach(t => {
- Services.obs.addObserver(this, t);
- });
- }
- },
-
- uninit() {
- if (this.enabled) {
- this.kTopics.forEach(t => {
- Services.obs.removeObserver(this, t);
- });
- }
-
- this.reset();
- },
-
- reset() {
- PanelUI.removeNotification(/^update-/);
- this.clearCallbacks();
- },
-
- clearCallbacks() {
- this.timeouts.forEach(t => clearTimeout(t));
- this.timeouts = [];
- },
-
- addTimeout(time, callback) {
- this.timeouts.push(setTimeout(() => {
- this.clearCallbacks();
- callback();
- }, time));
- },
-
- replaceReleaseNotes(update, whatsNewId) {
- let whatsNewLink = document.getElementById(whatsNewId);
- if (update && update.detailsURL) {
- whatsNewLink.href = update.detailsURL;
- } else {
- whatsNewLink.href = Services.urlFormatter.formatURLPref("app.update.url.details");
- }
- },
-
- requestRestart() {
- let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
- .createInstance(Ci.nsISupportsPRBool);
- Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
- if (!cancelQuit.data) {
- Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
- }
- },
-
- openManualUpdateUrl() {
- let manualUpdateUrl = Services.urlFormatter.formatURLPref("app.update.url.manual");
- openUILinkIn(manualUpdateUrl, "tab");
- },
-
- showUpdateNotification(type, dismissed, mainAction) {
- let action = {
- callback(fromDoorhanger) {
- if (fromDoorhanger) {
- Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_DOORHANGER").add(type);
- } else {
- Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_MENU").add(type);
- }
- mainAction();
- }
- };
-
- let secondaryAction = {
- callback() {
- Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_DISMISSED").add(type);
- },
- dismiss: true
- };
-
- PanelUI.showNotification("update-" + type, action, [secondaryAction], { dismissed });
- Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_SHOWN").add(type);
- },
-
- showRestartNotification(dismissed) {
- this.showUpdateNotification("restart", dismissed, () => gMenuButtonUpdateBadge.requestRestart());
- },
-
- showUpdateAvailableNotification(update, dismissed) {
- this.replaceReleaseNotes(update, "update-available-whats-new");
- this.showUpdateNotification("available", dismissed, () => {
- let updateService = Cc["@mozilla.org/updates/update-service;1"]
- .getService(Ci.nsIApplicationUpdateService);
- updateService.downloadUpdate(update, true);
- });
- },
-
- showManualUpdateNotification(update, dismissed) {
- this.replaceReleaseNotes(update, "update-manual-whats-new");
-
- this.showUpdateNotification("manual", dismissed, () => gMenuButtonUpdateBadge.openManualUpdateUrl());
- },
-
- handleUpdateError(update, status) {
- switch (status) {
- case "download-attempt-failed":
- this.clearCallbacks();
- this.showUpdateAvailableNotification(update, false);
- break;
- case "download-attempts-exceeded":
- this.clearCallbacks();
- this.showManualUpdateNotification(update, false);
- break;
- case "elevation-attempt-failed":
- this.clearCallbacks();
- this.showRestartNotification(update, false);
- break;
- case "elevation-attempts-exceeded":
- this.clearCallbacks();
- this.showManualUpdateNotification(update, false);
- break;
- case "check-attempts-exceeded":
- case "unknown":
- // Background update has failed, let's show the UI responsible for
- // prompting the user to update manually.
- this.clearCallbacks();
- this.showManualUpdateNotification(update, false);
- break;
- }
- },
-
- handleUpdateStagedOrDownloaded(update, status) {
- switch (status) {
- case "applied":
- case "pending":
- case "applied-service":
- case "pending-service":
- case "success":
- this.clearCallbacks();
-
- let badgeWaitTimeMs = this.badgeWaitTime * 1000;
- let doorhangerWaitTimeMs = update.promptWaitTime * 1000;
-
- if (badgeWaitTimeMs < doorhangerWaitTimeMs) {
- this.addTimeout(badgeWaitTimeMs, () => {
- this.showRestartNotification(true);
-
- // doorhangerWaitTimeMs is relative to when we initially received
- // the event. Since we've already waited badgeWaitTimeMs, subtract
- // that from doorhangerWaitTimeMs.
- let remainingTime = doorhangerWaitTimeMs - badgeWaitTimeMs;
- this.addTimeout(remainingTime, () => {
- this.showRestartNotification(false);
- });
- });
- } else {
- this.addTimeout(doorhangerWaitTimeMs, () => {
- this.showRestartNotification(false);
- });
- }
- break;
- }
- },
-
- handleUpdateAvailable(update, status) {
- switch (status) {
- case "show-prompt":
- // If an update is available and had the showPrompt flag set, then
- // show an update available doorhanger.
- this.clearCallbacks();
- this.showUpdateAvailableNotification(update, false);
- break;
- case "cant-apply":
- this.clearCallbacks();
- this.showManualUpdateNotification(update, false);
- break;
- }
- },
-
- observe(subject, topic, status) {
- if (!this.enabled) {
- return;
- }
-
- let update = subject && subject.QueryInterface(Ci.nsIUpdate);
-
- switch (topic) {
- case "update-available":
- this.handleUpdateAvailable(update, status);
- break;
- case "update-staged":
- case "update-downloaded":
- this.handleUpdateStagedOrDownloaded(update, status);
- break;
- case "update-error":
- this.handleUpdateError(update, status);
- break;
- }
- }
-};
-
// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
const TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED = 2;
const TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED = 3;
const TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND = 4;
const TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND = 5;
const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
--- a/browser/base/content/moz.build
+++ b/browser/base/content/moz.build
@@ -23,19 +23,16 @@ with Files("newtab/**"):
BUG_COMPONENT = ("Firefox", "New Tab Page")
with Files("pageinfo/**"):
BUG_COMPONENT = ("Firefox", "Page Info Window")
with Files("test/alerts/**"):
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")
-with Files("test/appUpdate/**"):
- BUG_COMPONENT = ("Toolkit", "Application Update")
-
with Files("test/captivePortal/**"):
BUG_COMPONENT = ("Firefox", "General")
with Files("test/chrome/**"):
BUG_COMPONENT = ("Firefox", "General")
with Files("test/forms/**"):
BUG_COMPONENT = ("Core", "Layout: Form Controls")
--- a/browser/base/content/test/appUpdate/head.js
+++ b/browser/base/content/test/appUpdate/head.js
@@ -1,33 +1,40 @@
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
+ "resource://gre/modules/AppMenuNotifications.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateListener",
+ "resource://gre/modules/UpdateListener.jsm");
+
const IS_MACOSX = ("nsILocalFileMac" in Ci);
const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
const BIN_SUFFIX = (IS_WIN ? ".exe" : "");
const FILE_UPDATER_BIN = "updater" + (IS_MACOSX ? ".app" : BIN_SUFFIX);
const FILE_UPDATER_BIN_BAK = FILE_UPDATER_BIN + ".bak";
const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer";
let gRembemberedPrefs = [];
-const DATA_URI_SPEC = "chrome://mochitests/content/browser/browser/base/content/test/appUpdate/";
+const DATA_URI_SPEC = "chrome://mochitests/content/browser/toolkit/mozapps/update/tests/browser/";
var DEBUG_AUS_TEST = true;
var gUseTestUpdater = false;
const LOG_FUNCTION = info;
+const MAX_UPDATE_COPY_ATTEMPTS = 10;
+
/* import-globals-from testConstants.js */
Services.scriptloader.loadSubScript(DATA_URI_SPEC + "testConstants.js", this);
-/* import-globals-from ../../../../../toolkit/mozapps/update/tests/data/shared.js */
+/* import-globals-from ../data/shared.js */
Services.scriptloader.loadSubScript(DATA_URI_SPEC + "shared.js", this);
var gURLData = URL_HOST + "/" + REL_PATH_DATA;
const URL_MANUAL_UPDATE = gURLData + "downloadPage.html";
const gEnv = Cc["@mozilla.org/process/environment;1"].
getService(Components.interfaces.nsIEnvironment);
@@ -99,18 +106,17 @@ function setUpdateTimerPrefs() {
* and additional validation/cleanup callbacks.
* @return A promise which will resolve once all of the steps have been run
* and cleanup has been performed.
*/
function runUpdateTest(updateParams, checkAttempts, steps) {
return (async function() {
registerCleanupFunction(() => {
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "");
- gMenuButtonUpdateBadge.uninit();
- gMenuButtonUpdateBadge.init();
+ UpdateListener.reset();
cleanUpUpdates();
});
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
setUpdateTimerPrefs();
await SpecialPowers.pushPrefEnv({
set: [
[PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
@@ -156,17 +162,17 @@ function runUpdateTest(updateParams, che
* and additional validation/cleanup callbacks.
* @return A promise which will resolve once all of the steps have been run
* and cleanup has been performed.
*/
function runUpdateProcessingTest(updates, steps) {
return (async function() {
registerCleanupFunction(() => {
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "");
- gMenuButtonUpdateBadge.reset();
+ UpdateListener.reset();
cleanUpUpdates();
});
setUpdateTimerPrefs();
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
SpecialPowers.pushPrefEnv({
set: [
[PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
@@ -198,17 +204,17 @@ function processStep(step) {
if (typeof(step) == "function") {
return step();
}
const {notificationId, button, beforeClick, cleanup} = step;
return (async function() {
await BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popupshown");
- const shownNotification = PanelUI.activeNotification.id;
+ const shownNotification = AppMenuNotifications.activeNotification.id;
is(shownNotification, notificationId, "The right notification showed up.");
if (shownNotification != notificationId) {
if (cleanup) {
await cleanup();
}
return;
}
@@ -330,36 +336,39 @@ function moveRealUpdater() {
})();
}
/**
* Copies the test updater so it can be used by tests and tries again on failure
* since Windows debug builds at times leave the file in use. After success it
* will call runTest to continue the test.
*/
-function copyTestUpdater() {
+function copyTestUpdater(attempt = 0) {
return (async function() {
try {
// Copy the test updater
let baseAppDir = getAppBaseDir();
let testUpdaterDir = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
let relPath = REL_PATH_DATA;
let pathParts = relPath.split("/");
for (let i = 0; i < pathParts.length; ++i) {
testUpdaterDir.append(pathParts[i]);
}
let testUpdater = testUpdaterDir.clone();
testUpdater.append(FILE_UPDATER_BIN);
+
testUpdater.copyToFollowingLinks(baseAppDir, FILE_UPDATER_BIN);
} catch (e) {
- logTestInfo("Attempt to copy the test updater failed... " +
- "will try again, Exception: " + e);
- await delay();
- await copyTestUpdater();
+ if (attempt < MAX_UPDATE_COPY_ATTEMPTS) {
+ logTestInfo("Attempt to copy the test updater failed... " +
+ "will try again, Exception: " + e);
+ await delay();
+ await copyTestUpdater(attempt + 1);
+ }
}
})();
}
/**
* Restores the updater that was backed up. This is called in setupTestUpdater
* before the backup of the real updater is done in case the previous test
* failed to restore the updater, in finishTestDefaultWaitForWindowClosed when
--- a/browser/base/content/test/appUpdate/testConstants.js
+++ b/browser/base/content/test/appUpdate/testConstants.js
@@ -1,4 +1,4 @@
-const REL_PATH_DATA = "browser/browser/base/content/test/appUpdate/";
+const REL_PATH_DATA = "browser/toolkit/mozapps/update/tests/browser/";
const URL_HOST = "http://example.com";
const URL_PATH_UPDATE_XML = "/" + REL_PATH_DATA + "update.sjs";
const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -36,33 +36,23 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/tabcrashed/browser.ini',
'content/test/tabPrompts/browser.ini',
'content/test/tabs/browser.ini',
'content/test/urlbar/browser.ini',
'content/test/webextensions/browser.ini',
'content/test/webrtc/browser.ini',
]
-if CONFIG['MOZ_UPDATER']:
- BROWSER_CHROME_MANIFESTS += ['content/test/appUpdate/browser.ini']
-
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
-TEST_HARNESS_FILES.testing.mochitest.browser.browser.base.content.test.appUpdate += [
- '/toolkit/mozapps/update/tests/chrome/update.sjs',
- '/toolkit/mozapps/update/tests/data/shared.js',
- '/toolkit/mozapps/update/tests/data/sharedUpdateXML.js',
- '/toolkit/mozapps/update/tests/data/simple.mar',
-]
-
JAR_MANIFESTS += ['jar.mn']
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -724,16 +724,20 @@ const PanelUI = {
_showNotificationPanel(notification) {
this._refreshNotificationPanel(notification);
if (this.isNotificationPanelOpen) {
return;
}
+ if (notification.options.beforeShowDoorhanger) {
+ notification.options.beforeShowDoorhanger(document);
+ }
+
let anchor = this._getPanelAnchor(this.menuButton);
this.notificationPanel.hidden = false;
this.notificationPanel.openPopup(anchor, "bottomcenter topright");
},
_clearNotificationPanel() {
for (let popupnotification of this.notificationPanel.children) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -31,17 +31,17 @@ XPCOMUtils.defineLazyGetter(this, "Weave
FileUtils:false, FormValidationHandler:false, Integration:false,
LightweightThemeManager:false, LoginHelper:false, LoginManagerParent:false,
NetUtil:false, NewTabUtils:false, OS:false,
PageThumbs:false, PdfJs:false, PermissionUI:false, PlacesBackups:false,
PlacesUtils:false, PluralForm:false, PrivateBrowsingUtils:false,
ProcessHangMonitor:false, ReaderParent:false, RecentWindow:false,
RemotePrompt:false, SessionStore:false,
ShellService:false, SimpleServiceDiscovery:false, TabCrashHandler:false,
- Task:false, UITour:false, WebChannel:false,
+ Task:false, UITour:false, UpdateListener:false, WebChannel:false,
WindowsRegistry:false, webrtcUI:false */
/**
* IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
* XXX Bug 1325373 is for making eslint detect these automatically.
*/
let initializedModules = {};
@@ -84,16 +84,17 @@ let initializedModules = {};
["RecentWindow", "resource:///modules/RecentWindow.jsm"],
["RemotePrompt", "resource:///modules/RemotePrompt.jsm"],
["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
["ShellService", "resource:///modules/ShellService.jsm"],
["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
["Task", "resource://gre/modules/Task.jsm"],
["UITour", "resource:///modules/UITour.jsm"],
+ ["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
["WebChannel", "resource://gre/modules/WebChannel.jsm"],
["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
].forEach(([name, resource, init]) => {
if (init) {
XPCOMUtils.defineLazyGetter(this, name, () => {
Cu.import(resource, initializedModules);
initializedModules[name][init]();
@@ -119,16 +120,23 @@ XPCOMUtils.defineLazyGetter(this, "gBran
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings.createBundle("chrome://browser/locale/browser.properties");
});
const global = this;
const listeners = {
+ observers: {
+ "update-staged": ["UpdateListener"],
+ "update-downloaded": ["UpdateListener"],
+ "update-available": ["UpdateListener"],
+ "update-error": ["UpdateListener"],
+ },
+
ppmm: {
// PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init
"ContentPrefs:FunctionCall": ["ContentPrefServiceParent"],
"ContentPrefs:AddObserverForName": ["ContentPrefServiceParent"],
"ContentPrefs:RemoveObserverForName": ["ContentPrefServiceParent"],
// PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN ContentPrefServiceParent.init
"FeedConverter:addLiveBookmark": ["Feeds"],
"WCCR:setAutoHandler": ["Feeds"],
@@ -160,29 +168,43 @@ const listeners = {
"rtcpeer:CancelRequest": ["webrtcUI"],
"rtcpeer:Request": ["webrtcUI"],
"webrtc:CancelRequest": ["webrtcUI"],
"webrtc:Request": ["webrtcUI"],
"webrtc:StopRecording": ["webrtcUI"],
"webrtc:UpdateBrowserIndicators": ["webrtcUI"],
},
+ observe(subject, topic, data) {
+ for (let module of this.observers[topic]) {
+ try {
+ global[module].observe(subject, topic, data);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ },
+
receiveMessage(modules, data) {
let val;
for (let module of modules[data.name]) {
try {
val = global[module].receiveMessage(data) || val;
} catch (e) {
Cu.reportError(e);
}
}
return val;
},
init() {
+ for (let observer of Object.keys(this.observers)) {
+ Services.obs.addObserver(this, observer);
+ }
+
let receiveMessageMM = this.receiveMessage.bind(this, this.mm);
for (let message of Object.keys(this.mm)) {
Services.mm.addMessageListener(message, receiveMessageMM);
}
let receiveMessagePPMM = this.receiveMessage.bind(this, this.ppmm);
for (let message of Object.keys(this.ppmm)) {
Services.ppmm.addMessageListener(message, receiveMessagePPMM);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/UpdateListener.jsm
@@ -0,0 +1,222 @@
+/* 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 = ["UpdateListener"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
+ "resource://gre/modules/AppMenuNotifications.jsm");
+
+// Setup the hamburger button badges for updates, if enabled.
+var UpdateListener = {
+ timeouts: [],
+
+ get enabled() {
+ return Services.prefs.getBoolPref("app.update.doorhanger", false);
+ },
+
+ get badgeWaitTime() {
+ return Services.prefs.getIntPref("app.update.badgeWaitTime", 4 * 24 * 3600); // 4 days
+ },
+
+ init() {
+ },
+
+ uninit() {
+ this.reset();
+ },
+
+ reset() {
+ AppMenuNotifications.removeNotification(/^update-/);
+ this.clearCallbacks();
+ },
+
+ clearCallbacks() {
+ this.timeouts.forEach(t => clearTimeout(t));
+ this.timeouts = [];
+ },
+
+ addTimeout(time, callback) {
+ this.timeouts.push(setTimeout(() => {
+ this.clearCallbacks();
+ callback();
+ }, time));
+ },
+
+ replaceReleaseNotes(doc, update, whatsNewId) {
+ let whatsNewLinkId = Services.prefs.getCharPref(`app.update.link.${whatsNewId}`, "");
+ if (whatsNewLinkId) {
+ let whatsNewLink = doc.getElementById(whatsNewLinkId);
+ if (update && update.detailsURL) {
+ whatsNewLink.href = update.detailsURL;
+ } else {
+ whatsNewLink.href = Services.urlFormatter.formatURLPref("app.update.url.details");
+ }
+ }
+ },
+
+ requestRestart() {
+ let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+ if (!cancelQuit.data) {
+ Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
+ }
+ },
+
+ openManualUpdateUrl(win) {
+ let manualUpdateUrl = Services.urlFormatter.formatURLPref("app.update.url.manual");
+ win.openURL(manualUpdateUrl);
+ },
+
+ showUpdateNotification(type, dismissed, mainAction, beforeShowDoorhanger) {
+ let action = {
+ callback(win, fromDoorhanger) {
+ if (fromDoorhanger) {
+ Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_DOORHANGER").add(type);
+ } else {
+ Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_MAIN_ACTION_MENU").add(type);
+ }
+ mainAction(win);
+ }
+ };
+
+ let secondaryAction = {
+ callback() {
+ Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_DISMISSED").add(type);
+ },
+ dismiss: true
+ };
+
+ AppMenuNotifications.showNotification("update-" + type,
+ action,
+ secondaryAction,
+ { dismissed, beforeShowDoorhanger });
+ Services.telemetry.getHistogramById("UPDATE_NOTIFICATION_SHOWN").add(type);
+ },
+
+ showRestartNotification(dismissed) {
+ this.showUpdateNotification("restart", dismissed, () => this.requestRestart());
+ },
+
+ showUpdateAvailableNotification(update, dismissed) {
+ this.showUpdateNotification("available", dismissed, () => {
+ let updateService = Cc["@mozilla.org/updates/update-service;1"]
+ .getService(Ci.nsIApplicationUpdateService);
+ updateService.downloadUpdate(update, true);
+ }, doc => this.replaceReleaseNotes(doc, update, "updateAvailableWhatsNew"));
+ },
+
+ showManualUpdateNotification(update, dismissed) {
+ this.showUpdateNotification("manual",
+ dismissed,
+ win => this.openManualUpdateUrl(win),
+ doc => this.replaceReleaseNotes(doc, update, "updateManualWhatsNew"));
+ },
+
+ handleUpdateError(update, status) {
+ switch (status) {
+ case "download-attempt-failed":
+ this.clearCallbacks();
+ this.showUpdateAvailableNotification(update, false);
+ break;
+ case "download-attempts-exceeded":
+ this.clearCallbacks();
+ this.showManualUpdateNotification(update, false);
+ break;
+ case "elevation-attempt-failed":
+ this.clearCallbacks();
+ this.showRestartNotification(update, false);
+ break;
+ case "elevation-attempts-exceeded":
+ this.clearCallbacks();
+ this.showManualUpdateNotification(update, false);
+ break;
+ case "check-attempts-exceeded":
+ case "unknown":
+ // Background update has failed, let's show the UI responsible for
+ // prompting the user to update manually.
+ this.clearCallbacks();
+ this.showManualUpdateNotification(update, false);
+ break;
+ }
+ },
+
+ handleUpdateStagedOrDownloaded(update, status) {
+ switch (status) {
+ case "applied":
+ case "pending":
+ case "applied-service":
+ case "pending-service":
+ case "success":
+ this.clearCallbacks();
+
+ let badgeWaitTimeMs = this.badgeWaitTime * 1000;
+ let doorhangerWaitTimeMs = update.promptWaitTime * 1000;
+
+ if (badgeWaitTimeMs < doorhangerWaitTimeMs) {
+ this.addTimeout(badgeWaitTimeMs, () => {
+ this.showRestartNotification(true);
+
+ // doorhangerWaitTimeMs is relative to when we initially received
+ // the event. Since we've already waited badgeWaitTimeMs, subtract
+ // that from doorhangerWaitTimeMs.
+ let remainingTime = doorhangerWaitTimeMs - badgeWaitTimeMs;
+ this.addTimeout(remainingTime, () => {
+ this.showRestartNotification(false);
+ });
+ });
+ } else {
+ this.addTimeout(doorhangerWaitTimeMs, () => {
+ this.showRestartNotification(false);
+ });
+ }
+ break;
+ }
+ },
+
+ handleUpdateAvailable(update, status) {
+ switch (status) {
+ case "show-prompt":
+ // If an update is available and had the showPrompt flag set, then
+ // show an update available doorhanger.
+ this.clearCallbacks();
+ this.showUpdateAvailableNotification(update, false);
+ break;
+ case "cant-apply":
+ this.clearCallbacks();
+ this.showManualUpdateNotification(update, false);
+ break;
+ }
+ },
+
+ observe(subject, topic, status) {
+ if (!this.enabled) {
+ return;
+ }
+
+ let update = subject && subject.QueryInterface(Ci.nsIUpdate);
+
+ switch (topic) {
+ case "update-available":
+ this.handleUpdateAvailable(update, status);
+ break;
+ case "update-staged":
+ case "update-downloaded":
+ this.handleUpdateStagedOrDownloaded(update, status);
+ break;
+ case "update-error":
+ this.handleUpdateError(update, status);
+ break;
+ }
+ }
+};
--- a/toolkit/mozapps/update/moz.build
+++ b/toolkit/mozapps/update/moz.build
@@ -19,15 +19,16 @@ TEST_DIRS += ['tests']
EXTRA_COMPONENTS += [
'nsUpdateService.js',
'nsUpdateService.manifest',
'nsUpdateServiceStub.js',
]
EXTRA_JS_MODULES += [
+ 'UpdateListener.jsm',
'UpdateTelemetry.jsm',
]
JAR_MANIFESTS += ['jar.mn']
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'Application Update')
rename from browser/base/content/test/appUpdate/.eslintrc.js
rename to toolkit/mozapps/update/tests/browser/.eslintrc.js
rename from browser/base/content/test/appUpdate/browser.ini
rename to toolkit/mozapps/update/tests/browser/browser.ini
rename from browser/base/content/test/appUpdate/browser_updatesBackgroundWindow.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesBackgroundWindow.js
--- a/browser/base/content/test/appUpdate/browser_updatesBackgroundWindow.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesBackgroundWindow.js
@@ -21,14 +21,14 @@ add_task(async function testUpdatesBackg
checkWhatsNewLink("update-available-whats-new");
let buttonEl = getNotificationButton(window, "update-available", "button");
buttonEl.click();
},
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesBackgroundWindowFailures.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesBackgroundWindowFailures.js
--- a/browser/base/content/test/appUpdate/browser_updatesBackgroundWindowFailures.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesBackgroundWindowFailures.js
@@ -35,13 +35,12 @@ add_task(async function testBackgroundWi
{
notificationId: "update-manual",
button: "button",
async cleanup() {
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.");
gBrowser.removeTab(gBrowser.selectedTab);
- gMenuButtonUpdateBadge.reset();
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesBasicPrompt.js
--- a/browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesBasicPrompt.js
@@ -10,13 +10,13 @@ add_task(async function testBasicPrompt(
beforeClick() {
checkWhatsNewLink("update-available-whats-new");
}
},
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesBasicPromptNoStaging.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesBasicPromptNoStaging.js
--- a/browser/base/content/test/appUpdate/browser_updatesBasicPromptNoStaging.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesBasicPromptNoStaging.js
@@ -10,13 +10,13 @@ add_task(async function testBasicPromptN
beforeClick() {
checkWhatsNewLink("update-available-whats-new");
}
},
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesCantApply.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCantApply.js
rename from browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
--- a/browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
@@ -1,13 +1,13 @@
add_task(async function testCompleteAndPartialPatchesWithBadCompleteSize() {
let updateParams = "invalidCompleteSize=1&promptWaitTime=0";
await runUpdateTest(updateParams, 1, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
--- a/browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
@@ -1,13 +1,13 @@
add_task(async function testCompleteAndPartialPatchesWithBadPartialSize() {
let updateParams = "invalidPartialSize=1&promptWaitTime=0";
await runUpdateTest(updateParams, 1, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadSizes.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCompleteAndPartialPatchesWithBadSizes.js
rename from browser/base/content/test/appUpdate/browser_updatesCompletePatchApplyFailure.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCompletePatchApplyFailure.js
rename from browser/base/content/test/appUpdate/browser_updatesCompletePatchWithBadCompleteSize.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesCompletePatchWithBadCompleteSize.js
rename from browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesDownloadFailures.js
--- a/browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesDownloadFailures.js
@@ -20,13 +20,12 @@ add_task(async function testDownloadFail
{
notificationId: "update-manual",
button: "button",
async cleanup() {
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.");
gBrowser.removeTab(gBrowser.selectedTab);
- gMenuButtonUpdateBadge.reset();
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesMalformedXml.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesMalformedXml.js
--- a/browser/base/content/test/appUpdate/browser_updatesMalformedXml.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesMalformedXml.js
@@ -17,13 +17,12 @@ add_task(async function testMalformedXml
beforeClick() {
checkWhatsNewLink("update-manual-whats-new", updateDetailsUrl);
},
async cleanup() {
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec,
URL_MANUAL_UPDATE, "Landed on manual update page.")
gBrowser.removeTab(gBrowser.selectedTab);
- gMenuButtonUpdateBadge.reset();
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailure.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesPartialPatchApplyFailure.js
rename from browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
--- a/browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
+++ b/toolkit/mozapps/update/tests/browser/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
@@ -10,13 +10,13 @@ add_task(async function testPartialPatch
null, null, null, null, "false",
null, null, null, null, promptWaitTime);
await runUpdateProcessingTest(updates, [
{
notificationId: "update-restart",
button: "secondarybutton",
cleanup() {
- PanelUI.removeNotification(/.*/);
+ AppMenuNotifications.removeNotification(/.*/);
}
},
]);
});
rename from browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js
rename from browser/base/content/test/appUpdate/browser_updatesPartialPatchWithBadPartialSize.js
rename to toolkit/mozapps/update/tests/browser/browser_updatesPartialPatchWithBadPartialSize.js
--- a/toolkit/mozapps/update/tests/moz.build
+++ b/toolkit/mozapps/update/tests/moz.build
@@ -3,16 +3,19 @@
# 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/.
HAS_MISC_RULE = True
FINAL_TARGET = '_tests/xpcshell/toolkit/mozapps/update/tests/data'
+if not CONFIG['MOZ_SUITE']:
+ BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']
+
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += [
'unit_aus_update/xpcshell.ini',
'unit_base_updater/xpcshell.ini'
]
if CONFIG['MOZ_MAINTENANCE_SERVICE']:
@@ -55,16 +58,23 @@ if CONFIG['MOZ_MAINTENANCE_SERVICE']:
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['UNICODE'] = True
DEFINES['_UNICODE'] = True
USE_STATIC_LIBS = True
if CONFIG['GNU_CC']:
WIN32_EXE_LDFLAGS += ['-municode']
+TEST_HARNESS_FILES.testing.mochitest.browser.toolkit.mozapps.update.tests.browser += [
+ 'chrome/update.sjs',
+ 'data/shared.js',
+ 'data/sharedUpdateXML.js',
+ 'data/simple.mar',
+]
+
TEST_HARNESS_FILES.testing.mochitest.chrome.toolkit.mozapps.update.tests.data += [
'data/shared.js',
'data/sharedUpdateXML.js',
'data/simple.mar',
]
FINAL_TARGET_FILES += [
'data/complete.exe',
--- a/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
+++ b/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
@@ -4,17 +4,17 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# For changes here, also consider ../Makefile.in
XPCSHELLTESTDIR = $(topobjdir)/_tests/xpcshell/toolkit/mozapps/update/tests
MOCHITESTCHROMEDIR = $(topobjdir)/_tests/testing/mochitest/chrome/toolkit/mozapps/update/tests
ifeq (,$(MOZ_SUITE)$(MOZ_THUNDERBIRD))
-MOCHITESTBROWSERDIR = $(topobjdir)/_tests/testing/mochitest/browser/browser/base/content/test/appUpdate
+MOCHITESTBROWSERDIR = $(topobjdir)/_tests/testing/mochitest/browser/toolkit/mozapps/update/tests/browser
endif
include $(topsrcdir)/config/rules.mk
ifndef MOZ_WINCONSOLE
ifdef MOZ_DEBUG
MOZ_WINCONSOLE = 1
else