Backed out 2 changesets (bug 893505) for failing eslint. r=backout
authorSebastian Hengst <archaeopteryx@coole-files.de>
Fri, 07 Apr 2017 01:04:54 +0200
changeset 351689 5f3b59dd91acce050b9a3aa096731b49c8d9bfd7
parent 351688 b13d3c03b5adb929f0672c08ce1c5de6019a3333
child 351690 d4b635aa0c2ee4a98c0aa15bec4f2736a0a8bd19
push id88921
push userarchaeopteryx@coole-files.de
push dateThu, 06 Apr 2017 23:05:10 +0000
treeherdermozilla-inbound@5f3b59dd91ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs893505
milestone55.0a1
backs out5880d471d75caf7ca6ce8d0848737a78131151da
3246d7cb2729cfa26e127e5411cb31193ca227ec
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 2 changesets (bug 893505) for failing eslint. r=backout Backed out changeset 5880d471d75c (bug 893505) Backed out changeset 3246d7cb2729 (bug 893505)
browser/app/profile/firefox.js
browser/base/content/browser-addons.js
browser/base/content/browser-fullScreenAndPointerLock.js
browser/base/content/browser-fxaccounts.js
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/test/appUpdate/.eslintrc.js
browser/base/content/test/appUpdate/browser.ini
browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
browser/base/content/test/appUpdate/browser_updatesBasicPromptNoStaging.js
browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadSizes.js
browser/base/content/test/appUpdate/browser_updatesCompletePatchApplyFailure.js
browser/base/content/test/appUpdate/browser_updatesCompletePatchWithBadCompleteSize.js
browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
browser/base/content/test/appUpdate/browser_updatesMalformedXml.js
browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailure.js
browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js
browser/base/content/test/appUpdate/browser_updatesPartialPatchWithBadPartialSize.js
browser/base/content/test/appUpdate/downloadPage.html
browser/base/content/test/appUpdate/head.js
browser/base/content/test/appUpdate/testConstants.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_menuButtonBadgeManager.js
browser/base/moz.build
browser/branding/official/pref/firefox-branding.js
browser/components/customizableui/content/panelUI.inc.xul
browser/components/customizableui/content/panelUI.js
browser/components/customizableui/test/browser.ini
browser/components/customizableui/test/browser_panelUINotifications.js
browser/components/downloads/content/indicator.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/themes/shared/customizableui/panelUI.inc.css
browser/themes/shared/notification-icons.inc.css
browser/themes/shared/notification-icons.svg
browser/themes/shared/update-badge.svg
testing/talos/talos/config.py
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/tests/chrome/chrome.ini
toolkit/mozapps/update/tests/chrome/testConstants.js
toolkit/mozapps/update/tests/chrome/update.sjs
toolkit/mozapps/update/tests/chrome/utils.js
toolkit/mozapps/update/tests/data/shared.js
toolkit/mozapps/update/tests/data/sharedUpdateXML.js
toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -110,35 +110,30 @@ pref("app.update.log", false);
 // The number of general background check failures to allow before notifying the
 // user of the failure. User initiated update checks always notify the user of
 // the failure.
 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);
-
-// 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);
-
 // If set to true, the Update Service will automatically download updates when
 // app updates are enabled per the app.update.enabled preference and if the user
 // can apply updates.
 pref("app.update.auto", true);
 
 // If set to true, the Update Service will present no UI for any event.
 pref("app.update.silent", false);
 
+// If set to true, the hamburger button will show badges for update events.
+#ifndef RELEASE_OR_BETA
+pref("app.update.badge", true);
+#else
+pref("app.update.badge", false);
+#endif
 // app.update.badgeWaitTime is in branding section
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
 pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -499,19 +499,20 @@ const gExtensionsNotifications = {
     }
     ExtensionsUI.off("change", this.boundUpdate);
   },
 
   updateAlerts() {
     let sideloaded = ExtensionsUI.sideloaded;
     let updates = ExtensionsUI.updates;
     if (sideloaded.size + updates.size == 0) {
-      PanelUI.removeNotification("addon-alert");
+      gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_ADDONS);
     } else {
-      PanelUI.showBadgeOnlyNotification("addon-alert");
+      gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_ADDONS,
+                                       "addon-alert");
     }
 
     let container = document.getElementById("PanelUI-footer-addons");
 
     while (container.firstChild) {
       container.firstChild.remove();
     }
 
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -532,31 +532,26 @@ var FullScreen = {
   },
 
   _setPopupOpen(aEvent) {
     // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
     // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
     // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
     // toggles chrome when moving mouse to the top, it doesn't go away again.
     if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
-        aEvent.target.localName != "tooltip" && aEvent.target.localName != "window" &&
-        aEvent.target.getAttribute("nopreventnavboxhide") != "true")
+        aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
       FullScreen._isPopupOpen = true;
     else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
              aEvent.target.localName != "window") {
       FullScreen._isPopupOpen = false;
       // Try again to hide toolbar when we close the popup.
       FullScreen.hideNavToolbox(true);
     }
   },
 
-  get navToolboxHidden() {
-    return this._isChromeCollapsed;
-  },
-
   // Autohide helpers for the context menu item
   getAutohide(aItem) {
     aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
   },
   setAutohide() {
     gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
     // Try again to hide toolbar when we change the pref.
     FullScreen.hideNavToolbox(true);
@@ -579,17 +574,16 @@ var FullScreen = {
         bottom: rect.bottom,
         left: rect.left,
         right: rect.right
       };
       MousePosTracker.addListener(this);
     }
 
     this._isChromeCollapsed = false;
-    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
   },
 
   hideNavToolbox(aAnimate = false) {
     if (this._isChromeCollapsed || !this._safeToCollapse())
       return;
 
     this._fullScrToggler.hidden = false;
 
@@ -603,18 +597,16 @@ var FullScreen = {
       };
       gNavToolbox.addEventListener("transitionend", listener, true);
       this._fullScrToggler.hidden = true;
     }
 
     gNavToolbox.style.marginTop =
       -gNavToolbox.getBoundingClientRect().height + "px";
     this._isChromeCollapsed = true;
