diff --git a/index.html b/index.html index 536a41d..7d3535b 100644 --- a/index.html +++ b/index.html @@ -16,193 +16,214 @@ }, group: "webapps", editors: [{ + name: "Marcos Cáceres", + company: "Apple Inc.", + companyURL: "https://apple.com", + w3cid: 78903, + }, + { name: "Matt Giuca", company: "Google Inc.", - companyURL: "https://google.com" - }, { + companyURL: "https://google.com", + retiredDate: "2022-12-21", + }, + { name: "Jay Harris", + company: "Google Inc.", + companyURL: "https://google.com", retiredDate: "2019-12-31" }], latestVersion: null, - // mdn: true, (not available yet) + mdn: true, xref: "web-platform", }; - +

- This specification defines an API allowing web pages and [=installed - web applications=] to set a badge on the document, or an - application-wide badge, shown in an operating-system-specific place - associated with the application (such as the shelf or home screen), for - the purpose of notifying the user when the state of the page or - application has changed (e.g., when new messages have arrived), without - showing a more heavyweight notification. + This specification defines an API that allows [=installed web + applications=] to set an application badge, which is usually shown + alongside the application's icon on the device's home screen or + application dock.

- This is an early draft of the Badging API spec. + This is a work in progress.

Usage examples

-

- The following example shows how an email application might set a badge - showing the unread count associated with the current document, which is - updated whenever the client polls for mail from the server. -

-        function pollForMail() {
-          // ... Fetch unread mail from server. ...
+      "Showing unread count on the app icon">
+        async function updateMailBadge() {
+          // Check if the API is supported.
+          if (!navigator.setAppBadge) return;
 
-          // Set the document badge. If getUnreadCount() returns 0, this is
-          // equivalent to navigator.clearClientBadge().
-          navigator.setClientBadge(getUnreadCount());
+          const unreadCount = await getUnreadMailCount();
+
+          // Try to set the app badge.
+          try {
+            await navigator.setAppBadge(unreadCount);
+          } catch (e) {
+            // The badge is not supported, or the user has prevented the app
+            // from setting a badge.
+          }
         }
       

- The next example shows how a game might show when it is the player's - turn. Again, this associates the badge with the current document. + The badge might show up on the application's icon in the operating + system. If multiple API calls within the same application [=badge/set=] + or [=badge/clear=] a badge, the most recent one takes effect, and may + continue being seen even after an application is closed.

-        function showPlayerTurn(playerTurnId) {
+      "Showing ready status on the app icon">
+        async function showPlayerTurn(playerTurnId) {
           if (playerTurnId === localPlayerId)
-            navigator.setClientBadge();
+            await navigator.setAppBadge();
           else
-            navigator.clearClientBadge();
+            await navigator.clearAppBadge();
         }
       
-

- A separate set of methods set the badge on the [=installed web - application=], if any, whose [=manifest/navigation scope=] this - document is within. The badge might show up on the application's icon - in the operating system shelf. These examples work the same as above, - except that the badge has global scope (if multiple documents within - the same application set or clear a badge, the most recent one takes - effect), and can continue being seen even after the last document - closes. +

+ On some operating systems [=badge/setting=] a badge can require + permission from the user. In this case, a developer need to query the + "[=notifications=]" permissions status before [=badge/setting=] a + badge. If the permission is not granted, the developer will need to + prompt for permission via the + {{Notification}}.{{Notification/requestPermission()}}.

-
-        function pollForMail() {
-          // ... Fetch unread mail from server. ...
-
-          // Set the app badge.
-          navigator.setAppBadge(getUnreadCount());
+      
+        async function checkPermission() {
+          permission = await navigator.permissions.query({
+            name: "notifications",
+          });
+          const button = document.getElementById("permission-button");
+          if (permission.state === "prompt") {
+            // Prompt the user to grant permission.
+            button.hidden = false;
+            button.addEventListener("click", async () => {
+              await Notification.requestPermission();
+              checkPermission();
+            }, { once: true });
+            return;
+          }
+          button.hidden = true;
         }
       
-
-        function showPlayerTurn(playerTurnId) {
-          if (playerTurnId === localPlayerId)
-            navigator.setAppBadge();
-          else
-            navigator.clearAppBadge();
-        }
-      
-

- To show a badge on both the document(s) and app icon(s), use both APIs - together. -

- Badge model + Model

- A badge may be one of the following values: + A badge is intended as a mechanism for [=installed web + applications=] to notify the user that there is some new activity that + might require their attention, or as a way of indicating a small amount + of information, such as an unread count.

-

- Each [=document=] and each [=installed web application=] is associated - with a badge value, which is initialized to nothing. + A [=badge=] can have one of the following values:

+
+
+ The special value "nothing": +
+
+ Indicates that there is no badge currently [=badge/set=]. +
+
+ The special value "flag": +
+
+ Indicates that the badge is [=badge/set=], but contains no specific + value. +
+
+ A number value: +
+
+ Indicates that the badge is [=badge/set=] to a numerical value + greater than `0`. +
+

- The user agent MAY reset an application's badge to nothing at - its discretion (for example, when the system is restarted). + An [=installed web application=] has an associated [=badge=], which is + initialized to [="nothing"=].

- If a badge is nothing, it is said to be - "cleared". Otherwise, it is said to - be "set". + The user agent MAY (re)[=set=] an application's badge to [="nothing"=] + at its discretion (for example, following system conventions).

-
+

- Badge display + Displaying a badge

- When a document's badge is set, if the document's [=browsing - context=] is a [=top-level browsing context=], the user agent SHOULD - display the document's badge value alongside the other identifying - information for that document (for example, on top of the document's - icon or near its title). + When the application's badge is set, the [=user agent=] or operating system SHOULD + display the application's [=badge=] alongside the primary iconic + representation of the application in the user's operating system (for + example, as a small overlay on top of the application's icon on the + home screen on a device).

-

- The user agent is not expected to display a badge associated with a - document that is not a [=top-level browsing context=], although it is - allowed to. A user agent does not need to store the badge for a - non-top-level browsing context if it does not intend to display it. +

+ A user agent MAY require [=express permission=] from the user to + [=badge/set=] the badge. When a user agent requires such + [=permission=], it SHOULD tie the permission grant to the + "[=notifications=]" permission.

- When the application's badge is set, the user agent SHOULD - display the application's badge alongside any symbolic representations - of the application in the user's operating system (for example, as a - small overlay on top of the application's icon). + When the [=badge=] is [=badge/set=] to [="flag"=], the [=user agent=] + or operating system SHOULD display an indicator with a non-specific + symbol (for example, a colored circle).

