diff --git a/index.html b/index.html index 603265b..3039990 100644 --- a/index.html +++ b/index.html @@ -699,9 +699,20 @@

A push subscription is a message delivery context established between the - user agent and the push service on behalf of a web application. Each - push subscription is associated with a service worker registration and a - service worker registration has at most one push subscription. + user agent and the push service on behalf of a web application. +

+

+ A [=push subscription=] has an associated scope, which is a [=/URL=]. +

+

+ A [=push subscription=] is considered to have a window-accessible scope when its [=push subscription/scope=]'s + [=url/path=] is a [=/list=] of [=list/size=] 1 and [=push subscription/scope=]'s + [=url/path=][0] is the empty string. +

+

+ I.e., the [=url/path=] component of the [=/URL=] serializes as "`/`".

A push subscription has an associated push endpoint. It MUST be the @@ -721,12 +732,9 @@

creating the push subscription.

- If the user agent has to change the keys for any reason, it MUST fire the - "`pushsubscriptionchange`" event with the service worker registration - associated with the push subscription as |registration|, a {{PushSubscription}} - instance representing the push subscription having the old keys as - |oldSubscription| and a {{PushSubscription}} instance representing the push - subscription having the new keys as |newSubscription|. + If the user agent has to change the keys of a [=push subscription=] for any reason + and the [=push subscription=]'s [=associated service worker registration=] is non-null, + it MUST [=refresh=] the [=push subscription=].

To create a push subscription, given an {{PushSubscriptionOptionsInit}} @@ -771,17 +779,41 @@

- Subscription Refreshes + Relationship to service worker registrations +

+

+ A [=push subscription=]'s associated service worker registration is the + [=service worker registration=] whose [=service worker registration/scope URL=] + [=URL/equals=] the [=push subscription=]'s [=push subscription/scope=], if any; + otherwise null. +

+

+ A [=push subscription=]'s [=associated service worker registration=] can only be null + when it has a [=push subscription/window-accessible scope=]. +

+

+ And vice versa, a [=service worker registration=]'s associated push + subscription is the [=push subscription=] whose [=push subscription/scope=] + [=URL/equals=] the [=service worker registration=]'s [=service worker + registration/scope URL=], if any; otherwise null. +

+
+
+

+ Subscription refreshes

A user agent or push service MAY choose to refresh a push - subscription at any time, for example because it has reached a certain age. + subscription whose [=associated service worker registration=] is non-null at any + time, for example because it has reached a certain age.

When this happens, the user agent MUST run the steps to create a push subscription given the PushSubscriptionOptions that were provided for - creating the current push subscription. The new push subscription MUST - have a key pair that's different from the original subscription. + creating the current push subscription, and set the new [=push subscription=]'s + [=push subscription/scope=] to the original subscription's [=push subscription/scope=]. + The new push subscription MUST have a key pair that's different from the + original subscription.

When successful, user agent then MUST fire the "`pushsubscriptionchange`" @@ -808,7 +840,7 @@

- Subscription Deactivation + Subscription deactivation

When a push subscription is deactivated, both @@ -817,13 +849,13 @@

delivered.

- A push subscription is deactivated when its associated service worker - registration is unregistered, though a push subscription MAY be - deactivated earlier. + A push subscription without a [=push subscription/window-accessible scope=] is + deactivated when its associated service worker registration is + unregistered, though a push subscription MAY be deactivated earlier.

- A push subscription is removed when service worker registration is - cleared. + A push subscription without a [=push subscription/window-accessible scope=] is + removed when service worker registration is cleared.

@@ -1049,23 +1081,37 @@

-

- Extensions to the `ServiceWorkerRegistration` Interface +

+ `PushManagerAttribute` mixin

- The Service Worker specification defines a {{ServiceWorkerRegistration}} interface - [[SERVICE-WORKERS]], which this specification extends. + This specifications extends the {{Window}} and {{ServiceWorkerRegistration}} objects + through the {{PushManagerAttribute}} mixin. [[HTML]] [[SERVICE-WORKERS]]

-
+      
         [SecureContext]