-    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
-
     MousePosTracker.removeListener(this);
   },
 
   _updateToolbars(aEnterFS) {
     for (let el of document.querySelectorAll("toolbar[fullscreentoolbar=true]")) {
       if (aEnterFS) {
         // Give the main nav bar and the tab bar the fullscreen context menu,
         // otherwise remove context menu to prevent breakage
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -212,19 +212,19 @@ var gFxAccounts = {
           this.panelUIFooter.setAttribute("fxastatus", "signedin");
           this.panelUILabel.setAttribute("label", userData.email);
         }
         if (profileInfoEnabled) {
           this.panelUIFooter.setAttribute("fxaprofileimage", "enabled");
         }
       }
       if (showErrorBadge) {
-        PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
+        gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
       } else {
-        PanelUI.removeNotification("fxa-needs-authentication");
+        gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
       }
     }
 
     let updateWithProfile = (profile) => {
       if (profileInfoEnabled) {
         if (profile.displayName) {
           this.panelUILabel.setAttribute("label", profile.displayName);
         }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1178,17 +1178,17 @@ toolbarpaletteitem[place="palette"][hidd
 
 #customization-palette .toolbarpaletteitem-box {
   -moz-box-pack: center;
   -moz-box-flex: 1;
   width: 10em;
   max-width: 10em;
 }
 
-#main-window[customizing=true] .PanelUI-notification-menu-item {
+#main-window[customizing=true] #PanelUI-update-status {
   display: none;
 }
 
 /* UI Tour */
 
 @keyframes uitour-wobble {
   from {
     transform: rotate(0deg) translateX(3px) rotate(0deg);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1510,16 +1510,18 @@ var gBrowserInit = {
     gSyncUI.init();
     gFxAccounts.init();
 
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
     gBrowserThumbnails.init();
 
+    gMenuButtonBadgeManager.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) {
@@ -1686,16 +1688,18 @@ var gBrowserInit = {
     TrackingProtection.uninit();
 
     RefreshBlocker.uninit();
 
     CaptivePortalWatcher.uninit();
 
     gMenuButtonUpdateBadge.uninit();
 
+    gMenuButtonBadgeManager.uninit();
+
     SidebarUI.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features)
@@ -2740,227 +2744,195 @@ function UpdatePopupNotificationsVisibil
   PopupNotifications.anchorVisibilityChange();
 }
 
 function PageProxyClickHandler(aEvent) {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
+var gMenuButtonBadgeManager = {
+  BADGEID_APPUPDATE: "update",
+  BADGEID_DOWNLOAD: "download",
+  BADGEID_FXA: "fxa",
+  BADGEID_ADDONS: "addons",
+
+  fxaBadge: null,
+  downloadBadge: null,
+  appUpdateBadge: null,
+  addonsBadge: null,
+
+  init() {
+    PanelUI.panel.addEventListener("popupshowing", this, true);
+  },
+
+  uninit() {
+    PanelUI.panel.removeEventListener("popupshowing", this, true);
+  },
+
+  handleEvent(e) {
+    if (e.type === "popupshowing") {
+      this.clearBadges();
+    }
+  },
+
+  _showBadge() {
+    let badgeToShow = this.downloadBadge || this.appUpdateBadge || this.fxaBadge || this.addonsBadge;
+
+    if (badgeToShow) {
+      PanelUI.menuButton.setAttribute("badge-status", badgeToShow);
+    } else {
+      PanelUI.menuButton.removeAttribute("badge-status");
+    }
+  },
+
+  _changeBadge(badgeId, badgeStatus = null) {
+    if (badgeId == this.BADGEID_APPUPDATE) {
+      this.appUpdateBadge = badgeStatus;
+    } else if (badgeId == this.BADGEID_DOWNLOAD) {
+      this.downloadBadge = badgeStatus;
+    } else if (badgeId == this.BADGEID_FXA) {
+      this.fxaBadge = badgeStatus;
+    } else if (badgeId == this.BADGEID_ADDONS) {
+      this.addonsBadge = badgeStatus;
+    } else {
+      Cu.reportError("The badge ID '" + badgeId + "' is unknown!");
+    }
+    this._showBadge();
+  },
+
+  addBadge(badgeId, badgeStatus) {
+    if (!badgeStatus) {
+      Cu.reportError("badgeStatus must be defined");
+      return;
+    }
+    this._changeBadge(badgeId, badgeStatus);
+  },
+
+  removeBadge(badgeId) {
+    this._changeBadge(badgeId);
+  },
+
+  clearBadges() {
+    this.appUpdateBadge = null;
+    this.downloadBadge = null;
+    this.fxaBadge = null;
+    this._showBadge();
+  }
+};
+
 // 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
-  },
+  enabled: false,
+  badgeWaitTime: 0,
+  timer: null,
+  cancelObserverRegistered: false,
 
   init() {
+    this.enabled = Services.prefs.getBoolPref("app.update.badge", false);
     if (this.enabled) {
-      this.kTopics.forEach(t => {
-        Services.obs.addObserver(this, t, false);
-      });
+      this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime",
+                                                     345600); // 4 days
+      Services.obs.addObserver(this, "update-staged", false);
+      Services.obs.addObserver(this, "update-downloaded", false);
     }
   },
 
   uninit() {
+    if (this.timer)
+      this.timer.cancel();
     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();
+      Services.obs.removeObserver(this, "update-staged");
+      Services.obs.removeObserver(this, "update-downloaded");
+      this.enabled = false;
+    }
+    if (this.cancelObserverRegistered) {
+      Services.obs.removeObserver(this, "update-canceled");
+      this.cancelObserverRegistered = false;
+    }
+  },
+
+  onMenuPanelCommand(event) {
+    if (event.originalTarget.getAttribute("update-status") === "succeeded") {
+      // restart the app
+      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);
       }
-    };
-
-    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;
+    } else {
+      // open the page for manual update
+      let url = Services.urlFormatter.formatURLPref("app.update.url.manual");
+      openUILinkIn(url, "tab");
     }
   },
 
   observe(subject, topic, status) {
-    if (!this.enabled) {
+    if (topic == "update-canceled") {
+      this.reset();
+      return;
+    }
+    if (status == "failed") {
+      // Background update has failed, let's show the UI responsible for
+      // prompting the user to update manually.
+      this.uninit();
+      this.displayBadge(false);
       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;
-    }
+    // Give the user badgeWaitTime seconds to react before prompting.
+    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this.timer.initWithCallback(this, this.badgeWaitTime * 1000,
+                                this.timer.TYPE_ONE_SHOT);
+    // The timer callback will call uninit() when it completes.
+  },
+
+  notify() {
+    // If the update is successfully applied, or if the updater has fallen back
+    // to non-staged updates, add a badge to the hamburger menu to indicate an
+    // update will be applied once the browser restarts.
+    this.uninit();
+    this.displayBadge(true);
+  },
+
+  displayBadge(succeeded) {
+    let status = succeeded ? "succeeded" : "failed";
+    let badgeStatus = "update-" + status;
+    gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, badgeStatus);
+
+    let stringId;
+    let updateButtonText;
+    if (succeeded) {
+      let brandBundle = document.getElementById("bundle_brand");
+      let brandShortName = brandBundle.getString("brandShortName");
+      stringId = "appmenu.restartNeeded.description";
+      updateButtonText = gNavigatorBundle.getFormattedString(stringId,
+                                                             [brandShortName]);
+      Services.obs.addObserver(this, "update-canceled", false);
+      this.cancelObserverRegistered = true;
+    } else {
+      stringId = "appmenu.updateFailed.description";
+      updateButtonText = gNavigatorBundle.getString(stringId);
+    }
+
+    let updateButton = document.getElementById("PanelUI-update-status");
+    updateButton.setAttribute("label", updateButtonText);
+    updateButton.setAttribute("update-status", status);
+    updateButton.hidden = false;
+  },
+
+  reset() {
+    gMenuButtonBadgeManager.removeBadge(
+      gMenuButtonBadgeManager.BADGEID_APPUPDATE);
+    let updateButton = document.getElementById("PanelUI-update-status");
+    updateButton.hidden = true;
+    this.uninit();
+    this.init();
   }
 };
 
 // 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;
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-"use strict";
-
-module.exports = {
-  "extends": [
-    "plugin:mozilla/browser-test"
-  ]
-};
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser.ini
+++ /dev/null
@@ -1,22 +0,0 @@
-[DEFAULT]
-tags = appupdate
-support-files =
-  head.js
-  downloadPage.html
-  testConstants.js
-
-[browser_updatesBasicPrompt.js]
-skip-if = asan
-reason = Bug 1168003
-[browser_updatesBasicPromptNoStaging.js]
-[browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js]
-[browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js]
-[browser_updatesCompleteAndPartialPatchesWithBadSizes.js]
-[browser_updatesCompletePatchApplyFailure.js]
-[browser_updatesCompletePatchWithBadCompleteSize.js]
-[browser_updatesDownloadFailures.js]
-[browser_updatesMalformedXml.js]
-[browser_updatesPartialPatchApplyFailure.js]
-[browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js]
-[browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js]
-[browser_updatesPartialPatchWithBadPartialSize.js]
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
+++ /dev/null
@@ -1,28 +0,0 @@
-add_task(function* testBasicPrompt() {
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_STAGING_ENABLED, true]]});
-  let updateParams = "showPrompt=1&promptWaitTime=0";
-  gUseTestUpdater = true;
-
-  // Open a new window to make sure that it doesn't get in the way
-  // of the notification management.
-  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      notificationId: "update-available",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-available-whats-new");
-      }
-    },
-    {
-      notificationId: "update-restart",
-      button: "secondarybutton",
-      *cleanup() {
-        PanelUI.removeNotification(/.*/);
-      }
-    },
-  ]);
-
-  yield BrowserTestUtils.closeWindow(extraWindow);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesBasicPromptNoStaging.js
+++ /dev/null
@@ -1,22 +0,0 @@
-add_task(function* testBasicPromptNoStaging() {
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_STAGING_ENABLED, false]]});
-
-  let updateParams = "showPrompt=1&promptWaitTime=0";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      notificationId: "update-available",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-available-whats-new");
-      }
-    },
-    {
-      notificationId: "update-restart",
-      button: "secondarybutton",
-      cleanup() {
-        PanelUI.removeNotification(/.*/);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js
+++ /dev/null
@@ -1,13 +0,0 @@
-add_task(function* testCompleteAndPartialPatchesWithBadCompleteSize() {
-  let updateParams = "invalidCompleteSize=1&promptWaitTime=0";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      notificationId: "update-restart",
-      button: "secondarybutton",
-      cleanup() {
-        PanelUI.removeNotification(/.*/);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js
+++ /dev/null
@@ -1,13 +0,0 @@
-add_task(function* testCompleteAndPartialPatchesWithBadPartialSize() {
-  let updateParams = "invalidPartialSize=1&promptWaitTime=0";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      notificationId: "update-restart",
-      button: "secondarybutton",
-      cleanup() {
-        PanelUI.removeNotification(/.*/);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesCompleteAndPartialPatchesWithBadSizes.js
+++ /dev/null
@@ -1,33 +0,0 @@
-add_task(function* testCompleteAndPartialPatchesWithBadSizes() {
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
-  let updateParams = "invalidPartialSize=1&invalidCompleteSize=1";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      // if we fail maxBackgroundErrors download attempts, then we want to
-      // first show the user an update available prompt.
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesCompletePatchApplyFailure.js
+++ /dev/null
@@ -1,25 +0,0 @@
-add_task(function* testCompletePatchApplyFailure() {
-  let patches = getLocalPatchString("complete", null, null, null, null, null,
-                                    STATE_PENDING);
-  let updates = getLocalUpdateString(patches, null, null, null,
-                                     Services.appinfo.version, null);
-
-  yield runUpdateProcessingTest(updates, [
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesCompletePatchWithBadCompleteSize.js
+++ /dev/null
@@ -1,34 +0,0 @@
-add_task(function* testCompletePatchWithBadCompleteSize() {
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
-
-  let updateParams = "completePatchOnly=1&invalidCompleteSize=1";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      // if we fail maxBackgroundErrors download attempts, then we want to
-      // first show the user an update available prompt.
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
+++ /dev/null
@@ -1,37 +0,0 @@
-add_task(function* testDownloadFailures() {
-  const maxBackgroundErrors = 5;
-  SpecialPowers.pushPrefEnv({set: [
-    [PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors],
-    [PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]
-  ]});
-  let updateParams = "badURL=1";
-
-  // Open a new window to make sure that our pref management isn't duplicated.
-  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      // if we fail maxBackgroundErrors download attempts, then we want to
-      // first show the user an update available prompt.
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      notificationId: "update-manual",
-      button: "button",
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.");
-        gBrowser.removeTab(gBrowser.selectedTab);
-        gMenuButtonUpdateBadge.reset();
-      }
-    },
-  ]);
-
-  yield BrowserTestUtils.closeWindow(extraWindow);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesMalformedXml.js
+++ /dev/null
@@ -1,29 +0,0 @@
-add_task(function* testMalformedXml() {
-  const updateDetailsUrl = "http://example.com/details";
-  const maxBackgroundErrors = 10;
-  SpecialPowers.pushPrefEnv({set: [
-    [PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors],
-    [PREF_APP_UPDATE_URL_DETAILS, updateDetailsUrl]
-  ]});
-
-  let updateParams = "xmlMalformed=1";
-
-  yield runUpdateTest(updateParams, maxBackgroundErrors, [
-    {
-      // if we fail 10 check attempts, then we want to just show the user a manual update
-      // workflow.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new", updateDetailsUrl);
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-        gMenuButtonUpdateBadge.reset();
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailure.js
+++ /dev/null
@@ -1,26 +0,0 @@
-add_task(function* testPartialPatchApplyFailure() {
-  let patches = getLocalPatchString("partial", null, null, null, null, null,
-                                    STATE_PENDING);
-  let updates = getLocalUpdateString(patches, null, null, null,
-                                     Services.appinfo.version, null,
-                                     null, null, null, null, "false");
-
-  yield runUpdateProcessingTest(updates, [
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteAvailable.js
+++ /dev/null
@@ -1,22 +0,0 @@
-add_task(function* testPartialPatchApplyFailureWithCompleteAvailable() {
-  let patches = getLocalPatchString("partial", null, null, null, null, null,
-                                    STATE_PENDING) +
-                getLocalPatchString("complete", null, null, null,
-                                    null, "false");
-
-  let promptWaitTime = "0";
-  let updates = getLocalUpdateString(patches, null, null, null,
-                                     Services.appinfo.version, null,
-                                     null, null, null, null, "false",
-                                     null, null, null, null, promptWaitTime);
-
-  yield runUpdateProcessingTest(updates, [
-    {
-      notificationId: "update-restart",
-      button: "secondarybutton",
-      cleanup() {
-        PanelUI.removeNotification(/.*/);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesPartialPatchApplyFailureWithCompleteValidationFailure.js
+++ /dev/null
@@ -1,33 +0,0 @@
-add_task(function* testPartialPatchApplyFailureWithCompleteValidationFailure() {
-  // because of the way we're simulating failure, we have to just pretend we've already
-  // retried.
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 0]]});
-  let patches = getLocalPatchString("partial", null, null, null, null, null,
-                                    STATE_PENDING) +
-                getLocalPatchString("complete", null, "MD5",
-                                    null, "1234",
-                                    "false");
-
-  let updates = getLocalUpdateString(patches, null, null, null,
-                                     Services.appinfo.version, null,
-                                     null, null, null, null, "false");
-
-  yield runUpdateProcessingTest(updates, [
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/browser_updatesPartialPatchWithBadPartialSize.js
+++ /dev/null
@@ -1,33 +0,0 @@
-add_task(function* testPartialPatchWithBadPartialSize() {
-  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]]});
-  let updateParams = "partialPatchOnly=1&invalidPartialSize=1";
-
-  yield runUpdateTest(updateParams, 1, [
-    {
-      // if we fail maxBackgroundErrors download attempts, then we want to
-      // first show the user an update available prompt.
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      notificationId: "update-available",
-      button: "button"
-    },
-    {
-      // if we have only an invalid patch, then something's wrong and we don't
-      // have an automatic way to fix it, so show the manual update
-      // doorhanger.
-      notificationId: "update-manual",
-      button: "button",
-      beforeClick() {
-        checkWhatsNewLink("update-manual-whats-new");
-      },
-      *cleanup() {
-        yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-        is(gBrowser.selectedBrowser.currentURI.spec,
-           URL_MANUAL_UPDATE, "Landed on manual update page.")
-        gBrowser.removeTab(gBrowser.selectedTab);
-      }
-    },
-  ]);
-});
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/downloadPage.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Download page</title>
-  <meta charset="utf-8">
-</head>
-<body>
-<!-- just use simple.mar since we have it available and it will result in a download dialog -->
-<a id="download-link" href="http://example.com/browser/browser/base/content/test/appUpdate/simple.mar" data-link-type="download">
-  Download
-</a>
-</body>
-</html>
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/head.js
+++ /dev/null
@@ -1,361 +0,0 @@
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.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";
-
-let gRembemberedPrefs = [];
-
-const DATA_URI_SPEC =  "chrome://mochitests/content/browser/browser/base/content/test/appUpdate/";
-
-var DEBUG_AUS_TEST = true;
-var gUseTestUpdater = false;
-
-const LOG_FUNCTION = info;
-
-/* import-globals-from testConstants.js */
-Services.scriptloader.loadSubScript(DATA_URI_SPEC + "testConstants.js", this);
-/* import-globals-from sharedUpdateXML.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 NOTIFICATIONS = [
-  "update-available",
-  "update-manual",
-  "update-restart"
-];
-
-/**
- * Delay for a very short period. Useful for moving the code after this
- * to the back of the event loop.
- *
- * @return A promise which will resolve after a very short period.
- */
-function delay() {
-  return new Promise(resolve => executeSoon(resolve));
-}
-
-/**
- * Gets the update version info for the update url parameters to send to
- * update.sjs.
- *
- * @param  aAppVersion (optional)
- *         The application version for the update snippet. If not specified the
- *         current application version will be used.
- * @return The url parameters for the application and platform version to send
- *         to update.sjs.
- */
-function getVersionParams(aAppVersion) {
-  let appInfo = Services.appinfo;
-  return "&appVersion=" + (aAppVersion ? aAppVersion : appInfo.version);
-}
-
-/**
- * Clean up updates list and the updates directory.
- */
-function cleanUpUpdates() {
-  gUpdateManager.activeUpdate = null;
-  gUpdateManager.saveUpdates();
-
-  removeUpdateDirsAndFiles();
-}
-
-/**
- * Runs a typical update test. Will set various common prefs for using the
- * updater doorhanger, runs the provided list of steps, and makes sure
- * everything is cleaned up afterwards.
- *
- * @param  updateParams
- *         URL-encoded params which will be sent to update.sjs.
- * @param  checkAttempts
- *         How many times to check for updates. Useful for testing the UI
- *         for check failures.
- * @param  steps
- *         A list of test steps to perform, specifying expected doorhangers
- *         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 Task.spawn(function*() {
-    registerCleanupFunction(() => {
-      gMenuButtonUpdateBadge.uninit();
-      gMenuButtonUpdateBadge.init();
-      cleanUpUpdates();
-    });
-    yield SpecialPowers.pushPrefEnv({
-      set: [
-        [PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
-        [PREF_APP_UPDATE_ENABLED, true],
-        [PREF_APP_UPDATE_IDLETIME, 0],
-        [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE],
-        [PREF_APP_UPDATE_LOG, DEBUG_AUS_TEST],
-      ]});
-
-    yield setupTestUpdater();
-
-    let url = URL_HTTP_UPDATE_SJS +
-              "?" + updateParams +
-              getVersionParams();
-
-    setUpdateURL(url);
-
-    executeSoon(() => {
-      Task.spawn(function*() {
-        gAUS.checkForBackgroundUpdates();
-        for (var i = 0; i < checkAttempts - 1; i++) {
-          yield waitForEvent("update-error", "check-attempt-failed");
-          gAUS.checkForBackgroundUpdates();
-        }
-      });
-    });
-
-    for (let step of steps) {
-      yield processStep(step);
-    }
-
-    yield finishTestRestoreUpdaterBackup();
-  });
-}
-
-/**
- * Runs a test which processes an update. Similar to runUpdateTest.
- *
- * @param  updates
- *         A list of updates to process.
- * @param  steps
- *         A list of test steps to perform, specifying expected doorhangers
- *         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 Task.spawn(function*() {
-    registerCleanupFunction(() => {
-      gMenuButtonUpdateBadge.reset();
-      cleanUpUpdates();
-    });
-
-    SpecialPowers.pushPrefEnv({
-      set: [
-        [PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS, 0],
-        [PREF_APP_UPDATE_ENABLED, true],
-        [PREF_APP_UPDATE_IDLETIME, 0],
-        [PREF_APP_UPDATE_URL_MANUAL, URL_MANUAL_UPDATE],
-        [PREF_APP_UPDATE_LOG, DEBUG_AUS_TEST],
-      ]});
-
-    yield setupTestUpdater();
-
-    writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
-
-    writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
-    writeStatusFile(STATE_FAILED_CRC_ERROR);
-    reloadUpdateManagerData();
-
-    testPostUpdateProcessing();
-
-    for (let step of steps) {
-      yield processStep(step);
-    }
-
-    yield finishTestRestoreUpdaterBackup();
-  });
-}
-
-function processStep({notificationId, button, beforeClick, cleanup}) {
-  return Task.spawn(function*() {
-
-    yield BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popupshown");
-    const shownNotification = PanelUI.activeNotification.id;
-
-    is(shownNotification, notificationId, "The right notification showed up.");
-    if (shownNotification != notificationId) {
-      if (cleanup) {
-        yield cleanup();
-      }
-      return;
-    }
-
-    let notification = document.getElementById(`PanelUI-${notificationId}-notification`);
-    is(notification.hidden, false, `${notificationId} notification is showing`);
-    if (beforeClick) {
-      yield Task.spawn(beforeClick);
-    }
-
-    let buttonEl = document.getAnonymousElementByAttribute(notification, "anonid", button);
-
-    buttonEl.click();
-
-    if (cleanup) {
-      yield cleanup();
-    }
-  });
-}
-
-/**
- * Waits for the specified topic and (optionally) status.
- * @param  topic
- *         String representing the topic to wait for.
- * @param  status
- *         Optional String representing the status on said topic to wait for.
- * @return A promise which will resolve the first time an event occurs on the
- *         specified topic, and (optionally) with the specified status.
- */
-function waitForEvent(topic, status = null) {
-  return new Promise(resolve => Services.obs.addObserver({
-    observe(subject, innerTopic, innerStatus) {
-      if (!status || status == innerStatus) {
-        Services.obs.removeObserver(this, topic);
-        resolve(innerStatus);
-      }
-    }
-  }, topic, false))
-}
-
-/**
- * Ensures that the "What's new" link with the provided ID is displayed and
- * matches the url parameter provided. If no URL is provided, it will instead
- * ensure that the link matches the default link URL.
- *
- * @param  id
- *         The ID of the "What's new" link element.
- * @param  url (optional)
- *         The URL to check against. If none is provided, a default will be used.
- */
-function checkWhatsNewLink(id, url) {
-  let whatsNewLink = document.getElementById(id);
-  is(whatsNewLink.href,
-     url || URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS",
-     "What's new link points to the test_details URL");
-  is(whatsNewLink.hidden, false, "What's new link is not hidden.");
-}
-
-/**
- * For tests that use the test updater restores the backed up real updater if
- * it exists and tries again on failure since Windows debug builds at times
- * leave the file in use. After success moveRealUpdater is called to continue
- * the setup of the test updater. For tests that don't use the test updater
- * runTest will be called.
- */
-function setupTestUpdater() {
-  return Task.spawn(function*() {
-    if (gUseTestUpdater) {
-      try {
-        restoreUpdaterBackup();
-      } catch (e) {
-        logTestInfo("Attempt to restore the backed up updater failed... " +
-                    "will try again, Exception: " + e);
-        yield delay();
-        yield setupTestUpdater();
-        return;
-      }
-      yield moveRealUpdater();
-    }
-  });
-}
-
-/**
- * Backs up the real updater and tries again on failure since Windows debug
- * builds at times leave the file in use. After success it will call
- * copyTestUpdater to continue the setup of the test updater.
- */
-function moveRealUpdater() {
-  return Task.spawn(function*() {
-    try {
-      // Move away the real updater
-      let baseAppDir = getAppBaseDir();
-      let updater = baseAppDir.clone();
-      updater.append(FILE_UPDATER_BIN);
-      updater.moveTo(baseAppDir, FILE_UPDATER_BIN_BAK);
-    } catch (e) {
-      logTestInfo("Attempt to move the real updater out of the way failed... " +
-                  "will try again, Exception: " + e);
-      yield delay();
-      yield moveRealUpdater();
-      return;
-    }
-
-    yield copyTestUpdater();
-  });
-}
-
-/**
- * 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() {
-  return Task.spawn(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);
-      yield delay();
-      yield copyTestUpdater();
-    }
-  });
-}
-
-/**
- * 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
- * the test has finished, and in test_9999_cleanup.xul after all tests have
- * finished.
- */
-function restoreUpdaterBackup() {
-  let baseAppDir = getAppBaseDir();
-  let updater = baseAppDir.clone();
-  let updaterBackup = baseAppDir.clone();
-  updater.append(FILE_UPDATER_BIN);
-  updaterBackup.append(FILE_UPDATER_BIN_BAK);
-  if (updaterBackup.exists()) {
-    if (updater.exists()) {
-      updater.remove(true);
-    }
-    updaterBackup.moveTo(baseAppDir, FILE_UPDATER_BIN);
-  }
-}
-
-/**
- * When a test finishes this will repeatedly attempt to restore the real updater
- * for tests that use the test updater and then call
- * finishTestDefaultWaitForWindowClosed after the restore is successful.
- */
-function finishTestRestoreUpdaterBackup() {
-  return Task.spawn(function*() {
-    if (gUseTestUpdater) {
-      try {
-        // Windows debug builds keep the updater file in use for a short period of
-        // time after the updater process exits.
-        restoreUpdaterBackup();
-      } catch (e) {
-        logTestInfo("Attempt to restore the backed up updater failed... " +
-                    "will try again, Exception: " + e);
-
-        yield delay();
-        yield finishTestRestoreUpdaterBackup();
-        return;
-      }
-    }
-  });
-}
deleted file mode 100644
--- a/browser/base/content/test/appUpdate/testConstants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const REL_PATH_DATA = "browser/browser/base/content/test/appUpdate/";
-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/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -633,16 +633,17 @@ tags = psm
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_contextmenu_childprocess.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug963945.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_domFullscreen_fullscreenMode.js]
 tags = fullscreen
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
+[browser_menuButtonBadgeManager.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_newTabDrop.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_newWindowDrop.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_newwindow_focus.js]
 skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_menuButtonBadgeManager.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+var menuButton = document.getElementById("PanelUI-menu-button");
+
+add_task(function* testButtonActivities() {
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+  is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
+  is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-failed");
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, "download-severe");
+  is(menuButton.getAttribute("badge-status"), "download-severe", "Should have download-severe badge status");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD, "download-warning");
+  is(menuButton.getAttribute("badge-status"), "download-warning", "Should have download-warning badge status");
+
+  gMenuButtonBadgeManager.addBadge("unknownbadge", "attr");
+  is(menuButton.getAttribute("badge-status"), "download-warning", "Should not have changed badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE);
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+
+  yield PanelUI.show();
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (Hamburger menu opened)");
+  PanelUI.hide();
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
+  gMenuButtonBadgeManager.clearBadges();
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (clearBadges called)");
+});
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -46,19 +46,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('win
     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
 
-if CONFIG['MOZ_UPDATER']:
-    BROWSER_CHROME_MANIFESTS += ['content/test/appUpdate/browser.ini']
-
-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/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -21,14 +21,14 @@ pref("app.update.url.manual", "https://w
 pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
 
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 63);
 
 // Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=4 days
-pref("app.update.badgeWaitTime", 345600);
+// button. default=immediately
+pref("app.update.badgeWaitTime", 0);
 
 // Number of usages of the web console or scratchpad.
 // If this is less than 5, then pasting code into the web console or scratchpad is disabled
 pref("devtools.selfxss.count", 0);
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -12,27 +12,19 @@
   <panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView">
     <panelview id="PanelUI-mainView" context="customizationPanelContextMenu">
       <vbox id="PanelUI-contents-scroller">
         <vbox id="PanelUI-contents" class="panelUI-grid"/>
       </vbox>
 
       <footer id="PanelUI-footer">
         <vbox id="PanelUI-footer-addons"></vbox>
-        <toolbarbutton id="PanelUI-update-available-menu-item"
-                       wrap="true"
-                       label="&updateAvailable.panelUI.label;"
-                       hidden="true"/>
-        <toolbarbutton id="PanelUI-update-manual-menu-item"
+        <toolbarbutton id="PanelUI-update-status"
+                       oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
                        wrap="true"
-                       label="&updateManual.panelUI.label;"
-                       hidden="true"/>
-        <toolbarbutton id="PanelUI-update-restart-menu-item"
-                       wrap="true"
-                       label="&updateRestart.panelUI.label;"
                        hidden="true"/>
         <hbox id="PanelUI-footer-fxa">
           <hbox id="PanelUI-fxa-status"
                 defaultlabel="&fxaSignIn.label;"
                 signedinTooltiptext="&fxaSignedIn.tooltip;"
                 tooltiptext="&fxaSignedIn.tooltip;"
                 errorlabel="&fxaSignInError.label;"
                 unverifiedlabel="&fxaUnverified.label;"
@@ -417,71 +409,8 @@
       <description>&panicButton.thankyou.msg1;</description>
       <description>&panicButton.thankyou.msg2;</description>
     </vbox>
   </hbox>
   <button label="&panicButton.thankyou.buttonlabel;"
           id="panic-button-success-closebutton"
           oncommand="PanicButtonNotifier.close()"/>
 </panel>
-
-<panel id="PanelUI-notification-popup"
-       class="popup-notification-panel"
-       type="arrow"
-       position="after_start"
-       hidden="true"
-       orient="vertical"
-       noautofocus="true"
-       noautohide="true"
-       nopreventnavboxhide="true"
-       role="alert">
-  <popupnotification id="PanelUI-update-available-notification"
-                     popupid="update-available"
-                     label="&updateAvailable.header.message;"
-                     buttonlabel="&updateAvailable.acceptButton.label;"
-                     buttonaccesskey="&updateAvailable.acceptButton.accesskey;"
-                     closebuttonhidden="true"
-                     secondarybuttonlabel="&updateAvailable.cancelButton.label;"
-                     secondarybuttonaccesskey="&updateAvailable.cancelButton.accesskey;"
-                     dropmarkerhidden="true"
-                     checkboxhidden="true"
-                     hidden="true">
-    <popupnotificationcontent id="update-available-notification-content" orient="vertical">
-      <description id="update-available-description">&updateAvailable.message;
-        <label id="update-available-whats-new" class="text-link" value="&updateAvailable.whatsnew.label;" />
-      </description>
-    </popupnotificationcontent>
-  </popupnotification>
-
-  <popupnotification id="PanelUI-update-manual-notification"
-                     popupid="update-manual"
-                     label="&updateManual.header.message;"
-                     buttonlabel="&updateManual.acceptButton.label;"
-                     buttonaccesskey="&updateManual.acceptButton.accesskey;"
-                     closebuttonhidden="true"
-                     secondarybuttonlabel="&updateManual.cancelButton.label;"
-                     secondarybuttonaccesskey="&updateManual.cancelButton.accesskey;"
-                     dropmarkerhidden="true"
-                     checkboxhidden="true"
-                     hidden="true">
-    <popupnotificationcontent id="update-manual-notification-content" orient="vertical">
-      <description id="update-manual-description">&updateManual.message;
-        <label id="update-manual-whats-new" class="text-link" value="&updateManual.whatsnew.label;" />
-      </description>
-    </popupnotificationcontent>
-  </popupnotification>
-
-  <popupnotification id="PanelUI-update-restart-notification"
-                     popupid="update-restart"
-                     label="&updateRestart.header.message;"
-                     buttonlabel="&updateRestart.acceptButton.label;"
-                     buttonaccesskey="&updateRestart.acceptButton.accesskey;"
-                     closebuttonhidden="true"
-                     secondarybuttonlabel="&updateRestart.cancelButton.label;"
-                     secondarybuttonaccesskey="&updateRestart.cancelButton.accesskey;"
-                     dropmarkerhidden="true"
-                     checkboxhidden="true"
-                     hidden="true">
-    <popupnotificationcontent id="update-restart-notification-content" orient="vertical">
-      <description id="update-restart-description">&updateRestart.message;</description>
-    </popupnotificationcontent>
-  </popupnotification>
-</panel>
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -27,51 +27,37 @@ const PanelUI = {
   get kElements() {
     return {
       contents: "PanelUI-contents",
       mainView: "PanelUI-mainView",
       multiView: "PanelUI-multiView",
       helpView: "PanelUI-helpView",
       menuButton: "PanelUI-menu-button",
       panel: "PanelUI-popup",
-      notificationPanel: "PanelUI-notification-popup",
-      scroller: "PanelUI-contents-scroller",
-      footer: "PanelUI-footer"
+      scroller: "PanelUI-contents-scroller"
     };
   },
 
   _initialized: false,
   init() {
     for (let [k, v] of Object.entries(this.kElements)) {
       // Need to do fresh let-bindings per iteration
       let getKey = k;
       let id = v;
       this.__defineGetter__(getKey, function() {
         delete this[getKey];
         return this[getKey] = document.getElementById(id);
       });
     }
 
-    this.notifications = [];
     this.menuButton.addEventListener("mousedown", this);
     this.menuButton.addEventListener("keypress", this);
     this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
-
-    Services.obs.addObserver(this, "fullscreen-nav-toolbox", false);
-    Services.obs.addObserver(this, "panelUI-notification-main-action", false);
-    Services.obs.addObserver(this, "panelUI-notification-dismissed", false);
-
-    window.addEventListener("fullscreen", this);
     window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.addListener(this);
-
-    for (let event of this.kEvents) {
-      this.notificationPanel.addEventListener(event, this);
-    }
-
     this._initialized = true;
   },
 
   _eventListenersAdded: false,
   _ensureEventListenersAdded() {
     if (this._eventListenersAdded)
       return;
     this._addEventListeners();
@@ -84,24 +70,17 @@ const PanelUI = {
 
     this.helpView.addEventListener("ViewShowing", this._onHelpViewShow);
     this._eventListenersAdded = true;
   },
 
   uninit() {
     for (let event of this.kEvents) {
       this.panel.removeEventListener(event, this);
-      this.notificationPanel.removeEventListener(event, this);
     }
-
-    Services.obs.removeObserver(this, "fullscreen-nav-toolbox");
-    Services.obs.removeObserver(this, "panelUI-notification-main-action");
-    Services.obs.removeObserver(this, "panelUI-notification-dismissed");
-
-    window.removeEventListener("fullscreen", this);
     this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
     this.menuButton.removeEventListener("mousedown", this);
     this.menuButton.removeEventListener("keypress", this);
     window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.removeListener(this);
     this._overlayScrollListenerBoundFn = null;
   },
 
@@ -172,160 +151,68 @@ const PanelUI = {
         } else {
           anchor = aEvent.target;
         }
 
         this.panel.addEventListener("popupshown", function() {
           resolve();
         }, {once: true});
 
-        anchor = this._getPanelAnchor(anchor);
-        this.panel.openPopup(anchor);
+        let iconAnchor =
+          document.getAnonymousElementByAttribute(anchor, "class",
+                                                  "toolbarbutton-icon");
+        this.panel.openPopup(iconAnchor || anchor);
       }, (reason) => {
         console.error("Error showing the PanelUI menu", reason);
       });
     });
   },
 
-  showNotification(id, mainAction, secondaryActions = [], options = {}) {
-    let notification = new PanelUINotification(id, mainAction, secondaryActions, options);
-    let existingIndex = this.notifications.findIndex(n => n.id == id);
-    if (existingIndex != -1) {
-      this.notifications.splice(existingIndex, 1);
-    }
-
-    // We don't want to clobber doorhanger notifications just to show a badge,
-    // so don't dismiss any of them and the badge will show once the doorhanger
-    // gets resolved.
-    if (!options.badgeOnly && !options.dismissed) {
-      this.notifications.forEach(n => { n.dismissed = true; });
-    }
-
-    // Since notifications are generally somewhat pressing, the ideal case is that
-    // we never have two notifications at once. However, in the event that we do,
-    // it's more likely that the older notification has been sitting around for a
-    // bit, and so we don't want to hide the new notification behind it. Thus,
-    // we want our notifications to behave like a stack instead of a queue.
-    this.notifications.unshift(notification);
-    this._updateNotifications();
-    return notification;
-  },
-
-  showBadgeOnlyNotification(id) {
-    return this.showNotification(id, null, null, { badgeOnly: true });
-  },
-
-  removeNotification(id) {
-    let notifications;
-    if (typeof id == "string") {
-      notifications = this.notifications.filter(n => n.id == id);
-    } else {
-      // If it's not a string, assume RegExp
-      notifications = this.notifications.filter(n => id.test(n.id));
-    }
-
-    notifications.forEach(n => {
-      this._removeNotification(n);
-    });
-    this._updateNotifications();
-  },
-
-  dismissNotification(id) {
-    let notifications;
-    if (typeof id == "string") {
-      notifications = this.notifications.filter(n => n.id == id);
-    } else {
-      // If it's not a string, assume RegExp
-      notifications = this.notifications.filter(n => id.test(n.id));
-    }
-
-    notifications.forEach(n => n.dismissed = true);
-    this._updateNotifications();
-  },
-
   /**
    * If the menu panel is being shown, hide it.
    */
   hide() {
     if (document.documentElement.hasAttribute("customizing")) {
       return;
     }
 
     this.panel.hidePopup();
   },
 
-  observe(subject, topic, status) {
-    switch (topic){
-      case "fullscreen-nav-toolbox":
-        this._updateNotifications();
-        break;
-      case "panelUI-notification-main-action":
-        if (subject != window) {
-          this.removeNotification(status);
-        }
-        break;
-      case "panelUI-notification-dismissed":
-        if (subject != window) {
-          this.dismissNotification(status);
-        }
-        break;
-    }
-  },
-
   handleEvent(aEvent) {
     // Ignore context menus and menu button menus showing and hiding:
     if (aEvent.type.startsWith("popup") &&
         aEvent.target != this.panel) {
       return;
     }
     switch (aEvent.type) {
       case "popupshowing":
         this._adjustLabelsForAutoHyphens();
         // Fall through
       case "popupshown":
         // Fall through
       case "popuphiding":
         // Fall through
       case "popuphidden":
-        this._updateNotifications();
         this._updatePanelButton(aEvent.target);
         break;
       case "mousedown":
         if (aEvent.button == 0)
           this.toggle(aEvent);
         break;
       case "keypress":
         this.toggle(aEvent);
         break;
-      case "fullscreen":
-        this._updateNotifications();
-        break;
     }
   },
 
   get isReady() {
     return !!this._isReady;
   },
 
-  get isNotificationPanelOpen() {
-    let panelState = this.notificationPanel.state;
-
-    return panelState == "showing" || panelState == "open";
-  },
-
-  get activeNotification() {
-    if (this.notifications.length > 0) {
-      const doorhanger =
-        this.notifications.find(n => !n.dismissed && !n.options.badgeOnly);
-      return doorhanger || this.notifications[0];
-    } else {
-      return null;
-    }
-  },
-
   /**
    * Registering the menu panel is done lazily for performance reasons. This
    * method is exposed so that CustomizationMode can force panel-readyness in the
    * event that customization mode is started before the panel has been opened
    * by the user.
    *
    * @param aCustomizing (optional) set to true if this was called while entering
    *        customization mode. If that's the case, we trust that customization
@@ -501,23 +388,24 @@ const PanelUI = {
         panelRemover();
         return;
       }
 
       viewShown = true;
       CustomizableUI.addPanelCloseListeners(tempPanel);
       tempPanel.addEventListener("popuphidden", panelRemover);
 
-      let anchor = this._getPanelAnchor(aAnchor);
+      let iconAnchor =
+        document.getAnonymousElementByAttribute(aAnchor, "class",
+                                                "toolbarbutton-icon");
 
-      if (aAnchor != anchor && aAnchor.id) {
-        anchor.setAttribute("consumeanchor", aAnchor.id);
+      if (iconAnchor && aAnchor.id) {
+        iconAnchor.setAttribute("consumeanchor", aAnchor.id);
       }
-
-      tempPanel.openPopup(anchor, "bottomcenter topright");
+      tempPanel.openPopup(iconAnchor || aAnchor, "bottomcenter topright");
     }
   }),
 
   /**
    * NB: The enable- and disableSingleSubviewPanelAnimations methods only
    * affect the hiding/showing animations of single-subview panels (tempPanel
    * in the showSubView method).
    */
@@ -645,246 +533,19 @@ const PanelUI = {
     quitButton.setAttribute("tooltiptext", tooltipString);
   },
 
   _overlayScrollListenerBoundFn: null,
   _overlayScrollListener(aMQL) {
     ScrollbarSampler.resetSystemScrollbarWidth();
     this._scrollWidth = null;
   },
-
-  _hidePopup() {
-    if (this.isNotificationPanelOpen) {
-      this.notificationPanel.hidePopup();
-    }
-  },
-
-  _updateNotifications() {
-    if (!this.notifications.length) {
-      this._clearAllNotifications();
-      this._hidePopup();
-      return;
-    }
-
-    if (window.fullScreen && FullScreen.navToolboxHidden) {
-      this._hidePopup();
-      return;
-    }
-
-    let doorhangers =
-      this.notifications.filter(n => !n.dismissed && !n.options.badgeOnly);
-
-    if (this.panel.state == "showing" || this.panel.state == "open") {
-      // If the menu is already showing, then we need to dismiss all notifications
-      // since we don't want their doorhangers competing for attention
-      doorhangers.forEach(n => { n.dismissed = true; })
-      this._hidePopup();
-      this._clearBadge();
-      if (!this.notifications[0].options.badgeOnly) {
-        this._showMenuItem(this.notifications[0]);
-      }
-    } else if (doorhangers.length > 0) {
-      this._clearBadge();
-      this._showNotificationPanel(doorhangers[0]);
-    } else {
-      this._hidePopup();
-      this._showBadge(this.notifications[0]);
-      this._showMenuItem(this.notifications[0]);
-    }
-  },
-
-  _showNotificationPanel(notification) {
-    this._refreshNotificationPanel(notification);
-
-    if (this.isNotificationPanelOpen) {
-      return;
-    }
-
-    let anchor = this._getPanelAnchor(this.menuButton);
-
-    this.notificationPanel.hidden = false;
-    this.notificationPanel.openPopup(anchor, "bottomcenter topright");
-  },
-
-  _clearNotificationPanel() {
-    for (let popupnotification of this.notificationPanel.children) {
-      popupnotification.hidden = true;
-      popupnotification.notification = null;
-    }
-  },
-
-  _clearAllNotifications() {
-    this._clearNotificationPanel();
-    this._clearBadge();
-    this._clearMenuItems();
-  },
-
-  _refreshNotificationPanel(notification) {
-    this._clearNotificationPanel();
-
-    let popupnotificationID = this._getPopupId(notification);
-    let popupnotification = document.getElementById(popupnotificationID);
-
-    popupnotification.setAttribute("id", popupnotificationID);
-    popupnotification.setAttribute("buttoncommand", "PanelUI._onNotificationButtonEvent(event, 'buttoncommand');");
-    popupnotification.setAttribute("secondarybuttoncommand", "PanelUI._onNotificationButtonEvent(event, 'secondarybuttoncommand');");
-
-    popupnotification.notification = notification;
-    popupnotification.hidden = false;
-  },
-
-  _showBadge(notification) {
-    let badgeStatus = this._getBadgeStatus(notification);
-    this.menuButton.setAttribute("badge-status", badgeStatus);
-  },
-
-  // "Menu item" here refers to an item in the hamburger panel menu. They will
-  // typically show up as a colored row near the bottom of the panel.
-  _showMenuItem(notification) {
-    this._clearMenuItems();
-
-    let menuItemId = this._getMenuItemId(notification);
-    let menuItem = document.getElementById(menuItemId);
-    if (menuItem) {
-      menuItem.notification = notification;
-      menuItem.setAttribute("oncommand", "PanelUI._onNotificationMenuItemSelected(event)");
-      menuItem.classList.add("PanelUI-notification-menu-item");
-      menuItem.hidden = false;
-      menuItem.fromPanelUINotifications = true;
-    }
-  },
-
-  _clearBadge() {
-    this.menuButton.removeAttribute("badge-status");
-  },
-
-  _clearMenuItems() {
-    for (let child of this.footer.children) {
-      if (child.fromPanelUINotifications) {
-        child.notification = null;
-        child.hidden = true;
-      }
-    }
-  },
-
-  _removeNotification(notification) {
-    // This notification may already be removed, in which case let's just fail
-    // silently.
-    let notifications = this.notifications;
-    if (!notifications)
-      return;
-
-    var index = notifications.indexOf(notification);
-    if (index == -1)
-      return;
-
-    // Remove the notification
-    notifications.splice(index, 1);
-  },
-
-  _onNotificationButtonEvent(event, type) {
-    let notificationEl = getNotificationFromElement(event.originalTarget);
-
-    if (!notificationEl)
-      throw "PanelUI._onNotificationButtonEvent: couldn't find notification element";
-
-    if (!notificationEl.notification)
-      throw "PanelUI._onNotificationButtonEvent: couldn't find notification";
-
-    let notification = notificationEl.notification;
-
-    let action = notification.mainAction;
-
-    if (type == "secondarybuttoncommand") {
-      action = notification.secondaryActions[0];
-    }
-
-    let dismiss = true;
-    if (action) {
-      try {
-        if (action === notification.mainAction) {
-          action.callback(true);
-          this._notify(notification.id, "main-action");
-        } else {
-          action.callback();
-        }
-      } catch (error) {
-        Cu.reportError(error);
-      }
-
-      dismiss = action.dismiss;
-    }
-
-    if (dismiss) {
-      notification.dismissed = true;
-      this._notify(notification.id, "dismissed");
-    } else {
-      this._removeNotification(notification);
-    }
-    this._updateNotifications();
-  },
-
-  _onNotificationMenuItemSelected(event) {
-    let target = event.originalTarget;
-    if (!target.notification)
-      throw "menucommand target has no associated action/notification";
-
-    event.stopPropagation();
-
-    try {
-      target.notification.mainAction.callback(false);
-      this._notify(target.notification.id, "main-action");
-    } catch (error) {
-      Cu.reportError(error);
-    }
-
-    this._removeNotification(target.notification);
-    this._updateNotifications();
-  },
-
-  _getPopupId(notification) { return "PanelUI-" + notification.id + "-notification"; },
-
-  _getBadgeStatus(notification) { return notification.id; },
-
-  _getMenuItemId(notification) { return "PanelUI-" + notification.id + "-menu-item"; },
-
-  _getPanelAnchor(candidate) {
-    let iconAnchor =
-      document.getAnonymousElementByAttribute(candidate, "class",
-                                              "toolbarbutton-icon");
-    return iconAnchor || candidate;
-  },
-
-  _notify(status, topic) {
-    Services.obs.notifyObservers(window, "panelUI-notification-" + topic, status);
-  }
 };
 
 XPCOMUtils.defineConstant(this, "PanelUI", PanelUI);
 
 /**
  * Gets the currently selected locale for display.
  * @return  the selected locale
  */
 function getLocale() {
   return Services.locale.getAppLocaleAsLangTag();
 }
-
-function PanelUINotification(id, mainAction, secondaryActions = [], options = {}) {
-  this.id = id;
-  this.mainAction = mainAction;
-  this.secondaryActions = secondaryActions;
-  this.options = options;
-  this.dismissed = this.options.dismissed || false;
-}
-
-function getNotificationFromElement(aElement) {
-  // Need to find the associated notification object, which is a bit tricky
-  // since it isn't associated with the element directly - this is kind of
-  // gross and very dependent on the structure of the popupnotification
-  // binding's content.
-  let notificationEl;
-  let parent = aElement;
-  while (parent && (parent = aElement.ownerDocument.getBindingParent(parent))) {
-    notificationEl = parent;
-  }
-  return notificationEl;
-}
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -145,12 +145,11 @@ skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
 [browser_exit_background_customize_mode.js]
 [browser_panel_toggle.js]
-[browser_panelUINotifications.js]
 [browser_switch_to_customize_mode.js]
 [browser_synced_tabs_menu.js]
 [browser_check_tooltips_in_navbar.js]
deleted file mode 100644
--- a/browser/components/customizableui/test/browser_panelUINotifications.js
+++ /dev/null
@@ -1,305 +0,0 @@
-"use strict";
-
-/**
- * Tests that when we click on the main call-to-action of the doorhanger, the provided
- * action is called, and the doorhanger removed.
- */
-add_task(function* testMainActionCalled() {
-  let options = {
-    gBrowser: window.gBrowser,
-    url: "about:blank"
-  };
-
-  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
-
-  yield BrowserTestUtils.withNewTab(options, function*(browser) {
-    let doc = browser.ownerDocument;
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    let mainActionCalled = false;
-    let mainAction = {
-      callback: () => { mainActionCalled = true; }
-    };
-    PanelUI.showNotification("update-manual", mainAction);
-
-    let extraMainActionCalled = false;
-    let extraMainAction = {
-      callback: () => { extraMainActionCalled = true; }
-    };
-    extraWindow.PanelUI.showNotification("update-manual", extraMainAction)
-
-    isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
-    let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    let doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
-
-    let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
-    mainActionButton.click();
-
-    ok(mainActionCalled, "Main action callback was called");
-    isnot(extraMainActionCalled, true, "Extra window's main action callback was not called");
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed.");
-    is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-  });
-
-  yield BrowserTestUtils.closeWindow(extraWindow);
-});
-
-/**
- * This tests that when we click the secondary action for a notification,
- * it will display the badge for that notification on the PanelUI menu button.
- * Once we click on this button, we should see an item in the menu which will
- * call our main action.
- */
-add_task(function* testSecondaryActionWorkflow() {
-  let options = {
-    gBrowser: window.gBrowser,
-    url: "about:blank"
-  };
-
-  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
-
-  yield BrowserTestUtils.withNewTab(options, function*(browser) {
-    let doc = browser.ownerDocument;
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-
-    let mainActionCalled = false;
-    let mainAction = {
-      callback: () => { mainActionCalled = true; },
-    };
-    PanelUI.showNotification("update-manual", mainAction);
-
-    let extraMainActionCalled = false;
-    let extraMainAction = {
-      callback: () => { extraMainActionCalled = true; }
-    };
-    extraWindow.PanelUI.showNotification("update-manual", extraMainAction)
-
-    isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
-    let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    let doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
-
-    let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
-    secondaryActionButton.click();
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    is(extraWindow.PanelUI.notificationPanel.state, "closed", "Extra window's update-manual doorhanger is closed.");
-
-    is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button.");
-
-    yield PanelUI.show();
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
-    let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
-    is(menuItem.hidden, false, "update-manual menu item is showing.");
-
-    yield PanelUI.hide();
-    is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is shown on PanelUI button.");
-
-    yield PanelUI.show();
-    menuItem.click();
-    ok(mainActionCalled, "Main action callback was called");
-    isnot(extraMainActionCalled, true, "Extra window's main action callback was not called");
-
-    PanelUI.removeNotification(/.*/);
-  });
-
-  yield BrowserTestUtils.closeWindow(extraWindow);
-});
-
-/**
- * We want to ensure a few things with this:
- * - Adding a doorhanger will make a badge disappear
- * - once the notification for the doorhanger is resolved (removed, not just dismissed),
- *   then we display any other badges that are remaining.
- */
-add_task(function* testInteractionWithBadges() {
-  yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
-    let doc = browser.ownerDocument;
-
-    PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
-    is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-
-    let mainActionCalled = false;
-    let mainAction = {
-      callback: () => { mainActionCalled = true; },
-    };
-    PanelUI.showNotification("update-manual", mainAction);
-
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button.");
-    isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
-    let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    let doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
-
-    let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
-    secondaryActionButton.click();
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-
-    is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is displaying on PanelUI button.");
-
-    yield PanelUI.show();
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is hidden on PanelUI button.");
-    let menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
-    is(menuItem.hidden, false, "update-manual menu item is showing.");
-
-    menuItem.click();
-    ok(mainActionCalled, "Main action callback was called");
-
-    is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
-    PanelUI.removeNotification(/.*/);
-    is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-  });
-});
-
-/**
- * This tests that adding a badge will not dismiss any existing doorhangers.
- */
-add_task(function* testAddingBadgeWhileDoorhangerIsShowing() {
-  yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
-    let doc = browser.ownerDocument;
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    let mainActionCalled = false;
-    let mainAction = {
-      callback: () => { mainActionCalled = true; }
-    };
-    PanelUI.showNotification("update-manual", mainAction);
-    PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
-
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is hidden on PanelUI button.");
-    isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
-    let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    let doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
-
-    let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
-    mainActionButton.click();
-
-    ok(mainActionCalled, "Main action callback was called");
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    is(PanelUI.menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Fxa badge is shown on PanelUI button.");
-    PanelUI.removeNotification(/.*/);
-    is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-  });
-});
-
-/**
- * Tests that badges operate like a stack.
- */
-add_task(function* testMultipleBadges() {
-  yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
-    let doc = browser.ownerDocument;
-    let menuButton = doc.getElementById("PanelUI-menu-button");
-
-    is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-    is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
-
-    PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
-    is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
-
-    PanelUI.showBadgeOnlyNotification("update-succeeded");
-    is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
-
-    PanelUI.showBadgeOnlyNotification("update-failed");
-    is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
-
-    PanelUI.showBadgeOnlyNotification("download-severe");
-    is(menuButton.getAttribute("badge-status"), "download-severe", "Should have download-severe badge status");
-
-    PanelUI.showBadgeOnlyNotification("download-warning");
-    is(menuButton.getAttribute("badge-status"), "download-warning", "Should have download-warning badge status");
-
-    PanelUI.removeNotification(/^download-/);
-    is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
-
-    PanelUI.removeNotification(/^update-/);
-    is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
-
-    PanelUI.removeNotification(/^fxa-/);
-    is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-
-    yield PanelUI.show();
-    is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (Hamburger menu opened)");
-    PanelUI.hide();
-
-    PanelUI.showBadgeOnlyNotification("fxa-needs-authentication");
-    PanelUI.showBadgeOnlyNotification("update-succeeded");
-    PanelUI.removeNotification(/.*/);
-    is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
-  });
-});
-
-/**
- * Tests that non-badges also operate like a stack.
- */
-add_task(function* testMultipleNonBadges() {
-  yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
-    let doc = browser.ownerDocument;
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-
-    let updateManualAction = {
-        called: false,
-        callback: () => { updateManualAction.called = true; },
-    };
-    let updateRestartAction = {
-        called: false,
-        callback: () => { updateRestartAction.called = true; },
-    };
-
-    PanelUI.showNotification("update-manual", updateManualAction);
-
-    let notifications;
-    let doorhanger;
-
-    isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
-    notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-manual-notification", "PanelUI is displaying the update-manual notification.");
-
-    PanelUI.showNotification("update-restart", updateRestartAction);
-
-    isnot(PanelUI.notificationPanel.state, "closed", "Doorhanger is showing.");
-    notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
-    is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
-    doorhanger = notifications[0];
-    is(doorhanger.id, "PanelUI-update-restart-notification", "PanelUI is displaying the update-restart notification.");
-
-    let secondaryActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
-    secondaryActionButton.click();
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    is(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is displaying on PanelUI button.");
-
-    let menuItem;
-
-    yield PanelUI.show();
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-restart", "update-restart badge is hidden on PanelUI button.");
-    menuItem = doc.getElementById("PanelUI-update-restart-menu-item");
-    is(menuItem.hidden, false, "update-restart menu item is showing.");
-
-    menuItem.click();
-    ok(updateRestartAction.called, "update-restart main action callback was called");
-
-    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
-    is(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is displaying on PanelUI button.");
-
-    yield PanelUI.show();
-    isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "update-manual badge is hidden on PanelUI button.");
-    menuItem = doc.getElementById("PanelUI-update-manual-menu-item");
-    is(menuItem.hidden, false, "update-manual menu item is showing.");
-
-    menuItem.click();
-    ok(updateManualAction.called, "update-manual main action callback was called");
-  });
-});
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -502,23 +502,25 @@ const DownloadsIndicatorView = {
     // progress in toolbar
     let suppressAttention = DownloadsCommon.arrowStyledIndicator && !inMenu &&
       this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
       this._percentComplete >= 0;
 
     if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
       this.indicator.removeAttribute("attention");
       if (inMenu) {
-        PanelUI.removeNotification(/^download-/);
+        gMenuButtonBadgeManager.removeBadge(
+                                      gMenuButtonBadgeManager.BADGEID_DOWNLOAD);
       }
     } else {
       this.indicator.setAttribute("attention", this._attention);
       if (inMenu) {
         let badgeClass = "download-" + this._attention;
-        PanelUI.showBadgeOnlyNotification(badgeClass);
+        gMenuButtonBadgeManager.addBadge(
+                          gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
       }
     }
   },
   _attention: DownloadsCommon.ATTENTION_NONE,
 
   // User interface event functions
 
   onWindowUnload() {
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -893,36 +893,8 @@ you can use these alternative items. Oth
 <!ENTITY panicButton.view.forgetButton            "Forget!">
 
 <!ENTITY panicButton.thankyou.msg1                "Your recent history is cleared.">
 <!ENTITY panicButton.thankyou.msg2                "Safe browsing!">
 <!ENTITY panicButton.thankyou.buttonlabel         "Thanks!">
 
 <!ENTITY emeLearnMoreContextMenu.label            "Learn more about DRM…">
 <!ENTITY emeLearnMoreContextMenu.accesskey        "D">
-
-<!ENTITY updateAvailable.message "Update your &brandShorterName; for the latest in speed and privacy.">
-<!ENTITY updateAvailable.whatsnew.label "See what’s new.">
-<!ENTITY updateAvailable.whatsnew.href "http://www.mozilla.org/">
-<!ENTITY updateAvailable.header.message "A new &brandShorterName; update is available.">
-<!ENTITY updateAvailable.acceptButton.label "Download Update">
-<!ENTITY updateAvailable.acceptButton.accesskey "D">
-<!ENTITY updateAvailable.cancelButton.label "Not Now">
-<!ENTITY updateAvailable.cancelButton.accesskey "N">
-<!ENTITY updateAvailable.panelUI.label "Download &brandShorterName; update">
-
-<!ENTITY updateManual.message "Download a fresh copy of &brandShorterName; and we’ll help you to install it.">
-<!ENTITY updateManual.whatsnew.label "See what’s new.">
-<!ENTITY updateManual.whatsnew.href "http://www.mozilla.org/">
-<!ENTITY updateManual.header.message "&brandShorterName; can’t update to the latest version.">
-<!ENTITY updateManual.acceptButton.label "Download &brandShorterName;">
-<!ENTITY updateManual.acceptButton.accesskey "D">
-<!ENTITY updateManual.cancelButton.label "Not Now">
-<!ENTITY updateManual.cancelButton.accesskey "N">
-<!ENTITY updateManual.panelUI.label "Download a fresh copy of &brandShorterName;">
-
-<!ENTITY updateRestart.message "After a quick restart, &brandShorterName; will restore all your open tabs and windows.">
-<!ENTITY updateRestart.header.message "Restart &brandShorterName; to apply update.">
-<!ENTITY updateRestart.acceptButton.label "Restart and Restore">
-<!ENTITY updateRestart.acceptButton.accesskey "R">
-<!ENTITY updateRestart.cancelButton.label "Not Now">
-<!ENTITY updateRestart.cancelButton.accesskey "N">
-<!ENTITY updateRestart.panelUI.label "Restart &brandShorterName; to apply update">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -756,16 +756,25 @@ flashHang.helpButton.accesskey = L
 # be replaced with a hyperlink containing the text defined in customizeTips.tip0.learnMore.
 customizeTips.tip0 = %1$S: You can customize %2$S to work the way you do. Simply drag any of the above to the menu or toolbar. %3$S about customizing %2$S.
 customizeTips.tip0.hint = Hint
 customizeTips.tip0.learnMore = Learn more
 
 # LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
 customizeMode.tabTitle = Customize %S
 
+# LOCALIZATION NOTE(appmenu.*.description, appmenu.*.label): these are used for
+# the appmenu labels and buttons that appear when an update is staged for
+# installation or a background update has failed and a manual download is required.
+# %S is brandShortName
+appmenu.restartNeeded.description = Restart %S to apply updates
+appmenu.updateFailed.description = Background update failed, please download update
+appmenu.restartBrowserButton.label = Restart %S
+appmenu.downloadUpdateButton.label = Download Update
+
 # LOCALIZATION NOTE : FILE Reader View is a feature name and therefore typically used as a proper noun.
 
 readingList.promo.firstUse.readerView.title = Reader View
 readingList.promo.firstUse.readerView.body = Remove clutter so you can focus exactly on what you want to read.
 
 # LOCALIZATION NOTE (appMenuRemoteTabs.mobilePromo.text2):
 # %1$S will be replaced with a link, the text of which is
 # appMenuRemoteTabs.mobilePromo.android and the link will be to
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -105,41 +105,24 @@
   background-size: contain;
   border: none;
 }
 
 #PanelUI-menu-button[badge-status="download-success"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   display: none;
 }
 
-#PanelUI-menu-button[badge-status="update-available"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
-#PanelUI-menu-button[badge-status="update-manual"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
-#PanelUI-menu-button[badge-status="update-restart"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+#PanelUI-menu-button[badge-status="update-succeeded"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
-  border-radius: 50%;
-  box-shadow: none;
-  border: 1px solid -moz-dialog;
-  /* "!important" is necessary to override the rule in toolbarbutton.css */
-  margin: -9px 0 0 !important;
-  margin-inline-end: -6px !important;
-  min-width: 16px;
-  min-height: 16px;
+  height: 13px;
 }
 
-#PanelUI-update-restart-menu-item::after,
-#PanelUI-update-available-menu-item::after,
-#PanelUI-update-manual-menu-item::after {
-  background: #74BF43 url(chrome://browser/skin/update-badge.svg) no-repeat center;
-  border-radius: 50%;
-}
-
-#PanelUI-update-restart-menu-item,
-#PanelUI-update-available-menu-item,
-#PanelUI-update-manual-menu-item {
-  list-style-image: url(chrome://branding/content/icon16.png);
+#PanelUI-menu-button[badge-status="update-failed"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
+  background: #D90000 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
+  height: 13px;
 }
 
 #PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
 #PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   box-shadow: none;
   filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
 }
 
@@ -456,21 +439,21 @@ toolbaritem[cui-areatype="menu-panel"][s
 }
 
 #PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview >  #PanelUI-mainView {
   background-color: var(--arrowpanel-dimmed);
 }
 
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
-#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > .PanelUI-notification-menu-item,
+#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-update-status,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon,
-#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-footer-inner > toolbarseparator,
+#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > toolbarseparator,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) {
   opacity: .5;
 }
 
 /*
  * XXXgijs: this is a workaround for a layout issue that was caused by these iframes,
  * which was affecting subview display. Because of this, we're hiding the iframe *only*
@@ -578,25 +561,38 @@ toolbarpaletteitem[place="palette"] > to
   width: 47px;
   padding-top: 1px;
   display: block;
   text-align: center;
   position: relative;
   top: 25%;
 }
 
-#PanelUI-footer-addons > toolbarbutton::after,
-.PanelUI-notification-menu-item::after {
+#PanelUI-update-status[update-status]::after,
+#PanelUI-footer-addons > toolbarbutton::after {
   content: "";
-  width: 16px;
-  height: 16px;
+  width: 14px;
+  height: 14px;
   margin-inline-end: 16.5px;
+  box-shadow: 0px 1px 0px rgba(255,255,255,.2) inset, 0px -1px 0px rgba(0,0,0,.1) inset, 0px 1px 0px rgba(12,27,38,.2);
+  border-radius: 2px;
+  background-size: contain;
   display: -moz-box;
 }
 
+#PanelUI-update-status[update-status="succeeded"]::after {
+  background-image: url(chrome://browser/skin/update-badge.svg);
+  background-color: #74BF43;
+}
+
+#PanelUI-update-status[update-status="failed"]::after {
+  background-image: url(chrome://browser/skin/update-badge-failed.svg);
+  background-color: #D90000;
+}
+
 #PanelUI-footer-addons > toolbarbutton {
   background-color: #FFEFBF;
   /* Force border to override `#PanelUI-footer-addons > toolbarbutton` selector below */
   border-top: 1px solid hsl(45, 100%, 77%) !important;
   display: flex;
   flex: 1 1 0%;
   width: calc(@menuPanelWidth@ + 30px);
   padding-inline-start: 15px;
@@ -645,17 +641,17 @@ toolbarpaletteitem[place="palette"] > to
   -moz-appearance: none;
 }
 
 #PanelUI-footer-inner:hover > toolbarseparator,
 #PanelUI-footer-fxa:hover > toolbarseparator {
   margin: 0;
 }
 
-.PanelUI-notification-menu-item,
+#PanelUI-update-status,
 #PanelUI-help,
 #PanelUI-fxa-label,
 #PanelUI-fxa-icon,
 #PanelUI-footer-addons > toolbarbutton,
 #PanelUI-customize,
 #PanelUI-quit {
   margin: 0;
   padding: 11px 0;
@@ -664,47 +660,50 @@ toolbarpaletteitem[place="palette"] > to
   -moz-appearance: none;
   box-shadow: none;
   border: none;
   border-radius: 0;
   transition: background-color;
   -moz-box-orient: horizontal;
 }
 
-.PanelUI-notification-menu-item {
+#PanelUI-update-status {
   border-top: 1px solid var(--panel-separator-color);
+}
+
+#PanelUI-update-status {
   border-bottom: 1px solid transparent;
   margin-bottom: -1px;
 }
 
-.PanelUI-notification-menu-item > .toolbarbutton-text {
+#PanelUI-update-status > .toolbarbutton-text {
   width: 0; /* Fancy cropping solution for flexbox. */
 }
 
 #PanelUI-help,
 #PanelUI-quit {
   min-width: 46px;
 }
 
-.PanelUI-notification-menu-item > .toolbarbutton-text,
+#PanelUI-update-status > .toolbarbutton-text,
 #PanelUI-fxa-label > .toolbarbutton-text,
 #PanelUI-footer-addons > toolbarbutton > .toolbarbutton-text,
 #PanelUI-customize > .toolbarbutton-text {
   margin: 0;
   padding: 0 6px;
   text-align: start;
 }
 
 #PanelUI-help > .toolbarbutton-text,
 #PanelUI-quit > .toolbarbutton-text,
 #PanelUI-fxa-avatar > .toolbarbutton-text {
   display: none;
 }
 
-.PanelUI-notification-menu-item > .toolbarbutton-icon,
+#PanelUI-update-status > .toolbarbutton-icon,
 #PanelUI-fxa-label > .toolbarbutton-icon,
 #PanelUI-fxa-icon > .toolbarbutton-icon,
 #PanelUI-customize > .toolbarbutton-icon,
 #PanelUI-help > .toolbarbutton-icon,
 #PanelUI-quit > .toolbarbutton-icon {
   margin-inline-end: 0;
 }
 
