Skip to content

Latest commit

 

History

History
469 lines (350 loc) · 24.9 KB

File metadata and controls

469 lines (350 loc) · 24.9 KB

Badging API Explainer

Author: Matt Giuca mgiuca@chromium.org
Author: Jay Harris harrisjay@chromium.org
Author: Marcos Cáceres marcos@marcosc.com

Date: 2019-10-10

Table of Contents

Overview

The Badging API is a Web Platform API allowing websites to apply badges (small status indicators) to installed web apps on their origin.

For installed web applications, the badge can be applied in whatever place the OS shows apps, such as the shelf, home screen or dock.

Here are some examples of app badging applied at the OS level:

Windows taskbar badge
Windows taskbar badge

macOS dock badge
macOS dock badge

Android home screen badge
Android home screen badge

Badges applied to apps can be shown and updated even when there are no tabs or windows open for the app.

Goals and use cases

The purpose of this API is:

  • To subtly notify the user that there is new activity that might require their attention without requiring an OS-level (banner) notification.
  • To indicate a small amount of additional information, such as an unread count.
  • To allow Installed Web Applications to convey this information, regardless of whether they are currently open.

Non-goals are:

  • To provide an arbitrary image badge. The web platform already provides this capability via favicons.

Possible areas for expansion:

  • Support rendering a small status indicator (e.g., a music app shows ▶️ or ⏸️; a weather app shows ⛈️ or ⛅️): either a pre-defined set of glyphs or simply allowing a Unicode character to be rendered.