-        partial interface ServiceWorkerRegistration {
+        interface mixin PushManagerAttribute {
           readonly attribute PushManager pushManager;
         };
+        Window includes PushManagerAttribute;
+        ServiceWorkerRegistration includes PushManagerAttribute;
       

- The pushManager attribute exposes a {{PushManager}}, which has an associated - service worker registration represented by the {{ServiceWorkerRegistration}} on - which the attribute is exposed. + {{Window}} and {{ServiceWorkerRegistration}} objects have an associated {{PushManager}} + object. +

+

+ The pushManager getter steps are to return [=this=]'s associated {{PushManager}} + object. +

+

+ On a {{Window}} object the {{PushManager}}'s [=PushManager/service worker registration=] is + null. +

+

+ On a {{ServiceWorkerRegistration}} object the {{PushManager}}'s [=PushManager/service + worker registration=] is the service worker registration represented by the + {{ServiceWorkerRegistration}} object.

@@ -1085,6 +1131,10 @@

Promise<PermissionState> permissionState(optional PushSubscriptionOptionsInit options = {}); }; +

+ A {{PushManager}} has an associated service worker + registration which is null or a service worker registration. +

The supportedContentEncodings attribute exposes the sequence of supported content codings that can be used to encrypt the payload of a push message. A content @@ -1100,14 +1150,35 @@

`subscribe()` method

