Bug 1358363 - Show PanelUI notifications when window focused draft
authorDoug Thayer <dothayer@mozilla.com>
Fri, 21 Apr 2017 08:57:28 -0700
changeset 575991 533be3686b2d3a164ea14daeb5a1b249d14e78d7
parent 575938 d8762cb967423618ff0a488f14745f60964e5c49
child 628073 4a3c4abd4e0f3f3b804426f3cad9d0fde1a281b2
push id58227
push userbmo:dothayer@mozilla.com
push dateThu, 11 May 2017 05:58:57 +0000
bugs1358363
milestone55.0a1
Bug 1358363 - Show PanelUI notifications when window focused Right now notifications displayed in non-focused windows are causing that window to be focused. This is annoying. We could work to make the doorhangers not focus the other windows, but a simpler solution is to just not show the doorhanger until the window is focused. This has the added benefit of ensuring that the doorhangers entry animation is seen by the user, increasing the likelihood that they will notice it. Additionally, some existing tests involving extra windows were refactored. I stripped the old tests of their extra windows and created new tests specifically to test the behavior of background windows. These tests were modeled off of the background window tests of PopupNotifications.jsm, which create a new window knowing that this will cause the existing window to be the background, rather than explicitly manipulating the focus of the two windows. MozReview-Commit-ID: 1fjrDOhEKCw
browser/base/content/test/appUpdate/browser.ini
browser/base/content/test/appUpdate/browser_updatesBackgroundWindow.js
browser/base/content/test/appUpdate/browser_updatesBackgroundWindowFailures.js
browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
browser/base/content/test/appUpdate/head.js
browser/components/customizableui/content/panelUI.js
browser/components/customizableui/test/browser_panelUINotifications.js
toolkit/mozapps/update/nsUpdateService.js
--- a/browser/base/content/test/appUpdate/browser.ini
+++ b/browser/base/content/test/appUpdate/browser.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 tags = appupdate
 support-files =
   head.js
   downloadPage.html
   testConstants.js
 
+[browser_updatesBackgroundWindow.js]
+[browser_updatesBackgroundWindowFailures.js]
 [browser_updatesBasicPrompt.js]
 skip-if = asan
 reason = Bug 1168003
 [browser_updatesBasicPromptNoStaging.js]
 [browser_updatesCantApply.js]
 skip-if = os != 'win'
 [browser_updatesCompleteAndPartialPatchesWithBadCompleteSize.js]
 [browser_updatesCompleteAndPartialPatchesWithBadPartialSize.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/appUpdate/browser_updatesBackgroundWindow.js
@@ -0,0 +1,34 @@
+add_task(function* testUpdatesBackgroundWindow() {
+  SpecialPowers.pushPrefEnv({set: [[PREF_APP_UPDATE_STAGING_ENABLED, false]]});
+
+  let updateParams = "showPrompt=1&promptWaitTime=0";
+  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
+  yield SimpleTest.promiseFocus(extraWindow);
+
+  yield runUpdateTest(updateParams, 1, [
+    function*() {
+      yield BrowserTestUtils.waitForCondition(() => PanelUI.menuButton.hasAttribute("badge-status"),
+                                              "Background window has a badge.");
+      is(PanelUI.notificationPanel.state, "closed",
+         "The doorhanger is not showing for the background window");
+      is(PanelUI.menuButton.getAttribute("badge-status"), "update-available",
+         "The badge is showing for the background window");
+      let popupShownPromise = BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popupshown");
+      yield BrowserTestUtils.closeWindow(extraWindow);
+      yield SimpleTest.promiseFocus(window);
+      yield popupShownPromise;
+
+      checkWhatsNewLink("update-available-whats-new");
+      let buttonEl = getNotificationButton(window, "update-available", "button");
+      buttonEl.click();
+    },
+    {
+      notificationId: "update-restart",
+      button: "secondarybutton",
+      cleanup() {
+        PanelUI.removeNotification(/.*/);
+      }
+    },
+  ]);
+});
+
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/appUpdate/browser_updatesBackgroundWindowFailures.js
@@ -0,0 +1,47 @@
+add_task(function* testBackgroundWindowFailures() {
+  const maxBackgroundErrors = 5;
+  SpecialPowers.pushPrefEnv({set: [
+    [PREF_APP_UPDATE_BACKGROUNDMAXERRORS, maxBackgroundErrors],
+    [PREF_APP_UPDATE_DOWNLOADPROMPTMAXATTEMPTS, 2]
+  ]});
+  let updateParams = "badURL=1";
+  let extraWindow = yield BrowserTestUtils.openNewBrowserWindow();
+  yield SimpleTest.promiseFocus(extraWindow);
+
+  function getBackgroundWindowHandler(destroyWindow) {
+    return function*() {
+      yield BrowserTestUtils.waitForCondition(() => PanelUI.menuButton.hasAttribute("badge-status"),
+                                              "Background window has a badge.");
+
+      is(PanelUI.notificationPanel.state, "closed",
+         "The doorhanger is not showing for the background window");
+      is(PanelUI.menuButton.getAttribute("badge-status"), "update-available",
+         "The badge is showing for the background window");
+
+      checkWhatsNewLink("update-available-whats-new");
+      let buttonEl = getNotificationButton(extraWindow, "update-available", "button");
+      buttonEl.click();
+
+      if (destroyWindow) {
+        yield BrowserTestUtils.closeWindow(extraWindow);
+        yield SimpleTest.promiseFocus(window);
+      }
+    };
+  }
+
+  yield runUpdateTest(updateParams, 1, [
+    getBackgroundWindowHandler(false),
+    getBackgroundWindowHandler(true),
+    {
+      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();
+      }
+    },
+  ]);
+});
--- a/browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
+++ b/browser/base/content/test/appUpdate/browser_updatesBasicPrompt.js
@@ -1,28 +1,22 @@
 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);
 });