Examples of sites that may use this API:

  • Chat, email, and social apps could signal that new messages have arrived.
  • Any application that needs to signal that user action is required (e.g., in a turn-based game, when it is the player's turn).
  • As a permanent indicator of a page's status (e.g., on a build page, to show that the build has completed).

(See Usage examples below for the code you might use.)

Comparison to Notifications

The Badge API goes hand-in-hand with Notifications, since both APIs provide a way to give the user updates when they aren't directly looking at the page.

Arguably, Badge could be part of the Notifications API, but we view it as a separate, complementary API. A notification is, by definition, "an abstract representation of something that happened, such as the delivery of a message," whereas, at least in this context, a badge is an abstract representation of the underlying state, such as the number of unread messages. A badge is not a notification.

Some situations will call for just using notifications, or just using a badge update, or both simultaneously. Commonly, you will both show a notification and set a badge at the same time (such as when a new email message arrives). But there are situations where it is appropriate to use the badging API without a notification: for much higher frequency / lower priority events (such as group chat messages not directly addressed to the user). Whenever some underlying state changes that would be convenient for the user to be able to see at a glance, but not important enough to disrupt the user, use of the Badge API without a notification is warranted.

There may be no need to request permission to use the badging API, since it is much less invasive than a notification, but this is at the discretion of the user agent.

Usage examples

To set a numeric badge on the current app:

navigator.setAppBadge(getUnreadCount());

If getUnreadCount() (the argument to navigator.setAppBadge) is 0, it will automatically clear the badge.

If you just want to show a status indicator flag without a number, use the Boolean mode of the API by calling navigator.setAppBadge without an argument, and navigator.clearAppBadge() to clear it:

if (myTurn())
  navigator.setAppBadge();
else
  navigator.clearAppBadge();

The effects of the app API are global and may outlast the document (it is intended to persist at least until the user agent closes). It can also be used from a service worker.

Here's a complete example for a site that wants to set the badge on the current installed web app:

// Should be called whenever the unread count changes (new mail arrives, or mail
// is marked read/unread).
function unreadCountChanged(newUnreadCount) {
  // Set the app badge, for app icons and links. This has a global and
  // semi-permanent effect, outliving the current document.
  if (navigator.setAppBadge) {
    navigator.setAppBadge(newUnreadCount);
  }
}

More advanced examples are given in a separate document.

Usage from service workers

TODO: This section could use some fleshing out.

Calling the API from a service worker has some differences:

When navigator.setAppBadge() is called from a service worker, it badges all apps whose scope is inside the service worker scope.

Background updates

For app badges, we would like to be able to update the badge with a server-side push, while there are no active documents open. This would allow, for example, app icon badges to show an up-to-date unread count even when no pages are open.

In this section, we explore two APIs that could be useful for this: Push API and Periodic Background Sync.

The Push problem

The Push API allows servers to send messages to service workers, which can run JavaScript code even when no foreground page is running. Thus, a server push could trigger a navigator.setAppBadge().

However, there is a de facto standard requirement that whenever a push is received, a notification needs to be displayed. This means it's impossible to subtly update a badge in the background while no pages are open, without also displaying a notification.

This may be fine for some use cases (if you want to always show a notification when updating the badge, possibly with an exception of not showing a notification if the app is in the foreground). But we specifically designed this API as a subtle notice mechanism that does not require a more distracting notification in order to set a badge. Because of this de facto requirement, the Push API currently isn't suitable for this use case.

There is also a requirement that the user grants notification permission to subscribe to push messages.

Periodic Background Sync

Periodic Background Sync is a proposed extension to the Background Sync API, that allows a service worker to periodically poll the server, which could be used to get an updated status and call navigator.setAppBadge(). However, this API is unreliable: the period that it gets called is at the discretion of the user agent and can be subject to things like battery status. This isn't really the use case that Periodic Background Sync was designed for (which is having caches updated while the user isn't directly using a site, not updating UI that's immediately visible to the user).

That means when the page isn't open, you could have the badge indicator update every once in awhile, but have no guarantee that it would be up to date.

(Also, the Periodic Background Sync API is not yet implemented in any browser, though it is in an origin trial in Chrome.)

It's possible that the use of Push API and Periodic Background Sync together is "good enough": high-priority notices (that show a notification) come in through a Push and are displayed immediately with a notification; low-priority notices are displayed as badges immediately if there are pages open, or "eventually" if there are no pages open.

However, in our view, there is a need for immediate update to low-priority notices: this is akin to the urgency vs importance dichotomy: a badge is for non-important information but that doesn't mean the user doesn't want to see the notice in a timely fashion.

Possible changes to the Push API

Here we discuss ways to potentially allow the Push API to update badges without showing notifications.

Note: This should not be considered blocking. The Badge API is usable, including from service workers, without these changes, so we consider these as add-ons that we could introduce at a later time.

Waiving the notification requirement

The purpose of the de facto requirement to show a notification upon receipt of a push message is to prevent malicious sites from using high-frequency pushes to do invisible background work, such as crypto mining (use of the user's computing resources) or monitoring the user's IP address, giving coarse-grained geographic location (privacy concern). The theory is that if the site is required to show a notification on each push, the user is at least going to be aware that the site is constantly doing something, and if it's unreasonably spammy, the user is likely to revoke the notification permission, thus erasing the push subscription.

We could make it so that use of the Badge API serves the same function as showing a notification (fulfilling the requirement to use the Push API). But doing so would probably nullify the reason for this requirement in the first place: a malicious site that wants to do crypto mining could just set a badge every 30 seconds and the user would probably not notice it. And they could set the badge to the same value it already had (which we definitely want to allow; otherwise the server has to keep track of what value is currently being displayed in each client).

On the other hand, this may be acceptable if we allow user agents to dictate a high bar for waiving this requirement, e.g., "the user must have installed the site as an application, and enabled notifications". It may be an acceptable trade-off to allow these "semi-trusted" sites to perform silent background tasks, in order to let them keep badges up to date in a timely fashion. This would have to be a discussion around privacy trade-offs which we (the Badge API authors) aren't equipped to answer by ourselves.

Technically, the way we would do this is by allowing certain sites to set userVisibleOnly to false in the push subscription (which currently has no defined meaning, and in Chrome at least, is not allowed to be false, so there is currently a de facto requirement that it be set to true). If a site is allowed to set userVisibleOnly to false, then it can receive badge-only push messages. If not, it is bound by the existing rules, and must either show a notification, or turn off low-priority messages. This might require user opt-in (or a separate permission to notifications).

A separate channel for Badge payloads

A more comprehensive solution includes some significant additions to the Push API (which the authors of the Badge spec are not equipped to do).

(This has been briefly discussed with Peter Beverloo, (@beverloo) an editor of the Push API spec, but only at a very high level.)

Instead of allowing the service worker to run JavaScript code and call the Badge API, we would introduce a new concept to a Push subscription called a "channel", which dictates what is done with the payload upon receipt. The default channel would be "event" (the payload is delivered to the "push" event), with a new channel, "badge", which imposes a specific format to the payload. The payload would now be interpreted as a JSON dictionary containing parameters to the navigator.setAppBadge() method. Upon receipt of the push, the user agent would automatically call the Badge API without running any user code, and with no requirement to show a notification.

This solution still has the following two flaws (expressed by Peter Beverloo):

  1. The server can still use badge delivery to know whether device is online through delivery receipts. This may or may not be a privacy concern.
  2. (On mobile) (at least on Android and iOS) the browser needs to be woken up to decrypt and process the payload; it can't be processed by the host OS's push receipt system due to the encryption. This could be a big resource drain if badges are pushed too frequently (the same concern applies for notifications, but as above, there is a natural tendency for developers to reduce the number of user-visible notifications due to user spam; the same forcing function does not apply for setting a badge).

These could be partly mitigated by introducing a throttle mechanism, which would mean badge updates can't be immediate (moving towards Periodic Background Sync), but at least we would be able to set a throttle sensible for badge updates, rather than the total lack of guarantees that PBS gives us.

Conclusion

Both of the above changes present huge obstacles and discussions involving privacy, resource usage, and utility trade-offs. Due to the increased complexity, we are not considering changes to the Push API at this time.

Feature detection

Sites can feature-detect the Badge API by checking for the presence of the navigator.setAppBadge method:

if (navigator.setAppBadge) {
  navigator.setAppBadge(getUnreadCount());
}

Detailed API proposal

The model

A badge is associated with an installed app.

At any time, the badge for a specific app, if it is set, may be either:

  • A "flag" indicating the presence of a badge with no contents, or
  • A positive integer.

The model does not allow a badge to be a negative integer, or the integer value 0 (setting the badge to 0 is equivalent to clearing the badge).

Important distinction: A "flag" badge generally displays a visual indicator to the user (such as a dot or circle), whereas a cleared badge (value 0 or calling clearAppBadge()) clears it. These are semantically different states, and platforms generally won't treat a request to set a "flag" as equivalent to clearing the badge.

The user agent is allowed to clear all badges on an origin whenever there are no foreground pages open on the origin (the intention of this is so that when the user agent quits, it does not need to serialize all the badge data and restore it on start-up; sites should re-apply the badge when they open).

The API

The Badge API methods are members of interface is a member object on Navigator and WorkerNavigator. They are as follows:

  • navigator.setAppBadge([contents]): Sets the badge for the matching app(s) to contents (an integer), or to "flag" if contents is omitted. If contents is 0, clears the badge for the matching app(s).
  • navigator.clearAppBadge(): Clears the badge for the matching app(s).

The matching app(s) has a different meaning depending on the context:

  • If called from a document, this refers to an installed app that this document is within scope of. If multiple apps match, it is the one with the most specific scope. If none match, the method has no effect. (Zero or one apps.)
  • If called from a service worker, this refers to all apps whose scope URL matches the service worker registration of this service worker. (Zero or more apps.)

Note: Should we have a separate overload for boolean flags now, as discussed in Issue 19 and Issue 42?

UX treatment

App badges are shown in OS-specific contexts. User agents should attempt reuse existing operating system APIs and conventions, to achieve a native look-and-feel for the badge.

Implementation Considerations for Platform Limitations

Different operating systems have varying support for badge types. Since the OS ultimately controls badge rendering, it is the user agent that communicates the correct semantic intent:

  • Flag support: Some platforms do not natively support "flag" badges (badges without numbers). In such cases, user agents communicate to the OS using the closest available representation that conveys badge presence (such as a generic symbol or the number "1") rather than requesting badge clearing.
  • Number support: Some platforms might not support numbered badges. In such cases, user agents can communicate the badge to the OS as a flag representation.
  • Semantic preservation: User agents preserve the semantic distinction between "flag" (show something) and "nothing" (show nothing) when communicating with the operating system, even when platform capabilities are limited.
  • OS control: The ultimate display behavior is controlled by the operating system and can be further controlled by user settings or system conventions.

This approach ensures consistent semantic behavior across platforms and prevents the issues identified in implementations where setAppBadge() (flag) incorrectly clears badges instead of displaying them.

Security and Privacy Considerations

The API is write-only, so data badged can't be used to track a user. Whether the API is present could possibly be used as a bit of entropy to fingerprint users, but this is the case for all new APIs.

These methods are only usable from secure contexts, so forged pages can't set badges on behalf of an origin they don't control.

If the badge methods are called from inside an iframe, the app API should apply to the app enclosing the iframed contents' URL, not the containing page's URL.

There are additional privacy considerations relating to the proposed extensions to the Push API, noted above. However, this does not apply to the base Badge API.

Design Questions

What data types are supported in different operating systems?

See implementation.md.

Why limit support to just an integer? What about other characters?

It isn't a technical limitation, it's an attempt to keep behavior as consistent as possible on different host platforms (UWP only supports a set of symbols, while iOS, Android and Ubuntu don't support them at all).

Limiting support to integers makes behavior more predictable, though we are considering whether it might be worth adding support for other characters or symbols in future.

Couldn't this be a declarative API (i.e., a DOM element), so it would work without JavaScript?

The app API is shared between different pages and has a lifetime beyond the page, so a JavaScript API is more appropriate for this use case.

Is this API useful for mobile OS’s?

iOS has support for badging APIs (see iOS).

On Android, badging is blocked at an OS level, as there is no API for setting a badge without also displaying a notification. However, a badge will already be displayed if a PWA has pending notifications (it just doesn't allow the fine grained control provided by this API).

To summarize: This API cannot be used in PWAs on either mobile operating system. Support on iOS is blocked until Safari implements the API and Android does not have an API for controlling badges. Should either situation change, the badging API will become trivially available.

Why is this API attached to navigator instead of window or notifications?

There is more detail and discussion in Issue 55.

Is there an upper limit on the size of the integer? And if so, what's the behavior if that limit is reached?

There is no upper limit (besides Number.MAX_SAFE_INTEGER). However, each user agent is free to impose a limit and silently saturate the value (e.g., display all values above 99 as "99+").

Are you concerned about apps perpetually showing a large unread count?

Yes. If users habitually leave mail or chats unread, and mail or chat apps simply call set(getUnreadCount()), it could result in several apps simply showing a large number, presenting several issues:

  • Leaving "clutter" on the user's shelf, and
  • Making the user unable to tell when new messages arrive.

However, the only solution to this is a much more limited API which only lets you show the count of notifications (or similar). We wanted to give apps the full power of showing a native badge.

Internationalization

The API allows set()ing an unsigned long long. When presenting this value, it should be formatted according to the user's locale settings.

Index of Considered Alternatives