@@ -721,24 +720,26 @@ toolbarpaletteitem[place="palette"] > to
   border-inline-start-style: none;
 }
 
 #PanelUI-footer-fxa[fxaprofileimage="set"] > #PanelUI-fxa-status > #PanelUI-fxa-label,
 #PanelUI-footer-fxa[fxaprofileimage="enabled"]:not([fxastatus="error"]) > #PanelUI-fxa-status > #PanelUI-fxa-label {
   padding-inline-start: 0px;
 }
 
-/* descend from #PanelUI-footer to add specificity, or else the
-   padding-inline-start will be overridden */
-#PanelUI-footer > .PanelUI-notification-menu-item {
+#PanelUI-update-status {
   width: calc(@menuPanelWidth@ + 30px);
   padding-inline-start: 15px;
   border-inline-start-style: none;
 }
 
+#PanelUI-update-status {
+  list-style-image: url(chrome://branding/content/icon16.png);
+}
+
 #PanelUI-fxa-label,
 #PanelUI-fxa-icon {
   list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
 }
 
 #PanelUI-remotetabs {
   --panel-ui-sync-illustration-height: 157.5px;
 }
@@ -974,29 +975,44 @@ toolbarpaletteitem[place="palette"] > to
   background-color: hsl(42,94%,85%);
 }
 
 #PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover:active {
   background-color: hsl(42,94%,82%);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
