Windows Native Notifications
Gecko has a concept of alerts: pop-up windows displaying small amounts of information. Such alerts are how Web Notifications are displayed. On Windows, Gecko supports “native notifications”: alerts rendered and managed by the OS. On Windows, such native notifications are referred to as toast notifications. This functionality is behind the alerts.useSystemBackend Gecko preference.
Native notifications can persist beyond the Firefox main process lifetime, visible both on the Desktop and in the Windows Action Center. When the user interacts with a Windows toast notification, the producing application can be notified in multiple ways. In Desktop Firefox, Windows launches the notificationserver.dll COM server, which provides a Win32 INotificationActivationCallback object that redirects the activation to Firefox. This document outlines how this activation and redirection is arranged.
Notification click sequence diagrams
The following diagrams show the major actors in the native notification lifecycle. Note that multiple instances of Firefox are involved. This is the case for two reasons:
A notification is handled by a different instance of Firefox than the one that created it if Firefox closes before the notification is interacted with. This is differentiated as
(previous session)and(new session)where relevant.The notification server invokes Firefox with command line arguments to ensure it is running. If Firefox was already running (
remote server), the newly launched Firefox instance (remote client) will forward the command line arguments to it.
When Firefox remains open until the notification is clicked
sequenceDiagram
participant FH as Firefox<br/>(remote server)
participant W as Windows
FH->>W: Show notification
create participant C as notificationserver.dll<br/>COM Server
W->>C: Notification clicked
Note left of C: Named pipe created
create participant FC as Firefox<br/>(remote client)
C->>FC: Launch browser
destroy FC
FC->>FH: Redirect command line
rect grey
Note left of C: Pipe IPC
FH->>C: Request foreground privilege
C->>FH: Success or failure of AllowSetForegroundWindow
end
destroy C
C->>W: INotificationActivationCallback::Activate<br/>returns
W->>FH: Call in-memory<br/>ToastNotification::Activated callback
Note over FH: Notification handled
Note
The ToastNotification::Activated in-memory notification callbacks are currently only registered with the application instance that created the notification. Additionally, the callback is only called by the system after INotificationActivationCallback::Activate returns; if needed we could register ToastNotification::Activated before returning from INotificationActivationCallback::Activate.
When Firefox closes before the notification is clicked
sequenceDiagram
participant FP as Firefox<br/>(previous session)
participant W as Windows
FP->>W: Show notification
destroy FP
FP-->W: Firefox closed
create participant C as notificationserver.dll<br/>COM Server
W->>C: Notification clicked
Note right of C: Named pipe created
create participant FN as Firefox<br/>(new session)
C->>FN: Launch browser
rect grey
Note right of C: Pipe IPC
FN->>C: Request foreground privilege
C->>FN: Success or failure of<br/>AllowSetForegroundWindow
end
destroy C
C->>W: INotificationActivationCallback::Activate<br/>returns
Note over FN: Notification handled
When Firefox closes and reopens before the notification is clicked
sequenceDiagram
participant FP as Firefox<br/>(previous session)
participant W as Windows
FP->>W: Show notification
destroy FP
FP-->W: Firefox closed
create participant FS as Firefox<br/>(remote server)<br/>(new session)
W-->FS: Firefox reopened
create participant C as notificationserver.dll<br/>COM Server
W->>C: Notification clicked
Note left of C: Named pipe created
create participant FC as Firefox<br/>(remote client)
C->>FC: Launch browser
destroy FC
FC->>FS: Redirect command line
rect grey
Note left of C: Pipe IPC
FS->>C: Request foreground privilege
C->>FS: Success or failure of<br/>AllowSetForegroundWindow
end
destroy C
C->>W: INotificationActivationCallback::Activate returns
Note over FS: Notification handled
Firefox creates Windows toast notifications using the Win32 CreateToastNotifierWithId API. The toast notification includes sufficient information to invoke Firefox and identify a particular toast notification. If an appropriate Firefox is already running, the new invocation will use the remote component (not to be confused with various remote protocols for automation) to forward the command line to the running Firefox. The running Firefox uses the command line arguments to identify the toast notification and invoke appropriate callbacks, etc. If an appropriate Firefox is not running, the new invocation will navigate to a location appropriate to the notification. See nsDefaultCommandLineHandler for details.
Registering the COM server DLL with Windows
The notificationserver.dll must be registered with Windows. This registration is done by adding various values to the Windows registry; the registration includes details about the COM server and about the visual display of notifications. The installer does this in HKEY_CLASSES_ROOT at install time and post-update time. (The uninstaller removes any added registry values.) If Firefox does not find these values, it dynamically registers the COM server DLL at runtime.
To observe and debug the COM server registration process, set MOZ_LOG=WindowsAlertService:5 in the Firefox process environment. To debug the notification server itself, set alerts.useSystemBackend.windows.notificationserver.verbose to true. The notification server’s logged messages can be viewed in Windows Event Viewer under Windows Logs/Application with Source [MOZ_APP_DISPLAYNAME] Notification Server.
Once registered, the notification server will always be called as a result of the notification being clicked. Not all use cases for notifications are supported by the notification server, notably the Default Browser Agent. In these instances, the notification server processing can be short-circuited via the alerts.useSystemBackend.windows.notificationserver.enabled pref.
Transferring Foreground Privilege
A process must have foreground privilege in order to set focus for its windows.
Foreground privilege is passed to notificationserver.dll when INotificationActivationCallback is called (likely via CoAllowSetForegroundWindow). We can’t pass this privilege directly to the launched Firefox instance. The exact reason was never verified, but it is likely the result of a break in the chain of foreground privilege passing between the launcher process, command line remoting, and the final receiving Firefox instance, either due to privileges not being passed, or privileges not being receivable because the non-terminal instances have no UI.
In order to pass the foreground privilege, the notification server provides a named pipe identifier for the notification handling Firefox instance to communicate its PID back. The notification server then transfers foreground privileges via AllowSetForegroundWindow.
Warning
Applications attached to a debugger always have foreground privilege, which leads to Heisenbugs when debugging window focus issues. See SetForegroundWindow restrictions.