diff --git a/index.html b/index.html index e4b4acb..0937b5f 100644 --- a/index.html +++ b/index.html @@ -56,7 +56,8 @@ }; - +

The Push API enables sending of a push message to a web application via @@ -164,6 +165,534 @@

service worker is not currently running, the worker is started to enable delivery.

+
+

+ Declarative push message +

+

+ A declarative push message is a [=push message=] whose data is a JSON document + that is understood by the user agent. A user agent opportunistically parses each incoming + [=push message=] to determine if it is a [=declarative push message=] using the + [=declarative push message parser=]. +

+

+ A [=declarative push message=] allows for the creation and display of a notification + without the involvement of a service worker. Nevertheless, a service worker can still be + involved if desired by the [=application server=]. In such a scenario the declarative + nature of the [=push message=] serves as a backup in case the service worker was evicted + due to storage pressure, for instance. And also provides a more object-oriented approach + to transmitting notification data. +

+
+          {
+            "web_push": 8030,
+            "notification": {
+              "title": "Ada emailed ‘London’",
+              "lang": "en-US",
+              "dir": "ltr",
+              "body": "Did you hear about the tube strikes?",
+              "navigate": "https://email.example/message/12"
+            }
+          }
+        
+
+

+ Members +

+

+ A [=declarative push message=] has the following members: +

+
+
+ web_push (required) +
+
+

+ An integer that must be 8030. Used to disambiguate a [=declarative push message=] + from other JSON documents. +

+
+
+ notification (required) +
+
+

+ A JSON object consisting of the following members, all analogous to Notifications + API features, though sometimes with a slightly stricter type. Apart from + title all members are derived from the {{NotificationOptions}} + dictionary and to be maintained in tandem. [[NOTIFICATIONS]] +

+
+
+ title (required) +
+
+

+ A string. +

+
+
+ dir +
+
+

+ "auto", "ltr", or "rtl". +

+
+
+ lang +
+
+

+ A string that holds a language tag. +

+
+
+ body +
+
+

+ A string. +

+
+
+ navigate (required) +
+
+

+ A string that holds a URL. +

+
+
+ tag +
+
+

+ A string. +

+
+
+ image +
+
+

+ A string that holds a URL. +

+
+
+ icon +
+
+

+ A string that holds a URL. +

+
+
+ badge +
+
+

+ A string that holds a URL. +

+
+
+ vibrate +
+
+

+ An array of [=/32-bit unsigned integers=]. +

+
+
+ timestamp +
+
+

+ A [=/64-bit unsigned integer=]. +

+
+
+ renotify +
+
+

+ A boolean. +

+
+
+ silent +
+
+

+ A boolean. +

+
+
+ requireInteraction +
+
+

+ A boolean. +

+

+ This is not named require_interaction for consistency with the + {{NotificationOptions}} dictionary. +

+
+
+ data +
+
+

+ Any JSON value. +

+
+
+ actions +
+
+

+ An array of JSON objects consisting of the following members, all derived from + the {{NotificationAction}} dictionary and to be maintained in tandem. +

+
+
+ action (required) +
+
+

+ A string. +

+
+
+ title (required) +
+
+

+ A string. +

+
+
+ navigate (required) +
+
+

+ A string that holds a URL. +

+
+
+ icon +
+
+

+ A string that holds a URL. +

+
+
+
+
+
+
+ mutable +
+
+

+ A boolean. When true causes a push event to be dispatched to a service + worker (if any) containing the {{Notification}} object described by the + declarative push message. +

+
+
+
+
+

+ Parser +

+

+ A declarative push message parser result is a [=/tuple=] consisting of a + notification (a + [=/notification=]) and a mutable (a boolean). +

+

+ The declarative push message parser given a [=/byte sequence=] + bytes, [=/origin=] origin, [=/URL=] baseURL, and + {{EpochTimeStamp}} fallbackTimestamp runs these steps. They return failure + or a [=/declarative push message parser result=]. +