-.PanelUI-notification-menu-item {
+#PanelUI-update-status {
   color: black;
+}
+
+#PanelUI-update-status[update-status="succeeded"] {
   background-color: hsla(96,65%,75%,.5);
 }
 
-.PanelUI-notification-menu-item:not([disabled]):hover {
+#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
   background-color: hsla(96,65%,75%,.8);
 }
 
-.PanelUI-notification-menu-item:not([disabled]):hover:active {
+#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
   background-color: hsl(96,65%,75%);
 }
 
+#PanelUI-update-status[update-status="failed"] {
+  background-color: hsla(359,69%,84%,.5);
+}
+
+#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
+  background-color: hsla(359,69%,84%,.8);
+}
+
+#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
+  background-color: hsl(359,69%,84%);
+}
+
 #PanelUI-quit:not([disabled]):hover {
   background-color: #d94141;
   outline-color: #c23a3a;
 }
 
 #PanelUI-quit:not([disabled]):hover:active {
   background-color: #ad3434;
   outline-color: #992e2e;
@@ -1691,19 +1707,17 @@ menuitem[checked="true"].subviewbutton >
   }
 
   #PanelUI-help[panel-multiview-anchor="true"]:-moz-locale-dir(rtl)::after,
   toolbarbutton[panel-multiview-anchor="true"]:-moz-locale-dir(rtl) {
     background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png),
                       linear-gradient(rgba(255,255,255,0.3), transparent);
   }
 
