Commit f203d491 authored by Nicholas Rishel's avatar Nicholas Rishel
Browse files

Bug 1789390 - Remove scheduled and Action Center notifications during uninstall. r=nalexander

Non-MSIX notifications are not removed when Firefox is uninstalled. To handled this we've added a new `uninstall` background task and extended `nsIWindowsAlertService` to deregister notifications on uninstall.

Differential Revision: https://phabricator.services.mozilla.com/D156625
parent eb7902be
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -416,6 +416,15 @@ Section "Uninstall"
  DetailPrint $(STATUS_UNINSTALL_MAIN)
  SetDetailsPrint none

  ; Some system cleanup is most easily handled when XPCOM functionality is
  ; available - e.g. removing notifications from Window's Action Center. We
  ; handle this in the `uninstall` background task.
  ;
  ; Return value is saved to an unused variable to prevent the the error flag
  ; from being set.
  Var /GLOBAL UnusedExecCatchReturn
  ExecWait '"$INSTDIR\${FileMainEXE}" --backgroundtask uninstall' $UnusedExecCatchReturn

  ; Delete the app exe to prevent launching the app while we are uninstalling.
  ClearErrors
  ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+41 −0
Original line number Diff line number Diff line
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * 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 { AppConstants } = ChromeUtils.import(
  "resource://gre/modules/AppConstants.jsm"
);

var EXPORTED_SYMBOLS = ["runBackgroundTask"];
async function runBackgroundTask(commandLine) {
  if (AppConstants.platform !== "win") {
    console.log("Not a Windows install, skipping `uninstall` background task.");
    return;
  }
  console.log("Running BackgroundTask_uninstall.");

  removeNotifications();
}

function removeNotifications() {
  console.log("Removing Windows toast notifications.");

  if (!("nsIWindowsAlertsService" in Ci)) {
    console.log("nsIWindowsAlertService not present.");
    return;
  }

  let alertsService;
  try {
    alertsService = Cc["@mozilla.org/system-alerts-service;1"]
      .getService(Ci.nsIAlertsService)
      .QueryInterface(Ci.nsIWindowsAlertsService);
  } catch (e) {
    console.error("Error retrieving nsIWindowsAlertService: " + e.message);
    return;
  }

  alertsService.removeAllNotificationsForInstall();
  console.log("Finished removing Windows toast notifications.");
}
+4 −0
Original line number Diff line number Diff line
@@ -154,3 +154,7 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
        "WindowsJumpLists.jsm",
        "WindowsPreviewPerTab.jsm",
    ]

    EXTRA_JS_MODULES.backgroundtasks += [
        "BackgroundTask_uninstall.jsm",
    ]
+7 −0
Original line number Diff line number Diff line
@@ -50,4 +50,11 @@ interface nsIWindowsAlertsService : nsIAlertsService
   */
  AString getXmlStringForWindowsAlert(in nsIAlertNotification aAlert,
                                      [optional] in AString aWindowsTag);

  /**
   * Removes all action center and snoozed notifications associated with this
   * install. Note that this removes all notifications regardless of which profile
   * they originated from.
   */
  void removeAllNotificationsForInstall();
};
+75 −0
Original line number Diff line number Diff line
@@ -9,7 +9,10 @@
#include <windows.h>
#include <appmodel.h>
#include <ktmw32.h>
#include <windows.foundation.h>
#include <wrl/client.h>

#include "ErrorList.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Buffer.h"
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
@@ -34,6 +37,18 @@
namespace mozilla {
namespace widget {

using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Needed to disambiguate internal and Windows `ToastNotification` classes.
using namespace ABI::Windows::UI::Notifications;
using WinToastNotification = ABI::Windows::UI::Notifications::ToastNotification;
using IVectorView_ToastNotification =
    ABI::Windows::Foundation::Collections::IVectorView<WinToastNotification*>;
using IVectorView_ScheduledToastNotification =
    ABI::Windows::Foundation::Collections::IVectorView<
        ScheduledToastNotification*>;

LazyLogModule sWASLog("WindowsAlertsService");

NS_IMPL_ISUPPORTS(ToastNotification, nsIAlertsService, nsIWindowsAlertsService,
@@ -611,5 +626,65 @@ void ToastNotification::RemoveHandler(const nsAString& aAlertName,
  }
}

NS_IMETHODIMP
ToastNotification::RemoveAllNotificationsForInstall() {
  HRESULT hr = S_OK;

  ComPtr<IToastNotificationManagerStatics> manager;
  hr = GetActivationFactory(
      HStringReference(
          RuntimeClass_Windows_UI_Notifications_ToastNotificationManager)
          .Get(),
      &manager);
  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);

  HString aumid;
  MOZ_ASSERT(mAumid.isSome());
  hr = aumid.Set(mAumid.ref().get());
  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);

  // Hide toasts in action center.
  [&]() {
    ComPtr<IToastNotificationManagerStatics2> manager2;
    hr = manager.As(&manager2);
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));

    ComPtr<IToastNotificationHistory> history;
    hr = manager2->get_History(&history);
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));

    hr = history->ClearWithId(aumid.Get());
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
  }();

  // Hide scheduled toasts.
  [&]() {
    ComPtr<IToastNotifier> notifier;
    hr = manager->CreateToastNotifierWithId(aumid.Get(), &notifier);
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));

    ComPtr<IVectorView_ScheduledToastNotification> scheduledToasts;
    hr = notifier->GetScheduledToastNotifications(&scheduledToasts);
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));

    unsigned int schedSize;
    hr = scheduledToasts->get_Size(&schedSize);
    NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));

    for (unsigned int i = 0; i < schedSize; i++) {
      ComPtr<IScheduledToastNotification> schedToast;
      hr = scheduledToasts->GetAt(i, &schedToast);
      if (NS_WARN_IF(FAILED(hr))) {
        continue;
      }

      hr = notifier->RemoveFromSchedule(schedToast.Get());
      Unused << NS_WARN_IF(FAILED(hr));
    }
  }();

  return NS_OK;
}

}  // namespace widget
}  // namespace mozilla