- The subscribe() method when invoked MUST run the following steps: + The subscribe() method steps are:

  1. Let |promise| be [=a new promise=].
  2. -
  3. Let |global| be [=this=]' [=relevant global object=]. +
  4. Let |global| be [=this=]'s [=relevant global object=]. +
  5. +
  6. Let |scope| be null. +
  7. +
  8. Let |registration| be null.
  9. -
  10. Return |promise| and continue [=in parallel=]. +
  11. If [=this=]'s [=PushManager/service worker registration=] is null: +
      +
    1. Set |scope| to the result of running the [=basic URL parser=] given "`/`" and + |global|'s associated Document's [=Document/URL=]. +
    2. + +
    +
  12. +
  13. Otherwise: +
      +
    1. [=/Assert=]: [=this=]'s [=PushManager/service worker registration=] is a [=service + worker registration=]. +
    2. +
    3. Set |registration| to [=this=]'s [=PushManager/service worker registration=]. +
    4. +
    +
  14. +
  15. Run these steps [=in parallel=]: -
  16. -
  17. If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} value - set to `false` and the user agent requires it to be `true`, [=queue a global task=] on the - [=networking task source=] using |global| to [=reject=] |promise| {{"NotAllowedError"}} - {{DOMException}} -
  18. -
  19. If the |options| argument does not include a non-null value for the - {{PushSubscriptionOptionsInit/applicationServerKey}} member, and the push service - requires one to be given, [=queue a global task=] on the [=networking task source=] using - |global| to [=reject=] |promise| with a {{"NotSupportedError"}} {{DOMException}}. -
  20. -
  21. If the |options| argument includes a non-null value for the - {{PushSubscriptionOptions/applicationServerKey}} attribute, run the following sub-steps:
      -
    1. If |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}} is a - {{DOMString}}, set its value to an {{ArrayBuffer}} containing the sequence of octets - that result from decoding |options|'s - {{PushSubscriptionOptionsInit/applicationServerKey}} using the base64url encoding - [[RFC7515]]. +
    2. If |scope| is failure or is a [=/URL=] whose [=url/scheme=] is not "`https`", then + [=queue a global task=] on the [=networking task source=] using |global| to [=reject=] + |promise| with a {{"NotAllowedError"}} {{DOMException}}.
    3. -
    4. If decoding fails, [=queue a global task=] on the [=networking task source=] using - |global| to [=reject=] |promise| with an {{"InvalidCharacterError"}} {{DOMException}} - and terminate these steps. +
    5. If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} + value set to `false` and the user agent requires it to be `true`, [=queue a global + task=] on the [=networking task source=] using |global| to [=reject=] |promise| with a + {{"NotAllowedError"}} {{DOMException}}.
    6. -
    7. Ensure that |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}} - describes a valid point on the P-256 curve. If its value is invalid, [=queue a global - task=] on the [=networking task source=] using |global| to [=reject=] |promise| with an - {{"InvalidAccessError"}} {{DOMException}} and terminate these steps. +
    8. If the |options| argument does not include a non-null value for the + {{PushSubscriptionOptionsInit/applicationServerKey}} member, and the push + service requires one to be given, [=queue a global task=] on the [=networking task + source=] using |global| to [=reject=] |promise| with a {{"NotSupportedError"}} + {{DOMException}}.
    9. -
    -
  22. -
  23. Let |registration:ServiceWorkerRegistration| be [=this=]'s associated service worker - registration. -
  24. -
  25. If |registration|'s [=service worker registration/active worker=] is null, [=queue a - global task=] on the [=networking task source=] using |global| to [=reject=] |promise| with - an {{"InvalidStateError"}} {{DOMException}} and terminate these steps. -
  26. -
  27. Let |permission| be [=request permission to use=] "push". -
  28. -
  29. If |permission| is {{PermissionState/"denied"}}, [=queue a global task=] on the [=user - interaction task source=] using |global| to [=reject=] |promise| with a - {{"NotAllowedError"}} {{DOMException}} and terminate these steps. -
  30. -
  31. If |registration| has a push subscription: -
      -
    1. Let |subscription| be the result of obtaining |registration|'s push - subscription. If there is an error, [=queue a global task=] on the [=networking - task source=] using |global| to [=reject=] |promise| with an {{"AbortError"}} - {{DOMException}} and terminate these steps. +
    2. If the |options| argument includes a non-null value for the + {{PushSubscriptionOptions/applicationServerKey}} attribute: +
        +
      1. If |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}} is a + {{DOMString}}, set its value to an {{ArrayBuffer}} containing the sequence of + octets that result from decoding |options|'s + {{PushSubscriptionOptionsInit/applicationServerKey}} using the base64url encoding + [[RFC7515]]. +
      2. +
      3. If decoding fails, [=queue a global task=] on the [=networking task source=] + using |global| to [=reject=] |promise| with an {{"InvalidCharacterError"}} + {{DOMException}} and terminate these steps. +
      4. +
      5. Ensure that |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}} + describes a valid point on the P-256 curve. If its value is invalid, [=queue a + global task=] on the [=networking task source=] using |global| to [=reject=] + |promise| with an {{"InvalidAccessError"}} {{DOMException}} and terminate these + steps. +
      6. +
      +
    3. +
    4. Let |subscription| be null. +
    5. +
    6. If |scope| is non-null: +
        +
      1. If there is a [=push subscription=] with a [=push + subscription/window-accessible scope=] whose [=push subscription/scope=] + [=URL/equals=] |scope|, then set |subscription| to that [=push subscription=]. +
      2. +
      +
    7. +
    8. Otherwise: +
        +
      1. [=/Assert=]: |registration| is non-null. +
      2. +
      3. If |registration|'s [=service worker registration/active worker=] is null, + [=queue a global task=] on the [=networking task source=] using |global| to + [=reject=] |promise| with an {{"InvalidStateError"}} {{DOMException}} and terminate + these steps. +
      4. +
      5. If |registration|'s [=associated push subscription=] is non-null, then set + |subscription| to |registration|'s [=associated push subscription=]. +
      6. +
      7. Set |scope| to |registration|'s [=service worker registration/scope URL=]. +
      8. +
      +
    9. +
    10. Let |permission| be [=request permission to use=] "push". +
    11. +
    12. If |permission| is {{PermissionState/"denied"}}, [=queue a global task=] on the + [=user interaction task source=] using |global| to [=reject=] |promise| with a + {{"NotAllowedError"}} {{DOMException}} and terminate these steps. +
    13. +
    14. If |subscription| is non-null: +
        +
      1. If there is an error with |subscription|, then [=queue a global task=] on the + [=networking task source=] using |global| to [=reject=] |promise| with an + {{"AbortError"}} {{DOMException}} and terminate these steps. +
      2. +
      3. Compare the |options| argument with the `options` attribute of |subscription|. + The contents of {{BufferSource}} values are compared for equality rather than + [=ECMAScript/reference record|reference=]. +
      4. +
      5. If any attribute on |options| contains a different value to that stored for + |subscription|, then [=queue a global task=] on the [=networking task source=] + using |global| to [=reject=] |promise| with an {{"InvalidStateError"}} + {{DOMException}} and terminate these steps. +
      6. +
      7. [=Queue a global task=] on the [=networking task source=] using |global| to + [=resolve=] |promise| with |subscription| and terminate these steps. +
      8. +
      +
    15. +
    16. [=/Assert=]: |subscription| is null and |scope| is a [=/URL=].
    17. -
    18. Compare the |options| argument with the `options` attribute of |subscription|. The - contents of {{BufferSource}} values are compared for equality rather than - [=ECMAScript/reference record|reference=]. +
    19. Set |subscription| to the result of trying to [=create a push subscription=] with + |options|. If creating the subscription [=exception/throws=] an [=exception=], [=queue + a global task=] on the [=networking task source=] using |global| to [=reject=] + |promise| with a that [=exception=] and terminate these steps.
    20. -
    21. If any attribute on |options| contains a different value to that stored for - |subscription|, then [=queue a global task=] on the [=networking task source=] using - |global| to [=reject=] |promise| with an {{"InvalidStateError"}} {{DOMException}} and - terminate these steps. +
    22. Set |subscription|'s [=push subscription/scope=] to |scope|.
    23. -
    24. When the request has been completed, [=queue a global task=] on the [=networking - task source=] using |global| to [=resolve=] |promise| with |subscription| and terminate - these steps. +
    25. [=Queue a global task=] on the [=networking task source=] using |global| to + [=resolve=] |promise| with a {{PushSubscription}} corresponding to |subscription|.
  32. -
  33. Let |subscription| be the result of trying to [=create a push subscription=] with - |options|. If creating the subscription [=exception/throws=] an [=exception=], [=queue a - global task=] on the [=networking task source=] using |global| to [=reject=] |promise| with - a that [=exception=] and terminate these these steps. -
  34. -
  35. Otherwise, [=queue a global task=] on the [=networking task source=] using |global| to - [=resolve=] |promise| with a {{PushSubscription}} providing the details of the new - |subscription|. +
  36. Return |promise|.