-  #PanelUI-update-restart-menu-item,
-  #PanelUI-update-available-menu-item,
-  #PanelUI-update-manual-menu-item {
+  #PanelUI-update-status {
     list-style-image: url(chrome://branding/content/icon32.png);
   }
 
   #PanelUI-fxa-label,
   #PanelUI-fxa-icon {
     list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
   }
 
@@ -1730,17 +1744,17 @@ menuitem[checked="true"].subviewbutton >
   #PanelUI-fxa-label,
   #PanelUI-fxa-icon,
   #PanelUI-customize,
   #PanelUI-help,
   #PanelUI-quit {
     -moz-image-region: rect(0, 32px, 32px, 0);
   }
 
-  .PanelUI-notification-menu-item > .toolbarbutton-icon,
+  #PanelUI-update-status > .toolbarbutton-icon,
   #PanelUI-fxa-label > .toolbarbutton-icon,
   #PanelUI-fxa-icon > .toolbarbutton-icon,
   #PanelUI-customize > .toolbarbutton-icon,
   #PanelUI-help > .toolbarbutton-icon,
   #PanelUI-quit > .toolbarbutton-icon {
     width: 16px;
   }
 
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -301,16 +301,8 @@ html|*#webRTC-previewVideo {
     -moz-image-region: rect(0px, 32px, 32px, 0px);
   }
 
   .translation-icon.in-use {
     -moz-image-region: rect(0px, 64px, 32px, 32px);
   }
 }
 %endif