- If the badge is set to special value flag, the user agent SHOULD - show an indicator with a non-specific symbol (such as a coloured - circle). + When a [=badge=]'s value is [=badge/set=] to [="nothing"=], the [=user + agent=] or operating system SHOULD clear the [=badge=] by no longer + displaying it.

- The user agent MAY simplify or degrade the data in any way. For - example, a large integer may be saturated (for example, as "99+"). The - font and formatting are entirely at the user agent's discretion. The - user agent MAY ignore the data, and merely show a marker when the - status is set. -

-

- When presenting a badge, it SHOULD be formatted according to the user's - locale settings. For example, the badge - content '7' should be displayed as '7' in the locale 'en-NZ' but as '٧' in 'ar-EG'. + When the [=badge=] is [=badge/set=] to a [=number=], the [=user agent=] + or operating system:

+
-
+

Extensions to the `Navigator` and `WorkerNavigator` interfaces

-

- The {{Navigator}} and {{WorkerNavigator}} interfaces are extended with - methods for setting and [=clearing=] both the document and application - badge indicators from documents and service workers, respectively. -

-        [SecureContext]
-        partial interface Navigator {
-          Promise<undefined> setClientBadge(optional [EnforceRange] unsigned long long contents);
-          Promise<undefined> clearClientBadge();
-        };
-
         [SecureContext]
         interface mixin NavigatorBadge {
-          Promise<undefined> setAppBadge(optional [EnforceRange] unsigned long long contents);
+          Promise<undefined> setAppBadge(
+            optional [EnforceRange] unsigned long long contents
+          );
           Promise<undefined> clearAppBadge();
         };
 