--- a/browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
+++ b/browser/base/content/test/appUpdate/browser_updatesDownloadFailures.js
@@ -1,19 +1,16 @@
 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"
     },
     {
@@ -27,11 +24,9 @@ add_task(function* testDownloadFailures(
         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);
 });
--- a/browser/base/content/test/appUpdate/head.js
+++ b/browser/base/content/test/appUpdate/head.js
@@ -189,37 +189,40 @@ function runUpdateProcessingTest(updates
     for (let step of steps) {
       yield processStep(step);
     }
 
     yield finishTestRestoreUpdaterBackup();
   });
 }
 
-function processStep({notificationId, button, beforeClick, cleanup}) {
+function processStep(step) {
+  if (typeof(step) == "function") {
+    return Task.spawn(step);
+  }
+
+  const {notificationId, button, beforeClick, cleanup} = step;
   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(`appMenu-${notificationId}-notification`);
-    is(notification.hidden, false, `${notificationId} notification is showing`);
+    let buttonEl = getNotificationButton(window, notificationId, button);
     if (beforeClick) {
-      yield Task.spawn(beforeClick);
+      yield beforeClick();
     }
 
-    let buttonEl = document.getAnonymousElementByAttribute(notification, "anonid", button);
 
     buttonEl.click();
 
     if (cleanup) {
       yield cleanup();
     }
   });
 }