-
-/* UPDATE */
-.popup-notification-icon[popupid="update-available"],
-.popup-notification-icon[popupid="update-manual"],
-.popup-notification-icon[popupid="update-restart"] {
-  background: #74BF43 url(chrome://browser/skin/notification-icons.svg#update) no-repeat center;
-  border-radius: 50%;
-}
--- a/browser/themes/shared/notification-icons.svg
+++ b/browser/themes/shared/notification-icons.svg
@@ -39,22 +39,16 @@
     }
 
     #camera-indicator,
     #microphone-indicator,
     #screen-indicator {
       fill: white;
       fill-opacity: 1;
     }
-
-    #update-icon {
-      stroke: #fff;
-      stroke-width: 3px;
-      stroke-linecap: round;
-    }
   </style>
 
   <defs>
     <path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z" />
     <path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z" />
     <path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z" />
     <path id="geo-linux-detailed-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z" />
     <path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z" />
@@ -63,17 +57,16 @@
     <path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z" />
     <path id="login-icon" d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z" />
     <path id="login-detailed-icon" d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z" />
     <path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
     <path id="microphone-detailed-icon" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
     <path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
     <path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
     <path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
-    <path id="update-icon" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14" />
 
     <clipPath id="blocked-clipPath">
       <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
     </clipPath>
 
     <mask id="i-mask" style="fill-opacity: 1;">
       <rect fill="white" width="32" height="32"/>
       <circle fill="black" cx="16" cy="9" r="2.5"/>
@@ -111,12 +104,11 @@
   <use id="microphone-detailed" xlink:href="#microphone-detailed-icon" />
   <use id="plugin" xlink:href="#plugin-icon" />
   <use id="plugin-blocked" class="blocked" xlink:href="#plugin-icon" />
   <use id="popup" xlink:href="#popup-icon" />
   <use id="screen" xlink:href="#screen-icon" />
   <use id="screen-sharing" xlink:href="#screen-icon"/>
   <use id="screen-indicator" xlink:href="#screen-icon"/>
   <use id="screen-blocked" class="blocked" xlink:href="#screen-icon" />
-  <use id="update" xlink:href="#update-icon" />
 
   <path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
 </svg>
--- a/browser/themes/shared/update-badge.svg
+++ b/browser/themes/shared/update-badge.svg
@@ -1,8 +1,6 @@
 <!-- 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/. -->
 <svg xmlns="http://www.w3.org/2000/svg" width="10px" height="10px">
-  <line x1="5" x2="5" y1="9" y2="2" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
-  <line x1="5" x2="2" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
-  <line x1="5" x2="8" y1="2" y2="5" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>
+  <polygon points="4,9 4,5 2,5 5,1 8,5 6,5 6,9" fill="#fff"/>
 </svg>
--- a/testing/talos/talos/config.py
+++ b/testing/talos/talos/config.py
@@ -169,16 +169,17 @@ DEFAULTS = dict(
         'browser.webapps.checkForUpdates': 0,
         'browser.search.geoSpecificDefaults': False,
         'browser.snippets.enabled': False,
         'browser.snippets.syncPromo.enabled': False,
         'toolkit.telemetry.server': 'https://127.0.0.1/telemetry-dummy/',
         'experiments.manifest.uri':
             'https://127.0.0.1/experiments-dummy/manifest',
         'network.http.speculative-parallel-limit': 0,
+        'app.update.badge': False,
         'lightweightThemes.selectedThemeID': "",
         'devtools.webide.widget.enabled': False,
         'devtools.webide.widget.inNavbarByDefault': False,
         'devtools.chrome.enabled': False,
         'devtools.debugger.remote-enabled': False,
         'devtools.theme': "light",
         'devtools.timeline.enabled': False,
         'identity.fxaccounts.migrateToDevEdition': False,
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5137,52 +5137,16 @@
   "UPDATE_WIZ_LAST_PAGE_CODE": {
     "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 30,
     "releaseChannelCollection": "opt-out",
     "description": "Update: the update wizard page displayed when the UI was closed (mapped in toolkit/mozapps/update/UpdateTelemetry.jsm)"
   },
-  "UPDATE_NOTIFICATION_SHOWN": {
-    "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "categorical",
-    "bug_numbers": [893505],
-    "releaseChannelCollection": "opt-out",
-    "description": "Update: the application update doorhanger type that was displayed.",
-    "labels": ["restart", "available", "manual"]
-  },
-  "UPDATE_NOTIFICATION_DISMISSED": {
-    "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "categorical",
-    "bug_numbers": [893505],
-    "releaseChannelCollection": "opt-out",
-    "description": "Update: the dismiss action was executed for this application update doorhanger type.",
-    "labels": ["restart", "available", "manual"]
-  },
-  "UPDATE_NOTIFICATION_MAIN_ACTION_DOORHANGER": {
-    "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "categorical",
-    "bug_numbers": [893505],
-    "releaseChannelCollection": "opt-out",
-    "description": "Update: the main update action was initiated for this application update doorhanger type.",
-    "labels": ["restart", "available", "manual"]
-  },
-  "UPDATE_NOTIFICATION_MAIN_ACTION_MENU": {
-    "alert_emails": ["application-update-telemetry-alerts@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "categorical",
-    "bug_numbers": [893505],
-    "releaseChannelCollection": "opt-out",
-    "description": "Update: the update action was initiated from the PanelUI application update menu item.",
-    "labels": ["restart", "available", "manual"]
-  },
   "THUNDERBIRD_GLODA_SIZE_MB": {
     "expires_in_version": "never",
     "kind": "linear",
     "high": 1000,
     "n_buckets": 40,
     "description": "Gloda: size of global-messages-db.sqlite (MB)"
   },
   "THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS": {
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -22,23 +22,18 @@ const UPDATESERVICE_CONTRACTID = "@mozil
 const PREF_APP_UPDATE_ALTWINDOWTYPE        = "app.update.altwindowtype";
 const PREF_APP_UPDATE_AUTO                 = "app.update.auto";
 const PREF_APP_UPDATE_BACKGROUNDINTERVAL   = "app.update.download.backgroundInterval";
 const PREF_APP_UPDATE_BACKGROUNDERRORS     = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS  = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CANCELATIONS         = "app.update.cancelations";
 const PREF_APP_UPDATE_CANCELATIONS_OSX     = "app.update.cancelations.osx";
 const PREF_APP_UPDATE_CANCELATIONS_OSX_MAX = "app.update.cancelations.osx.max";
-const PREF_APP_UPDATE_DOORHANGER           = "app.update.doorhanger";
-const PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS    = "app.update.download.attempts";
-const PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS = "app.update.download.maxAttempts";
 const PREF_APP_UPDATE_ELEVATE_NEVER        = "app.update.elevate.never";
 const PREF_APP_UPDATE_ELEVATE_VERSION      = "app.update.elevate.version";
-const PREF_APP_UPDATE_ELEVATE_ATTEMPTS     = "app.update.elevate.attempts";
-const PREF_APP_UPDATE_ELEVATE_MAXATTEMPTS  = "app.update.elevate.maxAttempts";
 const PREF_APP_UPDATE_ENABLED              = "app.update.enabled";
 const PREF_APP_UPDATE_IDLETIME             = "app.update.idletime";
 const PREF_APP_UPDATE_LOG                  = "app.update.log";
 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED  = "app.update.notifiedUnsupported";
 const PREF_APP_UPDATE_POSTUPDATE           = "app.update.postupdate";
 const PREF_APP_UPDATE_PROMPTWAITTIME       = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_SERVICE_ENABLED      = "app.update.service.enabled";
 const PREF_APP_UPDATE_SERVICE_ERRORS       = "app.update.service.errors";
@@ -1190,33 +1185,16 @@ function handleUpdateFailure(update, err
     Cc["@mozilla.org/updates/update-prompt;1"].
       createInstance(Ci.nsIUpdatePrompt).
       showUpdateError(update);
     writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
     return true;
   }
 
   if (update.errorCode == ELEVATION_CANCELED) {
-    if (getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false)) {
-      let elevationAttempts = getPref("getIntPref", PREF_APP_UPDATE_ELEVATE_ATTEMPTS, 0);
-      elevationAttempts++;
-      Services.prefs.setIntPref(PREF_APP_UPDATE_ELEVATE_ATTEMPTS, elevationAttempts);
-      let maxAttempts = Math.min(getPref("getIntPref", PREF_APP_UPDATE_ELEVATE_MAXATTEMPTS, 2), 10);
-
-      if (elevationAttempts > maxAttempts) {
-        LOG("handleUpdateFailure - notifying observers of error. " +
-            "topic: update-error, status: elevation-attempts-exceeded");
-        Services.obs.notifyObservers(update, "update-error", "elevation-attempts-exceeded");
-      } else {
-        LOG("handleUpdateFailure - notifying observers of error. " +
-            "topic: update-error, status: elevation-attempt-failed");
-        Services.obs.notifyObservers(update, "update-error", "elevation-attempt-failed");
-      }
-    }
-
     let cancelations = getPref("getIntPref", PREF_APP_UPDATE_CANCELATIONS, 0);
     cancelations++;
     Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations);
     if (AppConstants.platform == "macosx") {
       let osxCancelations = getPref("getIntPref",
                                   PREF_APP_UPDATE_CANCELATIONS_OSX, 0);
       osxCancelations++;
       Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX,
@@ -1302,21 +1280,17 @@ function handleFallbackToCompleteUpdate(
         "failed, downloading complete patch");
     var status = Cc["@mozilla.org/updates/update-service;1"].
                  getService(Ci.nsIApplicationUpdateService).
                  downloadUpdate(update, !postStaging);
     if (status == STATE_NONE)
       cleanupActiveUpdate();
   } else {
     LOG("handleFallbackToCompleteUpdate - install of complete or " +
-        "only one patch offered failed. Notifying observers. topic: " +
-        "update-error, status: unknown, " +
-        "update.patchCount: " + update.patchCount +
-        "oldType: " + oldType);
-    Services.obs.notifyObservers(update, "update-error", "unknown");
+        "only one patch offered failed.");
   }
   update.QueryInterface(Ci.nsIWritablePropertyBag);
   update.setProperty("patchingFailed", oldType);
 }
 
 function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
   let patchType = AUSTLMY.PATCH_UNKNOWN;
   if (aUpdate && aUpdate.selectedPatch && aUpdate.selectedPatch.type) {
@@ -2099,18 +2073,16 @@ UpdateService.prototype = {
     if (status == STATE_SUCCEEDED) {
       update.statusText = gUpdateBundle.GetStringFromName("installSuccess");
 
       // Update the patch's metadata.
       um.activeUpdate = update;
 
       // Done with this update. Clean it up.
       cleanupActiveUpdate();
-
-      Services.prefs.setIntPref(PREF_APP_UPDATE_ELEVATE_ATTEMPTS, 0);
     } else if (status == STATE_PENDING_ELEVATE) {
       let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
                      createInstance(Ci.nsIUpdatePrompt);
       prompter.showUpdateElevationRequired();
     } else {
       // If there was an I/O error it is assumed that the patch is not invalid
       // and it is set to pending so an attempt to apply it again will happen
       // when the application is restarted.
@@ -2187,25 +2159,19 @@ UpdateService.prototype = {
     errCount++;
     Services.prefs.setIntPref(PREF_APP_UPDATE_BACKGROUNDERRORS, errCount);
     // Don't allow the preference to set a value greater than 20 for max errors.
     let maxErrors = Math.min(getPref("getIntPref", PREF_APP_UPDATE_BACKGROUNDMAXERRORS, 10), 20);
 
     if (errCount >= maxErrors) {
       let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
                      createInstance(Ci.nsIUpdatePrompt);
-      LOG("UpdateService:onError - notifying observers of error. " +
-          "topic: update-error, status: check-attempts-exceeded");
-      Services.obs.notifyObservers(update, "update-error", "check-attempts-exceeded");
       prompter.showUpdateError(update);
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_GENERAL_ERROR_PROMPT);
     } else {
-      LOG("UpdateService:onError - notifying observers of error. " +
-          "topic: update-error, status: check-attempt-failed");
-      Services.obs.notifyObservers(update, "update-error", "check-attempt-failed");
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_GENERAL_ERROR_SILENT);
     }
   },
 
   /**
    * Called when a connection should be resumed
    */
   _attemptResume: function AUS_attemptResume() {
@@ -2547,35 +2513,29 @@ UpdateService.prototype = {
 
     var update = this.selectUpdate(updates, updates.length);
     if (!update || update.elevationFailure) {
       return;
     }
 
     if (update.unsupported) {
       LOG("UpdateService:_selectAndInstallUpdate - update not supported for " +
-          "this system. Notifying observers. topic: update-available, " +
-          "status: unsupported");
+          "this system");
       if (!getPref("getBoolPref", PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED, false)) {
         LOG("UpdateService:_selectAndInstallUpdate - notifying that the " +
             "update is not supported for this system");
         this._showPrompt(update);
       }
-
-      Services.obs.notifyObservers(null, "update-available", "unsupported");
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNSUPPORTED);
       return;
     }
 
     if (!getCanApplyUpdates()) {
       LOG("UpdateService:_selectAndInstallUpdate - the user is unable to " +
-          "apply updates... prompting. Notifying observers. " +
-          "topic: update-available, status: cant-apply");
-
-      Services.obs.notifyObservers(null, "update-available", "cant-apply");
+          "apply updates... prompting");
       this._showPrompt(update);
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_APPLY);
       return;
     }
 
     /**
      * From this point on there are two possible outcomes:
      * 1. download and install the update automatically
@@ -2589,32 +2549,26 @@ UpdateService.prototype = {
      * If the update when it is first read does not have an appVersion attribute
      * the following deprecated behavior will occur:
      * Update Type   Outcome
      * Major         Notify
      * Minor         Auto Install
      */
     if (update.showPrompt) {
       LOG("UpdateService:_selectAndInstallUpdate - prompting because the " +
-          "update snippet specified showPrompt. Notifying observers. " +
-          "topic: update-available, status: showPrompt");
+          "update snippet specified showPrompt");
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_SHOWPROMPT_SNIPPET);
-
-      Services.obs.notifyObservers(update, "update-available", "show-prompt");
       this._showPrompt(update);
       return;
     }
 
     if (!getPref("getBoolPref", PREF_APP_UPDATE_AUTO, true)) {
       LOG("UpdateService:_selectAndInstallUpdate - prompting because silent " +
-          "install is disabled. Notifying observers. topic: update-available, " +
-          "status: show-prompt");
+          "install is disabled");
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_SHOWPROMPT_PREF);
-
-      Services.obs.notifyObservers(update, "update-available", "show-prompt");
       this._showPrompt(update);
       return;
     }
 
     LOG("UpdateService:_selectAndInstallUpdate - download the update");
     let status = this.downloadUpdate(update, true);
     if (status == STATE_NONE) {
       cleanupActiveUpdate();
@@ -3146,21 +3100,21 @@ UpdateManager.prototype = {
       if (!handleUpdateFailure(update, parts[1])) {
         handleFallbackToCompleteUpdate(update, true);
       }
     }
     if (update.state == STATE_APPLIED && shouldUseService()) {
       writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE);
     }
 
