Repository: electron/electron
Pull request: feat: support notification priority on Windows

#### Description of Change

Add Windows notifications support urgency/priority levels. This maps the existing `urgency` option (previously Linux-only) to Windows toast notification priorities:

- 'critical' maps to ToastNotificationPriority_High, which sorts the notification above default-priority items in Action Center.
- 'normal' and 'low' both map to ToastNotificationPriority_Default.

Note that on Windows, 'critical' priority does not prevent the toast from being auto-dismissed. Users should additionally set `timeoutType` to 'never' for that behavior.

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included
- [x] I have built and tested this PR
- [ ] `npm test` passes
- [x] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Notes: Added support for the `urgency` option in Notifications on Windows.

Changed files:
filename: docs/api/notification.md
patch: @@ -90,11 +90,15 @@ app.whenReady().then(() => {
   * `timeoutType` string (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
   * `replyPlaceholder` string (optional) _macOS_ - The placeholder to write in the inline reply input field.
   * `sound` string (optional) _macOS_ - The name of the sound file to play when the notification is shown.
-  * `urgency` string (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
+  * `urgency` string (optional) _Linux_ _Windows_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
   * `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
   * `closeButtonText` string (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
   * `toastXml` string (optional) _Windows_ - A custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
 
+> [!NOTE]
+> On Windows, `urgency` type 'critical' sorts the notification higher in Action Center (above default priority notifications), but does not prevent auto-dismissal. To prevent auto-dismissal, you should also set
+> `timeoutType` to 'never'.
+
 ### Instance Events
 
 Objects created with `new Notification` emit the following events:

filename: shell/browser/notifications/notification.h
patch: @@ -45,7 +45,7 @@ struct NotificationOptions {
   std::u16string timeout_type;
   std::u16string reply_placeholder;
   std::u16string sound;
-  std::u16string urgency;  // Linux
+  std::u16string urgency;  // Linux/Windows
   std::vector<NotificationAction> actions;
   std::u16string close_button_text;
   std::u16string toast_xml;

filename: shell/browser/notifications/win/windows_toast_notification.cc
patch: @@ -280,8 +280,9 @@ void WindowsToastNotification::CreateToastNotificationOnBackgroundThread(
   // Continue to create the toast notification
   ComPtr<ABI::Windows::UI::Notifications::IToastNotification>
       toast_notification;
-  if (!CreateToastNotification(toast_xml, notification_id, weak_notification,
-                               ui_task_runner, &toast_notification)) {
+  if (!CreateToastNotification(toast_xml, options, notification_id,
+                               weak_notification, ui_task_runner,
+                               &toast_notification)) {
     return;  // Error already posted to UI thread
   }
 
@@ -349,6 +350,7 @@ bool WindowsToastNotification::CreateToastXmlDocument(
 // returns the created notification via out parameter.
 bool WindowsToastNotification::CreateToastNotification(
     ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
+    const NotificationOptions& options,
     const std::string& notification_id,
     base::WeakPtr<Notification> weak_notification,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -416,6 +418,27 @@ bool WindowsToastNotification::CreateToastNotification(
     return false;
   }
 
+  ComPtr<winui::Notifications::IToastNotification4> toast4;
+  hr = (*toast_notification)->QueryInterface(IID_PPV_ARGS(&toast4));
+  if (SUCCEEDED(hr)) {
+    winui::Notifications::ToastNotificationPriority priority =
+        winui::Notifications::ToastNotificationPriority::
+            ToastNotificationPriority_Default;
+    if (options.urgency == u"critical") {
+      priority = winui::Notifications::ToastNotificationPriority::
+          ToastNotificationPriority_High;
+    }
+
+    hr = toast4->put_Priority(priority);
+    if (FAILED(hr)) {
+      std::string err = base::StrCat({"WinAPI: Setting priority failed, ERROR ",
+                                      FailureResultToString(hr)});
+      DebugLog(err);
+      PostNotificationFailedToUIThread(weak_notification, err, ui_task_runner);
+      return false;
+    }
+  }
+
   return true;
 }
 

filename: shell/browser/notifications/win/windows_toast_notification.h
patch: @@ -94,6 +94,7 @@ class WindowsToastNotification : public Notification {
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
   static bool CreateToastNotification(
       ComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> toast_xml,
+      const NotificationOptions& options,
       const std::string& notification_id,
       base::WeakPtr<Notification> weak_notification,
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,