@@ -210,190 +231,101 @@ 

WorkerNavigator includes NavigatorBadge;

- User agents that never display document badges SHOULD NOT expose the - {{Navigator/setClientBadge()}} and {{Navigator/clearClientBadge()}} - methods. Similarly, user agents that never display application badges - SHOULD NOT expose the {{NavigatorBadge/setAppBadge()}} and - {{NavigatorBadge/clearAppBadge()}} methods. + User agents that never display application badges SHOULD NOT + [=interface/include=] {{NavigatorBadge}}. +

+

+ setAppBadge() method +

+

+ When the {{NavigatorBadge/setAppBadge()}} method is called, the user + agent MUST [=set the application badge=] of [=this=] to value of the + |contents:unsigned long long| argument. +

+

+ clearAppBadge() method +

+

+ When the {{NavigatorBadge/clearAppBadge()}} method is called, the user + agent MUST [=set the application badge=] of [=this=] to 0.

-

- This is important as a feature-detection mechanism to allow sites to - determine whether setting a badge will have any effect, based on the - presence of the methods. In particular, sites can use the absence of - {{Navigator/setClientBadge()}} as a condition for displaying a fallback - badge in the page's [^title^] or link type "icon". +

+
+

+ Setting the application badge +

+

+ To set the application badge of platform object + |context:platform object| to an optional {{unsigned long long}} + |contents:unsigned long long| value:

-
-
-

- setClientBadge() method -

-

- When the {{Navigator/setClientBadge()}} method is called with - argument |contents:unsigned long long|: -

-
    -
  1. Return [=a promise resolved with=] undefined, and [=in - parallel=]: -
      -
    1. Let |document:Document| be [=this=]'s [=relevant global - object=]'s [=associated `Document`=]. -
    2. -
    3. If |contents| is omitted, set the badge associated with - |document| to flag. -
    4. -
    5. Else, if |contents| is 0, set the badge associated with - |document| to nothing. -
    6. -
    7. Else, set the badge associated with |document| to |contents|. -
    8. -
    -
  2. -
-
-
-

- clearClientBadge() method -

-

- When the {{Navigator/clearClientBadge()}} method is called: -

-
    -
  1. Let |document:Document| be [=this=]'s [=relevant global object=] - [=associated document=]. -
  2. -
  3. Return [=a promise resolved with=] undefined and [=In parallel=], - set the badge associated with |document| to nothing. -
  4. -
-
-
-

- setAppBadge() method -

-

- When the {{NavigatorBadge/setAppBadge()}} method is called with - argument |contents:unsigned long long|: -

-
    -
  1. Let |global| be [=this=]'s [=relevant global object=]. -
  2. -
  3. If |global|'s [=associated `Document`=] is null, and the - [=environment settings object=] is not a [=service worker client=], - return [=a promise rejected with=] a {{"NotSupportedError"}} - {{DOMException}}. -
  4. -
  5. Otherwise, return [=a promise resolved with=] undefined, and [=in - parallel=]: -
      -
    1. Let |result:set of applications| be the result of - determining the matching applications on [=this=]'s - [=relevant settings object=]. -
    2. -
    3. For each |app:application| in |result|: -
        -
      1. If |contents| is omitted, set the badge associated with - |app| to flag. -
      2. -
      3. Else, if |contents| is 0, set the badge associated with - |app| to nothing. -
      4. -
      5. Else, set the badge associated with |app| to |contents|. -
      6. -
      -
    4. -
    -
  6. -
-
-
-

- clearAppBadge() method -

-

- When the {{NavigatorBadge/clearAppBadge()}} method is called: -

-
    -
  1. Let |global| be [=this=]'s [=relevant global object=]. -
  2. -
  3. If |global|'s [=associated `Document`=] is null, and the - [=environment settings object=] is not a [=service worker client=], - return [=a promise rejected with=] a {{"NotSupportedError"}} - {{DOMException}}. -
  4. -
  5. Otherwise, return [=a promise resolved with=] undefined, and [=in - parallel=]: -
      -
    1. Let |result:set of applications| be the result of - determining the matching applications on [=this=]'s - [=relevant settings object=]. -
    2. -
    3. Set the badge associated with each application in |result| to - nothing. -
    4. -
    -
  6. -
