Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / windows / ToastNotification.cpp
blob8ec7e1df41a64db26a26095b2785384e5b762dba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ToastNotification.h"
9 #include <windows.h>
10 #include <appmodel.h>
11 #include <ktmw32.h>
12 #include <windows.foundation.h>
13 #include <wrl/client.h>
15 #include "ErrorList.h"
16 #include "mozilla/BasePrincipal.h"
17 #include "mozilla/Buffer.h"
18 #include "mozilla/DynamicallyLinkedFunctionPtr.h"
19 #include "mozilla/mscom/COMWrappers.h"
20 #include "mozilla/Logging.h"
21 #include "mozilla/Services.h"
22 #include "mozilla/WidgetUtils.h"
23 #include "mozilla/WindowsVersion.h"
24 #include "nsAppRunner.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsCOMPtr.h"
27 #include "nsIObserverService.h"
28 #include "nsString.h"
29 #include "nsThreadUtils.h"
30 #include "nsWindowsHelpers.h"
31 #include "nsXREDirProvider.h"
32 #include "prenv.h"
33 #include "ToastNotificationHandler.h"
34 #include "WinTaskbar.h"
35 #include "WinUtils.h"
37 namespace mozilla {
38 namespace widget {
40 using namespace ABI::Windows::Foundation;
41 using namespace Microsoft::WRL;
42 using namespace Microsoft::WRL::Wrappers;
43 // Needed to disambiguate internal and Windows `ToastNotification` classes.
44 using namespace ABI::Windows::UI::Notifications;
45 using WinToastNotification = ABI::Windows::UI::Notifications::ToastNotification;
46 using IVectorView_ToastNotification =
47 ABI::Windows::Foundation::Collections::IVectorView<WinToastNotification*>;
48 using IVectorView_ScheduledToastNotification =
49 ABI::Windows::Foundation::Collections::IVectorView<
50 ScheduledToastNotification*>;
52 LazyLogModule sWASLog("WindowsAlertsService");
54 NS_IMPL_ISUPPORTS(ToastNotification, nsIAlertsService, nsIWindowsAlertsService,
55 nsIAlertsDoNotDisturb, nsIObserver)
57 ToastNotification::ToastNotification() = default;
59 ToastNotification::~ToastNotification() = default;
61 nsresult ToastNotification::Init() {
62 if (!IsWin8OrLater()) {
63 return NS_ERROR_NOT_IMPLEMENTED;
66 if (!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
67 // Windows Toast Notification requires AppId. But allow `xpcshell` to
68 // create the service to test other functionality.
69 if (!EnsureAumidRegistered()) {
70 MOZ_LOG(sWASLog, LogLevel::Warning, ("Failed to register AUMID!"));
71 return NS_ERROR_NOT_IMPLEMENTED;
73 } else {
74 MOZ_LOG(sWASLog, LogLevel::Info, ("Using dummy AUMID in xpcshell test"));
75 mAumid.emplace(u"XpcshellTestToastAumid"_ns);
78 MOZ_LOG(sWASLog, LogLevel::Info,
79 ("Using AUMID: '%s'", NS_ConvertUTF16toUTF8(mAumid.ref()).get()));
81 nsCOMPtr<nsIObserverService> obsServ =
82 mozilla::services::GetObserverService();
83 if (obsServ) {
84 Unused << NS_WARN_IF(
85 NS_FAILED(obsServ->AddObserver(this, "last-pb-context-exited", false)));
86 Unused << NS_WARN_IF(
87 NS_FAILED(obsServ->AddObserver(this, "quit-application", false)));
90 return NS_OK;
93 bool ToastNotification::EnsureAumidRegistered() {
94 // Check if this is an MSIX install, app identity is provided by the package
95 // so no registration is necessary.
96 if (AssignIfMsixAumid(mAumid)) {
97 MOZ_LOG(
98 sWASLog, LogLevel::Info,
99 ("Found MSIX AUMID: '%s'", NS_ConvertUTF16toUTF8(mAumid.ref()).get()));
100 return true;
103 nsAutoString installHash;
104 nsresult rv = gDirServiceProvider->GetInstallHash(installHash);
105 NS_ENSURE_SUCCESS(rv, false);
107 // Check if toasts were registered during NSIS/MSI installation.
108 if (AssignIfNsisAumid(installHash, mAumid)) {
109 MOZ_LOG(sWASLog, LogLevel::Info,
110 ("Found AUMID from installer (with install hash '%s'): '%s'",
111 NS_ConvertUTF16toUTF8(installHash).get(),
112 NS_ConvertUTF16toUTF8(mAumid.ref()).get()));
113 return true;
116 // No AUMID registered, fall through to runtime registration for development
117 // and portable builds.
118 if (RegisterRuntimeAumid(installHash, mAumid)) {
119 MOZ_LOG(
120 sWASLog, LogLevel::Info,
121 ("Updated AUMID registration at runtime (for install hash '%s'): '%s'",
122 NS_ConvertUTF16toUTF8(installHash).get(),
123 NS_ConvertUTF16toUTF8(mAumid.ref()).get()));
124 return true;
127 MOZ_LOG(sWASLog, LogLevel::Warning,
128 ("Failed to register AUMID at runtime! (for install hash '%s')",
129 NS_ConvertUTF16toUTF8(installHash).get()));
130 return false;
133 bool ToastNotification::AssignIfMsixAumid(Maybe<nsAutoString>& aAumid) {
134 // `GetCurrentApplicationUserModelId` added in Windows 8.
135 DynamicallyLinkedFunctionPtr<decltype(&GetCurrentApplicationUserModelId)>
136 pGetCurrentApplicationUserModelId(L"kernel32.dll",
137 "GetCurrentApplicationUserModelId");
138 if (!pGetCurrentApplicationUserModelId) {
139 return false;
142 UINT32 len = 0;
143 // ERROR_INSUFFICIENT_BUFFER signals that we're in an MSIX package, and
144 // therefore should use the package's AUMID.
145 if (pGetCurrentApplicationUserModelId(&len, nullptr) !=
146 ERROR_INSUFFICIENT_BUFFER) {
147 MOZ_LOG(sWASLog, LogLevel::Debug, ("Not an MSIX package"));
148 return false;
150 mozilla::Buffer<wchar_t> buffer(len);
151 LONG success = pGetCurrentApplicationUserModelId(&len, buffer.Elements());
152 NS_ENSURE_TRUE(success == ERROR_SUCCESS, false);
154 aAumid.emplace(buffer.Elements());
155 return true;
158 bool ToastNotification::AssignIfNsisAumid(nsAutoString& aInstallHash,
159 Maybe<nsAutoString>& aAumid) {
160 nsAutoString nsisAumidName =
161 u""_ns MOZ_TOAST_APP_NAME u"Toast-"_ns + aInstallHash;
162 nsAutoString nsisAumidPath = u"AppUserModelId\\"_ns + nsisAumidName;
163 if (!WinUtils::HasRegistryKey(HKEY_CLASSES_ROOT, nsisAumidPath.get())) {
164 MOZ_LOG(sWASLog, LogLevel::Debug,
165 ("No CustomActivator value from installer in key 'HKCR\\%s'",
166 NS_ConvertUTF16toUTF8(nsisAumidPath).get()));
167 return false;
170 aAumid.emplace(nsisAumidName);
171 return true;
174 bool ToastNotification::RegisterRuntimeAumid(nsAutoString& aInstallHash,
175 Maybe<nsAutoString>& aAumid) {
176 // Portable AUMID slightly differs from installed AUMID so we can
177 // differentiate installed to HKCU vs portable installs if necessary.
178 nsAutoString portableAumid =
179 u""_ns MOZ_TOAST_APP_NAME u"PortableToast-"_ns + aInstallHash;
181 nsCOMPtr<nsIFile> appdir;
182 nsresult rv = gDirServiceProvider->GetGREDir()->Clone(getter_AddRefs(appdir));
183 NS_ENSURE_SUCCESS(rv, false);
185 nsCOMPtr<nsIFile> icon;
186 rv = appdir->Clone(getter_AddRefs(icon));
187 NS_ENSURE_SUCCESS(rv, false);
189 rv = icon->Append(u"browser"_ns);
190 NS_ENSURE_SUCCESS(rv, false);
192 rv = icon->Append(u"VisualElements"_ns);
193 NS_ENSURE_SUCCESS(rv, false);
195 rv = icon->Append(u"VisualElements_70.png"_ns);
196 NS_ENSURE_SUCCESS(rv, false);
198 nsAutoString iconPath;
199 rv = icon->GetPath(iconPath);
200 NS_ENSURE_SUCCESS(rv, false);
202 nsCOMPtr<nsIFile> comDll;
203 rv = appdir->Clone(getter_AddRefs(comDll));
204 NS_ENSURE_SUCCESS(rv, false);
206 rv = comDll->Append(u"notificationserver.dll"_ns);
207 NS_ENSURE_SUCCESS(rv, false);
209 nsAutoString dllPath;
210 rv = comDll->GetPath(dllPath);
211 NS_ENSURE_SUCCESS(rv, false);
213 nsAutoHandle txn;
214 // Manipulate the registry using a transaction so that any failures are
215 // rolled back.
216 wchar_t transactionName[] = L"" MOZ_TOAST_APP_NAME L" toast registration";
217 txn.own(::CreateTransaction(nullptr, nullptr, TRANSACTION_DO_NOT_PROMOTE, 0,
218 0, 0, transactionName));
219 NS_ENSURE_TRUE(txn.get() != INVALID_HANDLE_VALUE, false);
221 LSTATUS status;
223 auto RegisterKey = [&](const nsAString& path, nsAutoRegKey& key) {
224 HKEY rawKey;
225 status = ::RegCreateKeyTransactedW(
226 HKEY_CURRENT_USER, PromiseFlatString(path).get(), 0, nullptr,
227 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &rawKey, nullptr, txn,
228 nullptr);
229 NS_ENSURE_TRUE(status == ERROR_SUCCESS, false);
231 key.own(rawKey);
232 return true;
234 auto RegisterValue = [&](nsAutoRegKey& key, const nsAString& name,
235 unsigned long type, const nsAString& data) {
236 status = ::RegSetValueExW(
237 key, PromiseFlatString(name).get(), 0, type,
238 static_cast<const BYTE*>(PromiseFlatString(data).get()),
239 (data.Length() + 1) * sizeof(wchar_t));
241 return status == ERROR_SUCCESS;
244 // clang-format off
245 /* Writes the following keys and values to the registry.
246 * HKEY_CURRENT_USER\Software\Classes\AppID\{GUID} DllSurrogate : REG_SZ = ""
247 * \AppUserModelId\{MOZ_TOAST_APP_NAME}PortableToast-{install hash} CustomActivator : REG_SZ = {GUID}
248 * DisplayName : REG_EXPAND_SZ = {display name}
249 * IconUri : REG_EXPAND_SZ = {icon path}
250 * \CLSID\{GUID} AppID : REG_SZ = {GUID}
251 * \InprocServer32 (Default) : REG_SZ = {notificationserver.dll path}
253 // clang-format on
255 constexpr nsLiteralString classes = u"Software\\Classes\\"_ns;
257 nsAutoString aumid = classes + u"AppUserModelId\\"_ns + portableAumid;
258 nsAutoRegKey aumidKey;
259 NS_ENSURE_TRUE(RegisterKey(aumid, aumidKey), false);
261 nsAutoString guidStr;
263 DWORD bufferSizeBytes = NSID_LENGTH * sizeof(wchar_t);
264 Buffer<wchar_t> guidBuffer(bufferSizeBytes);
265 status = ::RegGetValueW(HKEY_CURRENT_USER, aumid.get(), L"CustomActivator",
266 RRF_RT_REG_SZ, 0, guidBuffer.Elements(),
267 &bufferSizeBytes);
269 CLSID unused;
270 if (status == ERROR_SUCCESS &&
271 SUCCEEDED(CLSIDFromString(guidBuffer.Elements(), &unused))) {
272 guidStr = guidBuffer.Elements();
273 } else {
274 nsIDToCString uuidString(nsID::GenerateUUID());
275 size_t len = strlen(uuidString.get());
276 MOZ_ASSERT(len == NSID_LENGTH - 1);
277 CopyASCIItoUTF16(nsDependentCSubstring(uuidString.get(), len), guidStr);
280 if (status == ERROR_SUCCESS) {
281 MOZ_LOG(sWASLog, LogLevel::Debug,
282 ("Existing CustomActivator guid found: '%s'",
283 NS_ConvertUTF16toUTF8(guidStr).get()));
284 } else {
285 MOZ_LOG(sWASLog, LogLevel::Debug,
286 ("New CustomActivator guid generated: '%s'",
287 NS_ConvertUTF16toUTF8(guidStr).get()));
290 NS_ENSURE_TRUE(
291 RegisterValue(aumidKey, u"CustomActivator"_ns, REG_SZ, guidStr), false);
292 nsAutoString brandName;
293 WidgetUtils::GetBrandShortName(brandName);
294 NS_ENSURE_TRUE(
295 RegisterValue(aumidKey, u"DisplayName"_ns, REG_EXPAND_SZ, brandName),
296 false);
297 NS_ENSURE_TRUE(
298 RegisterValue(aumidKey, u"IconUri"_ns, REG_EXPAND_SZ, iconPath), false);
300 nsAutoString appid = classes + u"AppID\\"_ns + guidStr;
301 nsAutoRegKey appidKey;
302 NS_ENSURE_TRUE(RegisterKey(appid, appidKey), false);
303 NS_ENSURE_TRUE(RegisterValue(appidKey, u"DllSurrogate"_ns, REG_SZ, u""_ns),
304 false);
306 nsAutoString clsid = classes + u"CLSID\\"_ns + guidStr;
307 nsAutoRegKey clsidKey;
308 NS_ENSURE_TRUE(RegisterKey(clsid, clsidKey), false);
309 NS_ENSURE_TRUE(RegisterValue(clsidKey, u"AppID"_ns, REG_SZ, guidStr), false);
311 nsAutoString inproc = clsid + u"\\InprocServer32"_ns;
312 nsAutoRegKey inprocKey;
313 NS_ENSURE_TRUE(RegisterKey(inproc, inprocKey), false);
314 // Set the component's path to this DLL
315 NS_ENSURE_TRUE(RegisterValue(inprocKey, u""_ns, REG_SZ, dllPath), false);
317 NS_ENSURE_TRUE(::CommitTransaction(txn), false);
319 MOZ_LOG(
320 sWASLog, LogLevel::Debug,
321 ("Updated registration for CustomActivator value in key 'HKCU\\%s': '%s'",
322 NS_ConvertUTF16toUTF8(aumid).get(),
323 NS_ConvertUTF16toUTF8(guidStr).get()));
324 aAumid.emplace(portableAumid);
325 return true;
328 nsresult ToastNotification::BackgroundDispatch(nsIRunnable* runnable) {
329 return NS_DispatchBackgroundTask(runnable);
332 NS_IMETHODIMP
333 ToastNotification::GetSuppressForScreenSharing(bool* aRetVal) {
334 *aRetVal = mSuppressForScreenSharing;
335 return NS_OK;
338 NS_IMETHODIMP
339 ToastNotification::SetSuppressForScreenSharing(bool aSuppress) {
340 mSuppressForScreenSharing = aSuppress;
341 return NS_OK;
344 NS_IMETHODIMP
345 ToastNotification::Observe(nsISupports* aSubject, const char* aTopic,
346 const char16_t* aData) {
347 nsDependentCString topic(aTopic);
349 for (auto iter = mActiveHandlers.Iter(); !iter.Done(); iter.Next()) {
350 RefPtr<ToastNotificationHandler> handler = iter.UserData();
351 if (topic == "last-pb-context-exited"_ns) {
352 handler->HideIfPrivate();
353 } else if (topic == "quit-application"_ns) {
354 // The handlers' destructors will do the right thing (de-register with
355 // Windows).
356 iter.Remove();
358 // Break the cycle between the handler and the MSCOM notification so the
359 // handler's destructor will be called.
360 handler->UnregisterHandler();
364 return NS_OK;
367 NS_IMETHODIMP
368 ToastNotification::ShowAlertNotification(
369 const nsAString& aImageUrl, const nsAString& aAlertTitle,
370 const nsAString& aAlertText, bool aAlertTextClickable,
371 const nsAString& aAlertCookie, nsIObserver* aAlertListener,
372 const nsAString& aAlertName, const nsAString& aBidi, const nsAString& aLang,
373 const nsAString& aData, nsIPrincipal* aPrincipal, bool aInPrivateBrowsing,
374 bool aRequireInteraction) {
375 nsCOMPtr<nsIAlertNotification> alert =
376 do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
377 if (NS_WARN_IF(!alert)) {
378 return NS_ERROR_FAILURE;
380 // vibrate is unused for now
381 nsTArray<uint32_t> vibrate;
382 nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle, aAlertText,
383 aAlertTextClickable, aAlertCookie, aBidi, aLang,
384 aData, aPrincipal, aInPrivateBrowsing,
385 aRequireInteraction, false, vibrate);
386 if (NS_WARN_IF(NS_FAILED(rv))) {
387 return rv;
389 return ShowAlert(alert, aAlertListener);
392 NS_IMETHODIMP
393 ToastNotification::ShowPersistentNotification(const nsAString& aPersistentData,
394 nsIAlertNotification* aAlert,
395 nsIObserver* aAlertListener) {
396 return ShowAlert(aAlert, aAlertListener);
399 NS_IMETHODIMP
400 ToastNotification::SetManualDoNotDisturb(bool aDoNotDisturb) {
401 return NS_ERROR_NOT_IMPLEMENTED;
404 NS_IMETHODIMP
405 ToastNotification::GetManualDoNotDisturb(bool* aRet) {
406 return NS_ERROR_NOT_IMPLEMENTED;
409 NS_IMETHODIMP
410 ToastNotification::ShowAlert(nsIAlertNotification* aAlert,
411 nsIObserver* aAlertListener) {
412 NS_ENSURE_ARG(aAlert);
414 if (mSuppressForScreenSharing) {
415 return NS_OK;
418 nsAutoString cookie;
419 MOZ_TRY(aAlert->GetCookie(cookie));
421 nsAutoString name;
422 MOZ_TRY(aAlert->GetName(name));
424 nsAutoString title;
425 MOZ_TRY(aAlert->GetTitle(title));
427 nsAutoString text;
428 MOZ_TRY(aAlert->GetText(text));
430 bool textClickable;
431 MOZ_TRY(aAlert->GetTextClickable(&textClickable));
433 nsAutoString hostPort;
434 MOZ_TRY(aAlert->GetSource(hostPort));
436 nsAutoString launchUrl;
437 MOZ_TRY(aAlert->GetLaunchURL(launchUrl));
439 bool requireInteraction;
440 MOZ_TRY(aAlert->GetRequireInteraction(&requireInteraction));
442 bool inPrivateBrowsing;
443 MOZ_TRY(aAlert->GetInPrivateBrowsing(&inPrivateBrowsing));
445 nsTArray<RefPtr<nsIAlertAction>> actions;
446 MOZ_TRY(aAlert->GetActions(actions));
448 nsCOMPtr<nsIPrincipal> principal;
449 MOZ_TRY(aAlert->GetPrincipal(getter_AddRefs(principal)));
450 bool isSystemPrincipal = principal && principal->IsSystemPrincipal();
452 RefPtr<ToastNotificationHandler> oldHandler = mActiveHandlers.Get(name);
454 NS_ENSURE_TRUE(mAumid.isSome(), NS_ERROR_UNEXPECTED);
455 RefPtr<ToastNotificationHandler> handler = new ToastNotificationHandler(
456 this, mAumid.ref(), aAlertListener, name, cookie, title, text, hostPort,
457 textClickable, requireInteraction, actions, isSystemPrincipal, launchUrl,
458 inPrivateBrowsing);
459 mActiveHandlers.InsertOrUpdate(name, RefPtr{handler});
461 MOZ_LOG(sWASLog, LogLevel::Debug,
462 ("Adding handler '%s': [%p] (now %d handlers)",
463 NS_ConvertUTF16toUTF8(name).get(), handler.get(),
464 mActiveHandlers.Count()));
466 nsresult rv = handler->InitAlertAsync(aAlert);
467 if (NS_WARN_IF(NS_FAILED(rv))) {
468 MOZ_LOG(sWASLog, LogLevel::Debug,
469 ("Failed to init alert, removing '%s'",
470 NS_ConvertUTF16toUTF8(name).get()));
471 mActiveHandlers.Remove(name);
472 handler->UnregisterHandler();
473 return rv;
476 // If there was a previous handler with the same name then unregister it.
477 if (oldHandler) {
478 oldHandler->UnregisterHandler();
481 return NS_OK;
484 NS_IMETHODIMP
485 ToastNotification::GetXmlStringForWindowsAlert(nsIAlertNotification* aAlert,
486 const nsAString& aWindowsTag,
487 nsAString& aString) {
488 NS_ENSURE_ARG(aAlert);
490 nsAutoString cookie;
491 MOZ_TRY(aAlert->GetCookie(cookie));
493 nsAutoString name;
494 MOZ_TRY(aAlert->GetName(name));
496 nsAutoString title;
497 MOZ_TRY(aAlert->GetTitle(title));
499 nsAutoString text;
500 MOZ_TRY(aAlert->GetText(text));
502 bool textClickable;
503 MOZ_TRY(aAlert->GetTextClickable(&textClickable));
505 nsAutoString hostPort;
506 MOZ_TRY(aAlert->GetSource(hostPort));
508 nsAutoString launchUrl;
509 MOZ_TRY(aAlert->GetLaunchURL(launchUrl));
511 bool requireInteraction;
512 MOZ_TRY(aAlert->GetRequireInteraction(&requireInteraction));
514 bool inPrivateBrowsing;
515 MOZ_TRY(aAlert->GetInPrivateBrowsing(&inPrivateBrowsing));
517 nsTArray<RefPtr<nsIAlertAction>> actions;
518 MOZ_TRY(aAlert->GetActions(actions));
520 nsCOMPtr<nsIPrincipal> principal;
521 MOZ_TRY(aAlert->GetPrincipal(getter_AddRefs(principal)));
522 bool isSystemPrincipal = principal && principal->IsSystemPrincipal();
524 NS_ENSURE_TRUE(mAumid.isSome(), NS_ERROR_UNEXPECTED);
525 RefPtr<ToastNotificationHandler> handler = new ToastNotificationHandler(
526 this, mAumid.ref(), nullptr /* aAlertListener */, name, cookie, title,
527 text, hostPort, textClickable, requireInteraction, actions,
528 isSystemPrincipal, launchUrl, inPrivateBrowsing);
530 // Usually, this will be empty during testing, making test output
531 // deterministic.
532 MOZ_TRY(handler->SetWindowsTag(aWindowsTag));
534 nsAutoString imageURL;
535 MOZ_TRY(aAlert->GetImageURL(imageURL));
537 return handler->CreateToastXmlString(imageURL, aString);
540 NS_IMETHODIMP
541 ToastNotification::HandleWindowsTag(const nsAString& aWindowsTag,
542 nsIUnknownWindowsTagListener* aListener,
543 bool* aRetVal) {
544 *aRetVal = false;
545 NS_ENSURE_TRUE(mAumid.isSome(), NS_ERROR_UNEXPECTED);
547 MOZ_LOG(sWASLog, LogLevel::Debug,
548 ("Iterating %d handlers", mActiveHandlers.Count()));
550 for (auto iter = mActiveHandlers.Iter(); !iter.Done(); iter.Next()) {
551 RefPtr<ToastNotificationHandler> handler = iter.UserData();
552 nsAutoString tag;
553 nsresult rv = handler->GetWindowsTag(tag);
555 if (NS_SUCCEEDED(rv)) {
556 MOZ_LOG(sWASLog, LogLevel::Debug,
557 ("Comparing external windowsTag '%s' to handled windowsTag '%s'",
558 NS_ConvertUTF16toUTF8(aWindowsTag).get(),
559 NS_ConvertUTF16toUTF8(tag).get()));
560 if (aWindowsTag.Equals(tag)) {
561 MOZ_LOG(sWASLog, LogLevel::Debug,
562 ("External windowsTag '%s' is handled by handler [%p]",
563 NS_ConvertUTF16toUTF8(aWindowsTag).get(), handler.get()));
564 *aRetVal = true;
566 nsString eventName(aWindowsTag);
567 nsAutoHandle event(
568 OpenEventW(EVENT_MODIFY_STATE, FALSE, eventName.get()));
569 if (event.get()) {
570 if (SetEvent(event)) {
571 MOZ_LOG(sWASLog, LogLevel::Info,
572 ("Set event for event named '%s'",
573 NS_ConvertUTF16toUTF8(eventName).get()));
574 } else {
575 MOZ_LOG(
576 sWASLog, LogLevel::Error,
577 ("Failed to set event for event named '%s' (GetLastError=%lu)",
578 NS_ConvertUTF16toUTF8(eventName).get(), GetLastError()));
580 } else {
581 MOZ_LOG(sWASLog, LogLevel::Error,
582 ("Failed to open event named '%s' (GetLastError=%lu)",
583 NS_ConvertUTF16toUTF8(eventName).get(), GetLastError()));
586 return NS_OK;
588 } else {
589 MOZ_LOG(sWASLog, LogLevel::Debug,
590 ("Failed to get windowsTag for handler [%p]", handler.get()));
594 MOZ_LOG(sWASLog, LogLevel::Debug, ("aListener [%p]", aListener));
595 if (aListener) {
596 bool foundTag;
597 nsAutoString launchUrl;
598 nsAutoString privilegedName;
599 MOZ_TRY(
600 ToastNotificationHandler::FindLaunchURLAndPrivilegedNameForWindowsTag(
601 aWindowsTag, mAumid.ref(), foundTag, launchUrl, privilegedName));
603 // The tag should always be found, so invoke the callback (even just for
604 // logging).
605 aListener->HandleUnknownWindowsTag(aWindowsTag, launchUrl, privilegedName);
608 return NS_OK;
611 NS_IMETHODIMP
612 ToastNotification::CloseAlert(const nsAString& aAlertName) {
613 RefPtr<ToastNotificationHandler> handler;
614 if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
615 return NS_OK;
617 mActiveHandlers.Remove(aAlertName);
618 handler->UnregisterHandler();
619 return NS_OK;
622 bool ToastNotification::IsActiveHandler(const nsAString& aAlertName,
623 ToastNotificationHandler* aHandler) {
624 RefPtr<ToastNotificationHandler> handler;
625 if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
626 return false;
628 return handler == aHandler;
631 void ToastNotification::RemoveHandler(const nsAString& aAlertName,
632 ToastNotificationHandler* aHandler) {
633 // The alert may have been replaced; only remove it from the active
634 // handler's map if it's the same.
635 if (IsActiveHandler(aAlertName, aHandler)) {
636 // Terrible things happen if the destructor of a handler is called inside
637 // the hashtable .Remove() method. Wait until we have returned from there.
638 RefPtr<ToastNotificationHandler> kungFuDeathGrip(aHandler);
639 mActiveHandlers.Remove(aAlertName);
640 aHandler->UnregisterHandler();
644 NS_IMETHODIMP
645 ToastNotification::RemoveAllNotificationsForInstall() {
646 HRESULT hr = S_OK;
648 ComPtr<IToastNotificationManagerStatics> manager;
649 hr = GetActivationFactory(
650 HStringReference(
651 RuntimeClass_Windows_UI_Notifications_ToastNotificationManager)
652 .Get(),
653 &manager);
654 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
656 HString aumid;
657 MOZ_ASSERT(mAumid.isSome());
658 hr = aumid.Set(mAumid.ref().get());
659 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
661 // Hide toasts in action center.
662 [&]() {
663 ComPtr<IToastNotificationManagerStatics2> manager2;
664 hr = manager.As(&manager2);
665 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
667 ComPtr<IToastNotificationHistory> history;
668 hr = manager2->get_History(&history);
669 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
671 hr = history->ClearWithId(aumid.Get());
672 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
673 }();
675 // Hide scheduled toasts.
676 [&]() {
677 ComPtr<IToastNotifier> notifier;
678 hr = manager->CreateToastNotifierWithId(aumid.Get(), &notifier);
679 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
681 ComPtr<IVectorView_ScheduledToastNotification> scheduledToasts;
682 hr = notifier->GetScheduledToastNotifications(&scheduledToasts);
683 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
685 unsigned int schedSize;
686 hr = scheduledToasts->get_Size(&schedSize);
687 NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
689 for (unsigned int i = 0; i < schedSize; i++) {
690 ComPtr<IScheduledToastNotification> schedToast;
691 hr = scheduledToasts->GetAt(i, &schedToast);
692 if (NS_WARN_IF(FAILED(hr))) {
693 continue;
696 hr = notifier->RemoveFromSchedule(schedToast.Get());
697 Unused << NS_WARN_IF(FAILED(hr));
699 }();
701 return NS_OK;
704 } // namespace widget
705 } // namespace mozilla