@@ -240,16 +243,32 @@ function waitForEvent(topic, status = nu
         Services.obs.removeObserver(this, topic);
         resolve(innerStatus);
       }
     }
   }, topic))
 }
 
 /**
+ * Gets the specified button for the notification.
+ *
+ * @param  window
+ *         The window to get the notification button for.
+ * @param  notificationId
+ *         The ID of the notification to get the button for.
+ * @param  button
+ *         The anonid of the button to get.
+ */
+function getNotificationButton(win, notificationId, button) {
+  let notification = win.document.getElementById(`appMenu-${notificationId}-notification`);
+  is(notification.hidden, false, `${notificationId} notification is showing`);
+  return win.document.getAnonymousElementByAttribute(notification, "anonid", button);
+}
+
+/**
  * 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.
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -52,16 +52,17 @@ const PanelUI = {
     this.menuButton.addEventListener("keypress", this);
     this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
 
     Services.obs.addObserver(this, "fullscreen-nav-toolbox");
     Services.obs.addObserver(this, "panelUI-notification-main-action");
     Services.obs.addObserver(this, "panelUI-notification-dismissed");
 
     window.addEventListener("fullscreen", this);
+    window.addEventListener("activate", this);
     window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.addListener(this);
 
     for (let event of this.kEvents) {
       this.notificationPanel.addEventListener(event, this);
     }
 
     this._initPhotonPanel();
@@ -133,16 +134,17 @@ const PanelUI = {
       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);
+    window.removeEventListener("activate", this);
     this.menuButton.removeEventListener("mousedown", this);
     this.menuButton.removeEventListener("keypress", this);
     window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
     CustomizableUI.removeListener(this);
     this._overlayScrollListenerBoundFn = null;
   },
 
   /**
@@ -346,16 +348,17 @@ const PanelUI = {
       case "mousedown":
         if (aEvent.button == 0)
           this.toggle(aEvent);
         break;
       case "keypress":
         this.toggle(aEvent);
         break;
       case "fullscreen":
+      case "activate":
         this._updateNotifications();
         break;
     }
   },
 
   get isReady() {
     return !!this._isReady;
   },
@@ -744,17 +747,18 @@ const PanelUI = {
       // 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._showBannerItem(this.notifications[0]);
       }
     } else if (doorhangers.length > 0) {
-      if (window.fullScreen) {
+      // Only show the doorhanger if the window is focused and not fullscreen
+      if (window.fullScreen || Services.focus.activeWindow !== window) {
         this._hidePopup();
         this._showBadge(doorhangers[0]);
         this._showBannerItem(doorhangers[0]);
       } else {
         this._clearBadge();
         this._showNotificationPanel(doorhangers[0]);
       }
     } else {
--- a/browser/components/customizableui/test/browser_panelUINotifications.js
+++ b/browser/components/customizableui/test/browser_panelUINotifications.js
@@ -5,116 +5,194 @@
  * 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; }
+    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, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
+
+    let button = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
+    button.click();
+
+    ok(mainActionCalled, "Main action callback was called");
+    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
+    is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+  });
+});
+
+/**
+ * Tests that when we try to show a notification in a background window, it
+ * does not display until the window comes back into the foreground. However,
+ * it should display a badge.
+ */
+add_task(function* testDoesNotShowDoorhangerForBackgroundWindow() {
+  let options = {
+    gBrowser: window.gBrowser,
+    url: "about:blank"
+  };
+
+  yield BrowserTestUtils.withNewTab(options, function*(browser) {
+    let doc = browser.ownerDocument;
+
+    let win = yield BrowserTestUtils.openNewBrowserWindow();
+    let mainActionCalled = false;
+    let mainAction = {
+      callback: () => { mainActionCalled = true; }
     };
-    extraWindow.PanelUI.showNotification("update-manual", extraMainAction)
+    PanelUI.showNotification("update-manual", mainAction);
+    is(PanelUI.notificationPanel.state, "closed", "The background window's doorhanger is closed.");
+    is(PanelUI.menuButton.hasAttribute("badge-status"), true, "The background window has a badge.");
 
+    yield BrowserTestUtils.closeWindow(win);
+    yield SimpleTest.promiseFocus(window);
     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, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
 
-    let mainActionButton = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
-    mainActionButton.click();
+    let button = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
+    button.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");
+  });
+});
+
+/**
+ * Tests that when we try to show a notification in a background window and in
+ * a foreground window, if the foreground window's main action is called, the
+ * background window's doorhanger will be removed.
+ */
+add_task(function* testBackgroundWindowNotificationsAreRemovedByForeground() {
+  let options = {
+    gBrowser: window.gBrowser,
+    url: "about:blank"
+  };
+
+  yield BrowserTestUtils.withNewTab(options, function*(browser) {
+    let win = yield BrowserTestUtils.openNewBrowserWindow();
+    PanelUI.showNotification("update-manual", {callback() {}});
+    win.PanelUI.showNotification("update-manual", {callback() {}});
+    let doc = win.gBrowser.ownerDocument;
+    let notifications = [...win.PanelUI.notificationPanel.children].filter(n => !n.hidden);
+    let doorhanger = notifications[0];
+    let button = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
+    button.click();
+
+    yield BrowserTestUtils.closeWindow(win);
+    yield SimpleTest.promiseFocus(window);
+
+    is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
     is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
   });
+});
 
-  yield BrowserTestUtils.closeWindow(extraWindow);
+/**
+ * Tests that when we try to show a notification in a background window and in
+ * a foreground window, if the foreground window's doorhanger is dismissed,
+ * the background window's doorhanger will also be dismissed once the window
+ * regains focus.
+ */
+add_task(function* testBackgroundWindowNotificationsAreDismissedByForeground() {
+  let options = {
+    gBrowser: window.gBrowser,
+    url: "about:blank"
+  };
+
+  yield BrowserTestUtils.withNewTab(options, function*(browser) {
+    let win = yield BrowserTestUtils.openNewBrowserWindow();
+    PanelUI.showNotification("update-manual", {callback() {}});
+    win.PanelUI.showNotification("update-manual", {callback() {}});
+    let doc = win.gBrowser.ownerDocument;
+    let notifications = [...win.PanelUI.notificationPanel.children].filter(n => !n.hidden);
+    let doorhanger = notifications[0];
+    let button = doc.getAnonymousElementByAttribute(doorhanger, "anonid", "secondarybutton");
+    button.click();
+
+    yield BrowserTestUtils.closeWindow(win);
+    yield SimpleTest.promiseFocus(window);
+
+    is(PanelUI.notificationPanel.state, "closed", "The background window's doorhanger is closed.");
+    is(PanelUI.menuButton.hasAttribute("badge-status"), true,
+       "The dismissed notification should still have a badge status");
+
+    PanelUI.removeNotification(/.*/);
+  });
 });
 
 /**
  * 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, "appMenu-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 = PanelUI.mainView.querySelector(".panel-banner-item");
     is(menuItem.label, menuItem.getAttribute("label-update-manual"), "Showing correct label");
     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.
  */
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -3912,16 +3912,17 @@ UpdatePrompt.prototype = {
                  "Update:History", null, null);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateElevationRequired: function UP_showUpdateElevationRequired() {
     if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
+        getPref("getBoolPref", PREF_APP_UPDATE_DOORHANGER, false) ||
         this._getAltUpdateWindow()) {
       return;
     }
 
     let um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null,
                  UPDATE_WINDOW_NAME, "finishedBackground", um.activeUpdate);