← Notification attribution for PWAs on macOS · Chrome 152 reference
v152 · behavior contract · badging
App badging requires notification permission for installed PWAs on macOS
With notification attribution active (Chrome 152+, macOS, installed PWAs), the Badging API only updates the PWA's Dock icon badge when the app has notification permission. Without it, navigator.setAppBadge() and navigator.clearAppBadge() silently do nothing — no error, no rejection, no badge.
syntax
The API surface is unchanged — the change is a precondition on the existing methods:
// Badging API (W3C WICG)
await navigator.setAppBadge(contents); // contents: optional integer
await navigator.clearAppBadge();
Source: W3C Badging API — setAppBadge() / clearAppBadge()
inputs
| Input | Contract under attribution (Chrome 152+, macOS, installed PWA) |
|---|---|
setAppBadge(n), n > 0 | Shows the number badge on the PWA's Dock icon only if notification permission is granted; otherwise silently ignored |
setAppBadge() / setAppBadge(0) | Per the Badging API, no argument shows an unnumbered badge and 0 clears; the same permission precondition applies (Safari differs: 0 clears rather than showing a dot — see compatibility) |
clearAppBadge() | Clears the badge; subject to the same permission gate |
Scope: the gate applies to the app badge of installed PWAs with attribution active. It does not change the Badging API on other platforms or for non-installed pages.
Source: blink-dev PSA; W3C Badging APIoutputs
- The returned
Promisesettles normally in both cases — there is no rejection when the update is dropped. The PSA states: “If notifications permission isn't granted the API will silently do nothing.” - When permission is granted, the badge is applied to the PWA's app shim Dock icon by macOS, routed via
AppShimManager::UpdateAppBadge(profile, app_id, badge)(app_shim_manager_mac.h).
errors
The defining characteristic of this change is the absence of an error path: without notification permission the calls succeed from the page's point of view and simply have no effect. Permission state is observable in advance via Notification.permission ("granted" / "denied" / "default") or navigator.permissions.query({ name: "notifications" }) — check it before relying on the badge.
context and exposure
The gate applies under the same conditions as attribution itself (macOS, installed with OS integration, registered ad-hoc-signed app shim, feature enabled — see requirements). Why the coupling exists: with attribution, the badge belongs to the PWA's own bundle identity at the OS level, and macOS only shows a bundle's badge when that bundle is permitted to post notifications — badge visibility is part of per-app notification settings.
Source: blink-dev PSA; web_app_tab_helper.cclifecycle
- The PWA calls
navigator.setAppBadge(n)(page or service worker context, per the Badging API). - Chrome checks the app's notification permission for the attributed PWA (the two-layer model — web origin permission plus the shim's OS-level permission; see the permission model).
- Granted → the badge value is routed to the app shim (
UpdateAppBadge) and macOS updates the Dock icon. Not granted → the call silently does nothing. - Revoking notification permission later (macOS settings or Chrome settings) removes the OS-level badge display; subsequent calls no-op until permission is restored.
examples
// Gate badge updates on notification permission — required for
// installed PWAs on macOS from Chrome 152.
async function updateUnreadBadge(count) {
if (!("setAppBadge" in navigator)) return;
if (Notification.permission !== "granted") {
showInAppUnreadIndicator(count); // don't lose the signal
return;
}
if (count > 0) {
await navigator.setAppBadge(count);
} else {
await navigator.clearAppBadge();
}
}
// Ask up front (from a user gesture) if your UX depends on the badge:
enableButton.addEventListener("click", async () => {
const result = await Notification.requestPermission();
if (result === "granted") await updateUnreadBadge(currentCount);
});
Try it in the Chrome Platform Showcase badge permission lab (route HEAD-checked 200, 2026-07-29).
Source: behavior per the PSA; demo per chrome-platform-showcasebrowser compatibility
From BCD api/Navigator.json setAppBadge (fetched 2026-07-29), with the macOS permission gate annotated — BCD has no entry for the gate itself:
| Browser | setAppBadge() | Notes |
|---|---|---|
| Chrome | 81 | BCD notes: “Windows and macOS since Chrome 81. ChromeOS since Chrome 91. Linux offers no universal badging API on the operating system level.” 152 (macOS): notification permission required for installed PWAs (listing, PSA) |
| Edge | 81 (mirror) | BCD edge: mirror; no separate statement on the macOS permission gate |
| Firefox | No support | BCD version_added: false |
| Safari | 17 | “Badging is supported for installed web apps on macOS Sonoma and higher”; passing 0 clears the badge instead of displaying an unnumbered dot |
security and privacy
- Consent coupling. The badge is a persistent, attention-demanding surface; tying it to notification permission means a user who revoked a PWA's notifications also loses its Dock badge — one consent decision governs both.
- No silent degradation signal. Pages cannot distinguish “badge applied” from “badge dropped for lack of permission” after the fact; the only reliable check is the permission state before calling.
- Matches WebKit's trust model. Safari applies the same coupling for installed web apps on macOS, so cross-browser PWAs can share one gating strategy.
specifications
| W3C Badging API | WICG draft — unchanged; the notification-permission precondition is a Chrome-on-macOS platform constraint, not spec text |
| WHATWG Notifications API Standard — permission model | Living Standard — referenced for the permission state the gate consults |
see also
- Notification attribution for PWAs on macOS — overview
requireInteractionnot honored for installed PWAs- Enterprise deployment (MDM pre-grant)
- ChromeStatus 5863296436666368
- MDN — Badging API (does not document the macOS permission gate — checked 2026-07-29)