- The getSubscription method when invoked MUST run the - following steps: + The getSubscription() method steps are:

  1. Let |promise| be a new promise.
  2. -
  3. Return |promise| and continue the following steps asynchronously. +
  4. Let |global| be [=this=]'s [=relevant global object=].
  5. -
  6. If the Service Worker is not subscribed, resolve |promise| with null. +
  7. Let |windowScope| be null.
  8. -
  9. Retrieve the push subscription associated with the Service Worker. +
  10. Let |registration| be null.
  11. -
  12. If there is an error, reject |promise| with a {{DOMException}} whose name is - {{"AbortError"}} and terminate these steps. +
  13. If [=this=]'s [=PushManager/service worker registration=] is null, then set + |windowScope| to the result of running the [=basic URL parser=] given "`/`" and [=this=]'s + associated Document's [=Document/URL=].
  14. -
  15. When the request has been completed, resolve |promise| with a {{PushSubscription}} - providing the details of the retrieved push subscription. +
  16. Otherwise: +
      +
    1. [=/Assert=]: [=this=]'s [=PushManager/service worker registration=] is a [=service + worker registration=]. +
    2. +
    3. Set |registration| to [=this=]'s [=PushManager/service worker registration=]. +
    4. +
    +
  17. +
  18. Run these steps [=/in parallel=]: +
      +
    1. Let |subscription| be null. +
    2. +
    3. If |windowScope| is non-null: +
        +
      1. If there is a [=push subscription=] whose [=push subscription/scope=] is + |windowScope|, then set |subscription| to that [=push subscription=]. +
      2. +
      +
    4. +
    5. Otherwise: +
        +
      1. [=/Assert=]: |registration| is non-null. +
      2. +
      3. If |registration|'s [=associated push subscription=] is non-null, then set + |subscription| to |registration|'s [=associated push subscription=]. +
      4. +
      +
    6. +
    7. If |subscription| is null, then resolve |promise| with null. +
    8. +
    9. If there is an error with |subscription|, reject |promise| with a {{DOMException}} + whose name is {{"AbortError"}} and terminate these steps. +
    10. +
    11. Resolve |promise| with a {{PushSubscription}} corresponding to |subscription|. +
    12. +
    +
  19. +
  20. Return |promise|.

@@ -1639,20 +1777,34 @@

it MUST run the following steps.

    -
  1. Let |registration| be the service worker registration corresponding to the - push message. -
  2. -
  3. If |registration| is not found, abort these steps. +
  4. +

    + Let |subscription| be the active push subscription corresponding to the + push message. +

  5. -
  6. Let |subscription| be the active push subscription for |registration|. +
  7. +

    + Let |registration| be |subscription|'s [=push subscription/associated service worker + registration=]. +

  8. -
  9. Let |bytes| be null. +
  10. +

    + Let |bytes| be null. +

  11. -
  12. If the push message contains a payload: +
  13. +

    + If the push message contains a payload: +

      -
    1. 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=]. +
    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. @@ -1674,7 +1826,7 @@

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

      2. @@ -1718,7 +1870,7 @@

      3. If |declarativeResult|'s [=declarative push message parser result/mutable=] - is true: + is true and |registration| is non-null:

        1. @@ -1752,6 +1904,11 @@

      4. +
      5. +

        + If |registration| is null, then abort these steps. +

        +
      6. Let |data| be a new {{PushMessageData}} object whose [=PushMessageData/bytes=] is