Compare commits

..

2 Commits

Author SHA1 Message Date
Charles Kerr
4d1a32dab0 chore: make linter happy 2026-03-12 18:16:26 -05:00
Shelley Vohr
9774cbd508 feat: support notification priority on Windows
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.
2026-03-12 16:29:24 +01:00
6 changed files with 40 additions and 30 deletions

View File

@@ -88,11 +88,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:

View File

@@ -63,31 +63,6 @@ index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..209e3bcf8be5a23ac528dcd673bed82c
}
function ipToInt(ip) {
diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js
index a9f517ffc9e4eea5bc68997ffadc85d43dde2a52..d3db6bf119a6bb9cea1d069957f89cc7f99512b7 100644
--- a/lib/internal/trace_events_async_hooks.js
+++ b/lib/internal/trace_events_async_hooks.js
@@ -11,15 +11,13 @@ const { trace } = internalBinding('trace_events');
const async_wrap = internalBinding('async_wrap');
const async_hooks = require('async_hooks');
const {
- CHAR_LOWERCASE_B,
- CHAR_LOWERCASE_E,
+ CHAR_UPPERCASE_B,
+ CHAR_UPPERCASE_E,
} = require('internal/constants');
-// Use small letters such that chrome://tracing groups by the name.
-// The behavior is not only useful but the same as the events emitted using
-// the specific C++ macros.
-const kBeforeEvent = CHAR_LOWERCASE_B;
-const kEndEvent = CHAR_LOWERCASE_E;
+// See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
+const kBeforeEvent = CHAR_UPPERCASE_B;
+const kEndEvent = CHAR_UPPERCASE_E;
const kTraceEventCategory = 'node,node.async_hooks';
const kEnabled = Symbol('enabled');
diff --git a/node.gyp b/node.gyp
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..5eb70ce3820f2b82121bc102c5182ab768cbef36 100644
--- a/node.gyp

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -567,7 +567,14 @@ describe('command line switches', () => {
});
it('creates startup trace', async () => {
const rc = await startRemoteControlApp(['--trace-startup=*', `--trace-startup-file=${outputFilePath}`, '--trace-startup-duration=1', '--enable-logging']);
// node.async_hooks relies on %trace builtin to log trace points from JS
// https://github.com/nodejs/node/blob/8b199eef3dd4de910a6521adc42ae611a62a19e1/lib/internal/trace_events_async_hooks.js#L48-L53
// The phase event arg TRACE_EVENT_PHASE_NESTABLE_ASYNC_(BEGIN | END) is not supported in v8_use_perfetto mode
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/builtins-trace.cc;l=201-216
// and leads to the following error: TypeError: Trace event phase must be a number.
// TODO: Identify why the error started appearing with roll https://github.com/electron/electron/pull/47561
// given both v8_use_perfetto has been enabled before the roll and builtins-trace macro hasn't changed.
const rc = await startRemoteControlApp(['--trace-startup="*,-node.async_hooks"', `--trace-startup-file=${outputFilePath}`, '--trace-startup-duration=1', '--enable-logging']);
const stderrComplete = new Promise<string>(resolve => {
let stderr = '';
rc.process.stderr!.on('data', (chunk) => {