-
-
-

- Determining the set of matching applications -

-
-

- This algorithm is used to decide which app(s) receive a badge when - the {{NavigatorBadge/setAppBadge()}} method is called from either a - document or service worker context. -

-

- If called from a document context, the badge is applied to at most - one application: the one with the most specific scope whose scope - encloses this document URL. -

-

- If called from a service worker context, the badge is applied to - all applications whose scope sits within the service worker's - scope. -

-
-

- The steps for determining the matching applications takes - an [=environment settings object=] |environment:environment settings - object| that either has a [=global object=] with an [=associated - `Document`=], or is a [=service worker client=], and returns a - [=set=] of [=installed web applications=]: -

-
    -
  1. If |environment| has a [=global object=] with an [=associated - `Document`=] that is not null: -
      -
    1. Let |apps:set of applications| be the [=set=] of [=installed - web applications=] such that |document|'s {{Document/URL}} is - [=manifest/within scope=] of the application's manifest. (Order - is not important.) -
    2. -
    3. [=list/Remove=] all elements of |apps| other than the one - with the longest (i.e., most specific) [=manifest/navigation - scope=]. -
    4. -
    5. Return |apps|. -
    6. -
    -
  2. -
  3. Otherwise (|environment| is a [=service worker client=]): -
      -
    1. Return the [=set=] of [=installed web applications=] whose - [=manifest/navigation scope=] is [=manifest/within scope=] of - |environment|'s [=service worker/containing service worker - registration=]'s [=service worker registration/scope url=]. - (Order is not important.) -
    2. -
    -
  4. -
-
+
    +
  1. Let |global| be |context|'s [=relevant global object=]. +
  2. +
  3. If |global| is a {{Window}} object, then: +
      +
    1. Let |document| be |global|'s [=associated `Document`=]. +
    2. +
    3. If |document| is not [=Document/fully active=], return [=a + promise rejected with=] a {{InvalidStateError}}. +
    4. +
    +
  4. +
  5. Let |promise| be [=a new promise=]. +
  6. +
  7. [=In parallel=]: +
      +
    1. If [=this=]'s [=relevant settings object=]'s [=origin=] is not + [=same origin=] with [=this=]'s [=relevant settings object=]'s + [=environment/top-level origin=], [=queue a global task=] on the + [=DOM manipulation task source=] given |global| to [=reject=] + |promise| with a {{"SecurityError"}} and terminate this algorithm. +
    2. +
    3. If the [=user agent=] requires [=express permission=] to + [=badge/set=] the application badge, then: +
        +
      1. Let |permissionState| be the result of [=getting the + current permission state=] with "[=notifications=]". +
      2. +
      3. If |permissionState| is not {{PermissionState/"granted"}}, + [=queue a global task=] on the [=user interaction task source=] + given |global| to [=reject=] |promise| with a + {{NotAllowedError}} and terminate this algorithm. +
      4. +
      +
    4. +
    5. Switching on |contents|, if it happens to be the case that: +
      +
      + |contents| was not passed: +
      +
      + [=badge/Set=] |badge| to [="flag"=]. +
      +
      + |contents| is 0: +
      +
      + [=badge/Set=] |badge| to [="nothing"=]. +
      +
      + |contents|: +
      +
      + [=badge/Set=] |badge| to |contents|. +
      +
      +
    6. +
    7. [=Queue a global task=] on the [=DOM manipulation task source=] + given |global| to [=resolve=] |promise| with `undefined`. +
    8. +
    +
  8. +
  9. Return |promise|. +
  10. +
-
+

Security and privacy considerations

@@ -403,7 +335,12 @@

application badge cannot be used as a storage or fingerprinting mechanism.

-
+

+ The [=user agent=] or operating system MAY [=badge/clear=] a [=badge=] + at its discretion, and to follow any system conventions (for example, + when the system is reset). +

+