+
    +
  1. +

    + Let message be the result of [=parse JSON bytes to an Infra + value|parsing JSON bytes to an Infra value=] given bytes. If that throws + an exception, then return failure. +

    +
  2. +
  3. +

    + If message is not a [=/map=], then return failure. +

    +
  4. +
  5. +

    + If message["`web_push`"] does not [=map/exist=] or is not 8030, then + return failure. +

    +
  6. +
  7. +

    + If message["`notification`"] does not [=map/exist=], then return + failure. +

    +
  8. +
  9. +

    + Let notificationInput be message["`notification`"]. +

    +
  10. +
  11. +

    + If notificationInput is not a [=/map=], then return failure. +

    +
  12. +
  13. +

    + If notificationInput["`title`"] does not [=map/exist=] or is not a + string, then return failure. +

    +
  14. +
  15. +

    + If notificationInput["`navigate`"] does not [=map/exist=] or is not a + string, then return failure. +

    +
  16. + +
  17. +

    + Let notificationTitle be notificationInput["`title`"]. +

    +
  18. +
  19. +

    + Let notificationOptions be a {{NotificationOptions}} dictionary. +

    +
  20. +
  21. +

    + If notificationInput["`dir`"] [=map/exists=] and is "`auto`", "`ltr`", + or "`rtl`", then set notificationOptions["{{NotificationOptions/dir}}"] + to notificationInput["`dir`"]. +

    +
  22. +
  23. +

    + If notificationInput["`lang`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/lang}}"] to + notificationInput["`lang`"]. +

    +
  24. +
  25. +

    + If notificationInput["`body`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/body}}"] to + notificationInput["`body`"]. +

    +
  26. +
  27. +

    + Set notificationOptions["{{NotificationOptions/navigate}}"] to + notificationInput["`navigate`"], [=string/converted=]. +

    +
  28. +
  29. +

    + If notificationInput["`tag`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/tag}}"] to + notificationInput["`tag`"]. +

    +
  30. +
  31. +

    + If notificationInput["`image`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/image}}"] to + notificationInput["`image`"], [=string/converted=]. +

    +
  32. +
  33. +

    + If notificationInput["`icon`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/icon}}"] to + notificationInput["`icon`"], [=string/converted=]. +

    +
  34. +
  35. +

    + If notificationInput["`badge`"] [=map/exists=] and is a string, then set + notificationOptions["{{NotificationOptions/badge}}"] to + notificationInput["`badge`"], [=string/converted=]. +

    +
  36. +
  37. +

    + If notificationInput["`vibrate`"] [=map/exists=] and is a [=/list=] of + which each [=list/item=] is a [=/32-bit unsigned integer=], then set + notificationOptions["{{NotificationOptions/vibrate}}"] to + notificationInput["`vibrate`"]. +

    +
  38. +
  39. +

    + If notificationInput["`timestamp`"] [=map/exists=] and is a [=/64-bit + unsigned integer=], then set + notificationOptions["{{NotificationOptions/timestamp}}"] to + notificationInput["`timestamp`"]. +

    +
  40. +
  41. +

    + If notificationInput["`renotify`"] [=map/exists=] and is a boolean, then + set notificationOptions["{{NotificationOptions/renotify}}"] to + notificationInput["`renotify`"]. +

    +
  42. +
  43. +

    + If notificationInput["`silent`"] [=map/exists=] and is a boolean, then + set notificationOptions["{{NotificationOptions/silent}}"] to + notificationInput["`silent`"]. +

    +
  44. +
  45. +

    + If notificationInput["`requireInteraction`"] [=map/exists=] and is a + boolean, then set + notificationOptions["{{NotificationOptions/requireInteraction}}"] to + notificationInput["`requireInteraction`"]. +

    +
  46. +
  47. +

    + If notificationInput["`data`"] [=map/exists=], then set + notificationOptions["{{NotificationOptions/data}}"] to the result of + running convert an Infra value to a JSON-compatible JavaScript value given + notificationInput["`data`"]. +

    +
  48. +
  49. +

    + If notificationInput["`actions`"] [=map/exists=] and is a [=/list=]: +

    +
      +
    1. +

      + Let notificationActions be « ». +

      +
    2. +
    3. +

      + [=list/For each=] actionInput of + notificationInput["`actions`"]: +

      +
        +
      1. +

        + If actionInput["`action`"] does not [=map/exist=] or is not a + string, then [=iteration/continue=]. +

        +
      2. +
      3. +

        + If actionInput["`title`"] does not [=map/exist=] or is not a + string, then [=iteration/continue=]. +

        +
      4. +
      5. +

        + If actionInput["`navigate`"] does not [=map/exist=] or is not a + string, then [=iteration/continue=]. +

        +
      6. + +
      7. +

        + Let actionNavigate be actionInput["`navigate`"], + [=string/converted=]. +

        +
      8. +
      9. +

        + Let notificationAction be the {{NotificationAction}} dictionary + «[ "{{NotificationAction/action}}" → actionInput["`action`"], + "{{NotificationAction/title}}" → actionInput["`title`"], + "{{NotificationAction/navigate}}" → actionNavigate ]». +

        +
      10. + +
      11. +

        + If actionInput["`icon`"] [=map/exists=] and is a string, then + set notificationAction["{{NotificationAction/icon}}"] to + actionInput["`icon`"], [=string/converted=]. +

        +
      12. +
      13. +

        + [=list/Append=] notificationAction to + notificationActions. +

        +
      14. +
      +
    4. +
    5. +

      + Set notificationOptions["{{NotificationOptions/actions}}"] to + notificationActions. +

      +
    6. +
    +
  50. +
  51. +

    + Let notification be the result of creating a notification given + notificationTitle, notificationOptions, origin, + baseURL, and fallbackTimestamp. If this throws an exception, + then return failure. +

    +
  52. +
  53. +

    + If notification's [=notification/navigation URL=] is null, then return + failure. +

    +
  54. +
  55. +

    + If the [=notification action/navigation URL=] of any [=/notification action=] of + notification's [=notification/actions=] is null, then return failure. +

    +
  56. +
  57. +

    + Let mutable be false. +

    +
  58. +
  59. +

    + If message["`mutable`"] [=map/exists=] and + message["`mutable`"] is a boolean, then set mutable to + message["`mutable`"]. +

    +
  60. +
  61. +

    + Return (notification, mutable). +

    +
  62. +
