Skip to content

Commit

Permalink
Bug 1789390 - Remove scheduled and Action Center notifications during…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
PrototypeNM1 committed Sep 13, 2022
1 parent eb7902b commit f203d49
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 0 deletions.
9 changes: 9 additions & 0 deletions browser/installer/windows/nsis/uninstaller.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
41 changes: 41 additions & 0 deletions browser/modules/BackgroundTask_uninstall.jsm
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* -*- 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 changes: 4 additions & 0 deletions browser/modules/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,7 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
"WindowsJumpLists.jsm",
"WindowsPreviewPerTab.jsm",
]

EXTRA_JS_MODULES.backgroundtasks += [
"BackgroundTask_uninstall.jsm",
]
7 changes: 7 additions & 0 deletions toolkit/components/alerts/nsIWindowsAlertsService.idl
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 75 additions & 0 deletions widget/windows/ToastNotification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
Expand Down Expand Up @@ -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

0 comments on commit f203d49

Please sign in to comment.