-    // Send an observer notification which the app update doorhanger uses to
-    // display a restart notification
+    // Send an observer notification which the update wizard uses in
+    // order to update its UI.
     LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " +
-        "the update was staged. topic: update-staged, status: " + update.state);
-    Services.obs.notifyObservers(update, "update-staged", update.state);
+        "the update was staged. state: " + update.state + ", status: " + status);
+    Services.obs.notifyObservers(null, "update-staged", update.state);
 
     if (AppConstants.platform == "gonk") {
       // Do this after everything else, since it will likely cause the app to
       // shut down.
       if (update.state == STATE_APPLIED) {
         // Notify the user that an update has been staged and is ready for
         // installation (i.e. that they should restart the application). We do
         // not notify on failed update attempts.
@@ -3275,16 +3229,18 @@ Checker.prototype = {
   /**
    * See nsIUpdateService.idl
    */
   checkForUpdates: function UC_checkForUpdates(listener, force) {
     LOG("Checker: checkForUpdates, force: " + force);
     if (!listener)
       throw Cr.NS_ERROR_NULL_POINTER;
 
+    Services.obs.notifyObservers(null, "update-check-start", null);
+
     var url = this.getUpdateURL(force);
     if (!url || (!this.enabled && !force))
       return;
 
     this._request = new XMLHttpRequest();
     this._request.open("GET", url, true);
     this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(false);
     // Prevent the request from reading from the cache.
@@ -3856,19 +3812,19 @@ Downloader.prototype = {
         }
       }
     }
 
     update.QueryInterface(Ci.nsIPropertyBag);
     let interval = this.background ? update.getProperty("backgroundInterval")
                                    : DOWNLOAD_FOREGROUND_INTERVAL;
 
-    LOG("Downloader:downloadUpdate - url: " + this._patch.URL + ", path: " +
+    var uri = Services.io.newURI(this._patch.URL);
+    LOG("Downloader:downloadUpdate - url: " + uri.spec + ", path: " +
         patchFile.path + ", interval: " + interval);
-    var uri = Services.io.newURI(this._patch.URL);
 
     this._request = Cc["@mozilla.org/network/incremental-download;1"].
                     createInstance(Ci.nsIIncrementalDownload);
     this._request.init(uri, patchFile, DOWNLOAD_CHUNK_SIZE, interval);
     this._request.start(this, null);
 
     writeStatusFile(updateDir, STATE_DOWNLOADING);
     this._patch.QueryInterface(Ci.nsIWritablePropertyBag);
@@ -4056,17 +4012,16 @@ Downloader.prototype = {
         }
         AUSTLMY.pingDownloadCode(this.isCompleteUpdate, AUSTLMY.DWNLD_SUCCESS);
 
         // Tell the updater.exe we're ready to apply.
         writeStatusFile(getUpdatesDir(), state);
         writeVersionFile(getUpdatesDir(), this._update.appVersion);
         this._update.installDate = (new Date()).getTime();
         this._update.statusText = gUpdateBundle.GetStringFromName("installPending");
-        Services.prefs.setIntPref(PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0);
       } else {
         LOG("Downloader:onStopRequest - download verification failed");
         state = STATE_DOWNLOAD_FAILED;
         status = Cr.NS_ERROR_CORRUPTED_CONTENT;
 
         // Yes, this code is a string.
         const vfCode = "verification_failed";
         var message = getStatusTextFromCode(vfCode, vfCode);
@@ -4176,36 +4131,17 @@ Downloader.prototype = {
         if (updateStatus == STATE_NONE) {
           cleanupActiveUpdate();
         } else {
           allFailed = false;
         }
       }
 
       if (allFailed) {
-        if (getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false)) {
-          let downloadAttempts = getPref("getIntPref", PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0);
-          downloadAttempts++;
-          Services.prefs.setIntPref(PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, downloadAttempts);
-          let maxAttempts = Math.min(getPref("getIntPref", PREF_APP_UPDATE_DOWNLOAD_MAXATTEMPTS, 2), 10);
-
-          if (downloadAttempts > maxAttempts) {
-            LOG("Downloader:onStopRequest - notifying observers of error. " +
-                "topic: update-error, status: download-attempts-exceeded, " +
-                "downloadAttempts: " + downloadAttempts + " " +
-                "maxAttempts: " + maxAttempts);
-            Services.obs.notifyObservers(this._update, "update-error", "download-attempts-exceeded");
-          } else {
-            this._update.selectedPatch.selected = false;
-            LOG("Downloader:onStopRequest - notifying observers of error. " +
-                "topic: update-error, status: download-attempt-failed");
-            Services.obs.notifyObservers(this._update, "update-error", "download-attempt-failed");
-          }
-        }
-
+        LOG("Downloader:onStopRequest - all update patch downloads failed");
         // If the update UI is not open (e.g. the user closed the window while
         // downloading) and if at any point this was a foreground download
         // notify the user about the error. If the update was a background
         // update there is no notification since the user won't be expecting it.
         if (!Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME)) {
           this._update.QueryInterface(Ci.nsIWritablePropertyBag);
           if (this._update.getProperty("foregroundDownload") == "true") {
             let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
@@ -4321,41 +4257,33 @@ UpdatePrompt.prototype = {
                  null, null);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateAvailable: function UP_showUpdateAvailable(update) {
     if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
-        getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false) ||
         this._getUpdateWindow() || this._getAltUpdateWindow()) {
       return;
     }
 
     this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
                            UPDATE_WINDOW_NAME, "updatesavailable", update);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateDownloaded: function UP_showUpdateDownloaded(update, background) {
     if (background && getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false)) {
       return;
     }
-
-    // Trigger the display of the hamburger doorhanger.
-    LOG("showUpdateDownloaded - Notifying observers that " +
-        "an update was downloaded. topic: update-downloaded, status: " + update.state);
-    Services.obs.notifyObservers(update, "update-downloaded", update.state);
-
-    if (getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false)) {
-      return;
-    }
+    // Trigger the display of the hamburger menu badge.
+    Services.obs.notifyObservers(null, "update-downloaded", update.state);
 
     if (this._getAltUpdateWindow())
       return;
 
     if (background) {
       this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
                               UPDATE_WINDOW_NAME, "finishedBackground", update);
     } else {
@@ -4364,17 +4292,16 @@ UpdatePrompt.prototype = {
     }
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateError: function UP_showUpdateError(update) {
     if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
-        getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false) ||
         this._getAltUpdateWindow())
       return;
 
     // In some cases, we want to just show a simple alert dialog.
     // Replace with Array.prototype.includes when it has stabilized.
     if (update.state == STATE_FAILED &&
         (WRITE_ERRORS.indexOf(update.errorCode) != -1 ||
          update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR ||
--- a/toolkit/mozapps/update/tests/chrome/chrome.ini
+++ b/toolkit/mozapps/update/tests/chrome/chrome.ini
@@ -1,16 +1,15 @@
 # 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/.
 
 [DEFAULT]
 tags = appupdate
 support-files =
-  testConstants.js
   utils.js
   update.sjs
 
 # mochitest-chrome tests must start with "test_" and are executed in sorted
 # order and not in the order specified in the manifest.
 [test_0010_background_basic.xul]
 [test_0011_check_basic.xul]
 [test_0012_check_basic_staging.xul]
deleted file mode 100644
--- a/toolkit/mozapps/update/tests/chrome/testConstants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/";
-const URL_HOST = "http://example.com";
-const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
-const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
--- a/toolkit/mozapps/update/tests/chrome/update.sjs
+++ b/toolkit/mozapps/update/tests/chrome/update.sjs
@@ -3,48 +3,45 @@
  */
 
 /**
  * Server side http server script for application update tests.
  */
 
 const { classes: Cc, interfaces: Ci } = Components;
 
+const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data/";
+
 function getTestDataFile(aFilename) {
   let file = Cc["@mozilla.org/file/directory_service;1"].
             getService(Ci.nsIProperties).get("CurWorkD", Ci.nsILocalFile);
   let pathParts = REL_PATH_DATA.split("/");
   for (let i = 0; i < pathParts.length; ++i) {
     file.append(pathParts[i]);
   }
   if (aFilename) {
     file.append(aFilename);
   }
   return file;
 }
 
-function loadHelperScript(aScriptFile) {
+function loadHelperScript() {
+  let scriptFile = getTestDataFile("sharedUpdateXML.js");
   let io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2);
-  let scriptSpec = io.newFileURI(aScriptFile).spec;
+  let scriptSpec = io.newFileURI(scriptFile).spec;
   let scriptloader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                      getService(Ci.mozIJSSubScriptLoader);
   scriptloader.loadSubScript(scriptSpec, this);
 }
+loadHelperScript();
 
-var scriptFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
-scriptFile.initWithPath(getState("__LOCATION__"));
-scriptFile = scriptFile.parent;
-scriptFile.append("testConstants.js");
-loadHelperScript(scriptFile);
-
-scriptFile = getTestDataFile("sharedUpdateXML.js");
-loadHelperScript(scriptFile);
-
+const URL_HOST = "http://example.com";
+const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
+const URL_HTTP_UPDATE_SJS = URL_HOST + URL_PATH_UPDATE_XML;
 const SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + FILE_SIMPLE_MAR;
-const BAD_SERVICE_URL = URL_HOST + "/" + REL_PATH_DATA + "not_here.mar";
 
 const SLOW_MAR_DOWNLOAD_INTERVAL = 100;
 var gTimer;
 
 function handleRequest(aRequest, aResponse) {
   let params = { };
   if (aRequest.queryString) {
     params = parseQueryString(aRequest.queryString);
@@ -106,27 +103,25 @@ function handleRequest(aRequest, aRespon
                                               "unsupported=\"true\" " +
                                               "detailsURL=\"" + URL_HOST +
                                               "\"></update>\n"));
     return;
   }
 
   let size;
   let patches = "";
-  let url = params.badURL ? BAD_SERVICE_URL : SERVICE_URL;
-
   if (!params.partialPatchOnly) {
     size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
-    patches += getRemotePatchString("complete", url, "SHA512",
+    patches += getRemotePatchString("complete", SERVICE_URL, "SHA512",
                                     SHA512_HASH_SIMPLE_MAR, size);
   }
 
   if (!params.completePatchOnly) {
     size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
-    patches += getRemotePatchString("partial", url, "SHA512",
+    patches += getRemotePatchString("partial", SERVICE_URL, "SHA512",
                                     SHA512_HASH_SIMPLE_MAR, size);
   }
 
   let type = params.type ? params.type : "major";
   let name = params.name ? params.name : "App Update Test";
   let appVersion = params.appVersion ? params.appVersion : "999999.9";
   let displayVersion = params.displayVersion ? params.displayVersion
                                              : "version " + appVersion;
--- a/toolkit/mozapps/update/tests/chrome/utils.js
+++ b/toolkit/mozapps/update/tests/chrome/utils.js
@@ -71,19 +71,16 @@
 
 /* globals TESTS, runTest, finishTest */
 
 const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr,
         utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 
-/* import-globals-from testConstants.js */
-Services.scriptloader.loadSubScript("chrome://mochitests/content/chrome/toolkit/mozapps/update/tests/chrome/testConstants.js", this);
-
 const IS_MACOSX = ("nsILocalFileMac" in Ci);
 const IS_WIN = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 // The tests have to use the pageid instead of the pageIndex due to the
 // app update wizard's access method being random.
 const PAGEID_DUMMY            = "dummy";                 // Done
 const PAGEID_CHECKING         = "checking";              // Done
 const PAGEID_NO_UPDATES_FOUND = "noupdatesfound";        // Done
@@ -94,19 +91,23 @@ const PAGEID_DOWNLOADING      = "downloa
 const PAGEID_ERRORS           = "errors";                // Done
 const PAGEID_ERROR_EXTRA      = "errorextra";            // Done
 const PAGEID_ERROR_PATCHING   = "errorpatching";         // Done
 const PAGEID_FINISHED         = "finished";              // Done
 const PAGEID_FINISHED_BKGRD   = "finishedBackground";    // Done
 
 const UPDATE_WINDOW_NAME = "Update:Wizard";
 
+const URL_HOST = "http://example.com";
+const URL_PATH_UPDATE_XML = "/chrome/toolkit/mozapps/update/tests/chrome/update.sjs";
+const REL_PATH_DATA = "chrome/toolkit/mozapps/update/tests/data";
+
 // These two URLs must not contain parameters since tests add their own
 // test specific parameters.
-const URL_HTTP_UPDATE_XML = URL_HTTP_UPDATE_SJS;
+const URL_HTTP_UPDATE_XML = URL_HOST + URL_PATH_UPDATE_XML;
 const URL_HTTPS_UPDATE_XML = "https://example.com" + URL_PATH_UPDATE_XML;
 
 const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
 
 const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
 const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer";
 
 const LOG_FUNCTION = info;
@@ -483,17 +484,17 @@ function delayedDefaultCallback() {
  * downloading for slow download mar file tests without creating it.
  *
  * @return nsILocalFile for the continue file.
  */
 function getContinueFile() {
   let continueFile = Cc["@mozilla.org/file/directory_service;1"].
                      getService(Ci.nsIProperties).
                      get("CurWorkD", Ci.nsILocalFile);
-  let continuePath = REL_PATH_DATA + "continue";
+  let continuePath = REL_PATH_DATA + "/continue";
   let continuePathParts = continuePath.split("/");
   for (let i = 0; i < continuePathParts.length; ++i) {
     continueFile.append(continuePathParts[i]);
   }
   return continueFile;
 }
 
 /**
@@ -807,17 +808,16 @@ function setupPrefs() {
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_STAGING_ENABLED)) {
     gAppUpdateStagingEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_STAGING_ENABLED);
   }
   Services.prefs.setBoolPref(PREF_APP_UPDATE_STAGING_ENABLED, false);
 
   Services.prefs.setIntPref(PREF_APP_UPDATE_IDLETIME, 0);
   Services.prefs.setIntPref(PREF_APP_UPDATE_PROMPTWAITTIME, 0);
   Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, false);
-  Services.prefs.setBoolPref(PREF_APP_UPDATE_DOORHANGER, false);
 }
 
 /**
  * Restores files that were backed up for the tests and general file cleanup.
  */
 function resetFiles() {
   // Restore the backed up updater-settings.ini if it exists.
   let baseAppDir = getGREDir();
@@ -901,20 +901,16 @@ function resetPrefs() {
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
   }
 
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDMAXERRORS)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDMAXERRORS);
   }
 
-  if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_DOORHANGER)) {
-    Services.prefs.clearUserPref(PREF_APP_UPDATE_DOORHANGER);
-  }
-
   try {
     Services.prefs.deleteBranch(PREFBRANCH_APP_UPDATE_NEVER);
   } catch (e) {
   }
 }
 
 function setupTimer(aTestTimeout) {
   gTestTimeout = aTestTimeout;
--- a/toolkit/mozapps/update/tests/data/shared.js
+++ b/toolkit/mozapps/update/tests/data/shared.js
@@ -3,37 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Shared code for xpcshell and mochitests-chrome */
 /* eslint-disable no-undef */
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-const PREF_APP_UPDATE_AUTO                       = "app.update.auto";
-const PREF_APP_UPDATE_BACKGROUNDERRORS           = "app.update.backgroundErrors";
-const PREF_APP_UPDATE_BACKGROUNDMAXERRORS        = "app.update.backgroundMaxErrors";
-const PREF_APP_UPDATE_CHANNEL                    = "app.update.channel";
-const PREF_APP_UPDATE_DOORHANGER                 = "app.update.doorhanger";
-const PREF_APP_UPDATE_DOWNLOADPROMPTATTEMPTS     = "app.update.download.attempts";
-const PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS  = "app.update.download.maxAttempts";
-const PREF_APP_UPDATE_ENABLED                    = "app.update.enabled";
-const PREF_APP_UPDATE_IDLETIME                   = "app.update.idletime";
-const PREF_APP_UPDATE_LOG                        = "app.update.log";
-const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED        = "app.update.notifiedUnsupported";
-const PREF_APP_UPDATE_PROMPTWAITTIME             = "app.update.promptWaitTime";
-const PREF_APP_UPDATE_RETRYTIMEOUT               = "app.update.socket.retryTimeout";
-const PREF_APP_UPDATE_SERVICE_ENABLED            = "app.update.service.enabled";
-const PREF_APP_UPDATE_SILENT                     = "app.update.silent";
-const PREF_APP_UPDATE_SOCKET_MAXERRORS           = "app.update.socket.maxErrors";
-const PREF_APP_UPDATE_STAGING_ENABLED            = "app.update.staging.enabled";
-const PREF_APP_UPDATE_URL                        = "app.update.url";
-const PREF_APP_UPDATE_URL_DETAILS                = "app.update.url.details";
-const PREF_APP_UPDATE_URL_MANUAL                 = "app.update.url.manual";
-
+const PREF_APP_UPDATE_AUTO                 = "app.update.auto";
+const PREF_APP_UPDATE_BACKGROUNDERRORS     = "app.update.backgroundErrors";
+const PREF_APP_UPDATE_BACKGROUNDMAXERRORS  = "app.update.backgroundMaxErrors";
+const PREF_APP_UPDATE_CHANNEL              = "app.update.channel";
+const PREF_APP_UPDATE_ENABLED              = "app.update.enabled";
+const PREF_APP_UPDATE_IDLETIME             = "app.update.idletime";
+const PREF_APP_UPDATE_LOG                  = "app.update.log";
+const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED  = "app.update.notifiedUnsupported";
+const PREF_APP_UPDATE_PROMPTWAITTIME       = "app.update.promptWaitTime";
+const PREF_APP_UPDATE_RETRYTIMEOUT         = "app.update.socket.retryTimeout";
+const PREF_APP_UPDATE_SERVICE_ENABLED      = "app.update.service.enabled";
+const PREF_APP_UPDATE_SILENT               = "app.update.silent";
+const PREF_APP_UPDATE_SOCKET_MAXERRORS     = "app.update.socket.maxErrors";
+const PREF_APP_UPDATE_STAGING_ENABLED      = "app.update.staging.enabled";
+const PREF_APP_UPDATE_URL                  = "app.update.url";
+const PREF_APP_UPDATE_URL_DETAILS          = "app.update.url.details";
 
 const PREFBRANCH_APP_UPDATE_NEVER = "app.update.never.";
 
 const PREFBRANCH_APP_PARTNER         = "app.partner.";
 const PREF_DISTRIBUTION_ID           = "distribution.id";
 const PREF_DISTRIBUTION_VERSION      = "distribution.version";
 const PREF_TOOLKIT_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
 
--- a/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
+++ b/toolkit/mozapps/update/tests/data/sharedUpdateXML.js
@@ -226,18 +226,18 @@ function getLocalPatchString(aType, aURL
  *         The update's application version.
  *         If not specified it will default to the value of
  *         DEFAULT_UPDATE_VERSION.
  * @param  aBuildID (optional)
  *         The update's build id.
  *         If not specified it will default to '20080811053724'.
  * @param  aDetailsURL (optional)
  *         The update's details url.
- *         If not specified it will default to
- *         URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS" due to bug 470244.
+ *         If not specified it will default to 'http://test_details/' due to due
+ *         to bug 470244.
  * @param  aShowPrompt (optional)
  *         Whether to show the prompt for the update when auto update is
  *         enabled.
  *         If not specified it will not be present and the update service will
  *         default to false.
  * @param  aShowNeverForVersion (optional)
  *         Whether to show the 'No Thanks' button in the update prompt.
  *         If not specified it will not be present and the update service will
@@ -268,17 +268,17 @@ function getUpdateString(aType, aName, a
   let appVersion = "appVersion=\"" +
                    (aAppVersion ? aAppVersion : DEFAULT_UPDATE_VERSION) +
                    "\" ";
   let buildID = aBuildID ? aBuildID : "20080811053724";
   // XXXrstrong - not specifying a detailsURL will cause a leak due to bug 470244
 //   let detailsURL = aDetailsURL ? "detailsURL=\"" + aDetailsURL + "\" " : "";
   let detailsURL = "detailsURL=\"" +
                    (aDetailsURL ? aDetailsURL
-                                : URL_HTTP_UPDATE_SJS + "?uiURL=DETAILS") + "\" ";
+                                : "http://test_details/") + "\" ";
   let showPrompt = aShowPrompt ? "showPrompt=\"" + aShowPrompt + "\" " : "";
   let showNeverForVersion = aShowNeverForVersion ? "showNeverForVersion=\"" +
                                                    aShowNeverForVersion + "\" "
                                                  : "";
   let promptWaitTime = aPromptWaitTime ? "promptWaitTime=\"" + aPromptWaitTime +
                                          "\" "
                                        : "";
   let backgroundInterval = aBackgroundInterval ? "backgroundInterval=\"" +
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -29,18 +29,16 @@
  */
 
 "use strict";
 /* eslint-disable no-undef */
 
 const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr,
         utils: Cu } = Components;
 
-const URL_HTTP_UPDATE_SJS = "http://test_details/";
-
 /* global INSTALL_LOCALE, MOZ_APP_NAME, BIN_SUFFIX, MOZ_APP_VENDOR */
 /* global MOZ_APP_BASENAME, APP_BIN_SUFFIX, APP_INFO_NAME, APP_INFO_VENDOR */
 /* global IS_WIN, IS_MACOSX, IS_UNIX, IS_ANDROID, IS_TOOLKIT_GONK */
 /* global MOZ_VERIFY_MAR_SIGNATURE, MOZ_VERIFY_MAR_SIGNATURE, IS_AUTHENTICODE_CHECK_ENABLED */
 load("../data/xpcshellConstantsPP.js");
 
 function getLogSuffix() {
   if (IS_WIN) {
--- a/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
+++ b/toolkit/mozapps/update/updater/updater-xpcshell/Makefile.in
@@ -1,42 +1,39 @@
 # vim:set ts=8 sw=8 sts=8 noet:
 # 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/.
 
 # 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
-MOCHITESTBROWSERDIR = $(topobjdir)/_tests/testing/mochitest/browser/browser/base/content/test/appUpdate
+XPCSHELLTESTROOT = $(topobjdir)/_tests/xpcshell/toolkit/mozapps/update/tests
+MOCHITESTROOT = $(topobjdir)/_tests/testing/mochitest/chrome/toolkit/mozapps/update/tests
 
 include $(topsrcdir)/config/rules.mk
 
 ifndef MOZ_WINCONSOLE
 ifdef MOZ_DEBUG
 MOZ_WINCONSOLE = 1
 else
 MOZ_WINCONSOLE = 0
 endif
 endif
 
 tools::
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 	# Copy for xpcshell tests
-	$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
-	rsync -a -C --exclude '*.in' $(srcdir)/../macbuild/Contents $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
+	$(NSINSTALL) -D $(XPCSHELLTESTROOT)/data/updater-xpcshell.app
+	rsync -a -C --exclude '*.in' $(srcdir)/../macbuild/Contents $(XPCSHELLTESTROOT)/data/updater-xpcshell.app
 	sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/../macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
-	  iconv -f UTF-8 -t UTF-16 > $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings
-	$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/MacOS
-	$(NSINSTALL) $(FINAL_TARGET)/updater-xpcshell $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/MacOS
-	rm -Rf $(XPCSHELLTESTDIR)/data/updater.app
-	mv $(XPCSHELLTESTDIR)/data/updater-xpcshell.app $(XPCSHELLTESTDIR)/data/updater.app
-	mv $(XPCSHELLTESTDIR)/data/updater.app/Contents/MacOS/updater-xpcshell $(XPCSHELLTESTDIR)/data/updater.app/Contents/MacOS/org.mozilla.updater
+	  iconv -f UTF-8 -t UTF-16 > $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings
+	$(NSINSTALL) -D $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS
+	$(NSINSTALL) $(FINAL_TARGET)/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS
+	rm -Rf $(XPCSHELLTESTROOT)/data/updater.app
+	mv $(XPCSHELLTESTROOT)/data/updater-xpcshell.app $(XPCSHELLTESTROOT)/data/updater.app
+	mv $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/org.mozilla.updater
 
 	# Copy for mochitest chrome tests
-	rsync -a -C $(XPCSHELLTESTDIR)/data/updater.app $(MOCHITESTCHROMEDIR)/data/
-	rsync -a -C $(XPCSHELLTESTDIR)/data/updater.app $(MOCHITESTBROWSERDIR)/
+	rsync -a -C $(XPCSHELLTESTROOT)/data/updater.app $(MOCHITESTROOT)/data/
 else
-	cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(XPCSHELLTESTDIR)/data/updater$(BIN_SUFFIX)
-	cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(MOCHITESTCHROMEDIR)/data/updater$(BIN_SUFFIX)
-	cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(MOCHITESTBROWSERDIR)/updater$(BIN_SUFFIX)
+	cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(XPCSHELLTESTROOT)/data/updater$(BIN_SUFFIX)
+	cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(MOCHITESTROOT)/data/updater$(BIN_SUFFIX)
 endif