+
+

Push subscription @@ -527,7 +1056,7 @@

The Service Worker specification defines a {{ServiceWorkerRegistration}} interface [[SERVICE-WORKERS]], which this specification extends.

-
+      
         [SecureContext]
         partial interface ServiceWorkerRegistration {
           readonly attribute PushManager pushManager;
@@ -978,8 +1507,8 @@ 

};

- {{PushMessageData}} objects have an associated bytes (a [=byte sequence=]), - which is set on creation. + {{PushMessageData}} objects have an associated bytes (a [=byte sequence=]), which is set on creation.

The arrayBuffer() method steps are to return an {{ArrayBuffer}} whose contents @@ -995,11 +1524,11 @@

{{ArrayBuffer}} whose contents are [=this=]'s [=bytes=]. Exceptions thrown during the creation of the {{ArrayBuffer}} object are re-thrown.

-

+

The json() method steps are to return the result of [=parse JSON bytes to a JavaScript value|parsing JSON bytes to a JavaScript value=] given [=this=]'s [=bytes=].

-

+

The text() method steps are to return the result of running UTF-8 decode on [=this=]'s [=bytes=].

@@ -1010,7 +1539,7 @@

  • Let |bytes| be an empty byte sequence.
  • Switch on |object|'s type: -
    +
    {{BufferSource}}
    @@ -1020,7 +1549,7 @@

    {{USVString}}
    -
    +
    Set |bytes| to the result of running utf-8 encode on |object|.

    @@ -1063,12 +1592,20 @@

    PushEvent Interface

    -
    +        
                 [Exposed=ServiceWorker, SecureContext]
                 interface PushEvent : ExtendableEvent {
                   constructor(DOMString type, optional PushEventInit eventInitDict = {});
                   readonly attribute PushMessageData? data;
    +              readonly attribute Notification? notification;
    +            };
    +
    +            dictionary PushEventInit : ExtendableEventInit {
    +              PushMessageDataInit? data = null;
    +              Notification? notification = null;
                 };
    +
    +            typedef (BufferSource or USVString) PushMessageDataInit;
               

    When a constructor of the PushEvent interface, or of an interface that @@ -1082,29 +1619,15 @@

  • Set |b| to the result of extracting a byte sequence from the "`data`" member of |eventInitDict|.
  • -
  • Set the `data` attribute of the event to a new PushMessageData instance with - `bytes` set to |b|. +
  • Set the `data` attribute of the event to a new {{PushMessageData}} instance whose + [=PushMessageData/bytes=] is |b|.
  • - The data, when getting, returns the value it was initialized with. + The data attribute must return the value it was initialized with.

    -

  • -
    -

    - PushEventInit dictionary -

    -
    -            typedef (BufferSource or USVString) PushMessageDataInit;
    -
    -            dictionary PushEventInit : ExtendableEventInit {
    -              PushMessageDataInit data;
    -            };
    -          

    - The data member contains the data included in the push message when - included and the user agent verified its authenticity. The value will be set to - `null` in all other cases. + The notification attribute must return the value it was initialized with.

    @@ -1127,28 +1650,149 @@

  • If the push message contains a payload:
      -
    1. Decrypt the push message payload using the private key from the key pair +
    2. Decrypt the push message's payload using the private key from the key pair associated with |subscription| and the process described in [[RFC8291]]. Set |bytes| to the resulting [=/byte sequence=].
    3. -
    4. If the push message payload could not be decrypted for any reason: +
    5. +

      + If the push message payload could not be decrypted for any reason, then + [=acknowledge a push message|acknowledge=] the push message and abort + these steps. +

      +

      + A `push` event is not fired for a push message that was not successfully + decrypted using the key pair associated with the push subscription. +

      +
    6. +
    +
  • +
  • +

    + If |bytes| is non-null: +

    +
      +
    1. +

      + Let |baseURL| be |registration|'s [=service worker registration/scope URL=]. +

      +
    2. +
    3. +

      + Let |origin| be |baseURL|'s [=url/origin=]. +

      +
    4. +
    5. +

      + Let |fallbackTimestamp| be [=current coarsened wall time=]. +

      +
    6. +
    7. +

      + Let |declarativeResult| be the result of running the [=/declarative push message + parser=] given |bytes|, |origin|, |baseURL|, and |fallbackTimestamp|. +

      +
    8. +
    9. +

      + If |declarativeResult| is not failure: +

        -
      1. Acknowledge the receipt of the push message according to [[RFC8030]]. - Though the message was not successfully received and processed, this prevents the - push service from attempting to retransmit the message; a badly encrypted message - is not recoverable. +
      2. +

        + Let |notification| be |declarativeResult|'s [=declarative push message parser + result/notification=]. +

        +
      3. +
      4. +

        + Set |notification|'s [=notification/service worker registration=] to + |registration|. +

      5. -
      6. Abort these steps. +
      7. +

        + Let |notificationShown| be false. +

        +
      8. +
      9. +

        + If |declarativeResult|'s [=declarative push message parser result/mutable=] + is true: +

        +
          +
        1. +

          + Let |result| be the result of [=fire a push event|firing a push event=] + given |registration|, null, and a new {{Notification}} object representing + |notification|. +

          +
        2. +
        3. +

          + If |result| is not failure, then set |notificationShown| to |result|'s + [=push event result/notification shown=]. +

          +
        4. +
        +
      10. +
      11. +

        + If |notificationShown| is false, then run the [=notification show steps=] + given |notification|. +

        +
      12. +
      13. +

        + [=acknowledge a push message|Acknowledge=] the push message and abort + these steps. +

      -

      - A `push` event will not be fired for a push message that was not - successfully decrypted using the key pair associated with the push - subscription. -

  • +
  • +

    + Let |data| be a new {{PushMessageData}} object whose [=PushMessageData/bytes=] is + |bytes| if |bytes| is non-null; otherwise null. +

    +
  • +
  • +

    + Let |result| be the result of [=fire a push event|firing a push event=] given + |registration|, |data|, and null. +

    +
  • +
  • +

    + If result is failure and the same push message has been delivered + to a service worker registration multiple times unsuccessfully, then + [=acknowledge a push message|acknowledge=] the push message. +

    +
  • +
  • +

    + If result is not failure, then [=acknowledge a push message|acknowledge=] + the push message. +

    +
  • + +

    + A push event result is a [=/tuple=] consisting of a notification shown (a [=/boolean=]). +

    +

    + To fire a push event given a [=/service worker registration=] |registration|, + {{PushMessageData}} object or null |data|, and a [=/notification=] or null |notification|, + run these steps. They return failure or a [=/push event result=]. +

    +
      +
    1. +

      + Let |notificationResult| be null. +

      +
    2. Fire a functional event named "`push`" using PushEvent on @@ -1156,44 +1800,80 @@

      - `data` + {{PushEvent/data}}
      - A new {{PushMessageData}} object whose [=bytes=] is |bytes|. + |data| +
      +
      + {{PushEvent/notification}} +
      +
      + |notification|

      Then run the following steps in parallel, with |dispatchedEvent|:

        -
      1. Wait for all of the promises in the [=ExtendableEvent/extend lifetime promises=] - of |dispatchedEvent| to resolve. -
      2. -
      3. If all the promises resolve successfully, acknowledge the receipt of the push - message according to [[RFC8030]] and abort these steps. -
      4. - If the same push message has been delivered to a service worker - registration multiple times unsuccessfully, acknowledge the receipt of the - push message according to [[RFC8030]]. + Wait for all of the promises in the [=ExtendableEvent/extend lifetime promises=] + of |dispatchedEvent| to resolve.

        +
      5. +
      6. - Acknowledging the push message causes the push service to stop - delivering the message and to report success to the application server. - This prevents the same push message from being retried by the push - service indefinitely. + If they do not resolve successfully, then set |notificationResult| to failure and + abort these steps.

        +
      7. +
      8. - Acknowledging also means that an application server could incorrectly - receive a delivery receipt indicating successful delivery of the push - message. Therefore, multiple rejections SHOULD be permitted before - acknowledging; allowing at least three attempts is recommended. + Set |notificationResult| to true if + {{ServiceWorkerRegistration/showNotification()}} has been invoked; otherwise + false.

    3. +
    4. +

      + Wait for |notificationResult| to be non-null. +

      +
    5. +
    6. +

      + If |notificationResult| is failure, then return failure. +

      +
    7. +
    8. +

      + [=/Assert=]: |notificationResult| is a [=/boolean=]. +

      +
    9. +
    10. +

      + Return (|notificationResult|). +

      +
    +

    + To acknowledge a push message given a push message + pushMessage means to acknowledge the receipt of pushMessage + according to [[RFC8030]]. +

    +

    + Acknowledging the push message causes the push service to stop delivering + the message and to report success to the application server. This prevents the + same push message from being retried by the push service indefinitely. +

    +

    + Acknowledging also means that an application server could incorrectly receive a + delivery receipt indicating successful delivery of the push message. Therefore, + multiple rejections SHOULD be permitted before acknowledging; allowing at least three + attempts is recommended. +

    @@ -1234,7 +1914,7 @@

    PushSubscriptionChangeEvent Interface

    -
    +          
                 [Exposed=ServiceWorker, SecureContext]
                 interface PushSubscriptionChangeEvent : ExtendableEvent {
                   constructor(DOMString type, optional PushSubscriptionChangeEventInit eventInitDict = {});
    @@ -1255,7 +1935,7 @@ 

    PushSubscriptionChangeEventInit Interface

    -
    +          
                   dictionary PushSubscriptionChangeEventInit : ExtendableEventInit {
                     PushSubscription newSubscription = null;
                     PushSubscription oldSubscription = null;