From 2b432f7be5916e840428f1d42dc03b8073df3a70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcos=20C=C3=A1ceres?=
Date: Thu, 2 Jun 2022 12:30:19 +1000
Subject: [PATCH 1/5] Rewrite .subscribe() to fix various bugs
* Fix `PushSubscriptionOptionsInit` usage.
* Do less work in parallel.
* Handle `userVisibleOnly` member.
* Resolve promises using networking tasks source.
---
index.html | 108 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 65 insertions(+), 43 deletions(-)
diff --git a/index.html b/index.html
index 275be05..b7a856c 100644
--- a/index.html
+++ b/index.html
@@ -195,13 +195,16 @@
subscription having the new keys as |newSubscription|.
- To create a push subscription, given an PushSubscriptionOptions object
- of |options|, the user agent must run the following steps:
+ To create a push subscription, given an {{PushSubscriptionOptionsInit}}
+ |optionsDictionary:PushSubscriptionOptionsInit|:
Set the `options` attribute of |subscription| to be a copy of |options|.
+
Let |options:PushSubscriptionOptions| be a newly created {{PushSubscriptionOptions}}
+ object, initializing its attributes with the corresponding values of |optionsDictionary|.
+
+
Set |subscription|'s {{PushSubscription/options}} attribute to |options|.
Generate a new P-256 ECDH key pair [[ANSI-X9-62]]. Store the private key in an
internal slot on |subscription|; this value MUST NOT be made available to applications.
@@ -548,73 +551,92 @@
MAY support content codings defined in previous versions of the draft for compatibility
reasons.
+
+ `subscribe()` method
+
- The subscribe method when invoked MUST run the following steps:
+ The subscribe() method when invoked MUST run the following steps:
If the user agent requires |options|'s {{PushSubscriptionOptionsInit/userVisibleOnly}}
+ member to be `true`, return [=a promise rejected with=] an {{"NotAllowedError"}}
+ {{DOMException}}.
-
Return |promise| and continue the following steps asynchronously.
+
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, return [=a promise rejected with=] a {{"NotSupportedError"}}
+ {{DOMException}}.
If the |options| argument includes a non-null value for the
{{PushSubscriptionOptions/applicationServerKey}} attribute, run the following sub-steps:
-
If the |applicationServerKey| is provided as a {{DOMString}}, set its value to an
- {{ArrayBuffer}} containing the sequence of octets that result from decoding
- |applicationServerKey| using the base64url encoding [[RFC7515]]. If decoding fails,
- reject promise with a {{DOMException}} whose name is {{"InvalidCharacterError"}} and
- terminate these steps.
+
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]].
+
+
If decoding fails, return [=a promise rejected with=] a {{"InvalidCharacterError"}}
+ {{DOMException}}.
-
Ensure that |applicationServerKey| describes a valid point on the P-256 curve. If
- the |applicationServerKey| value is invalid, reject |promise| with a {{DOMException}}
- whose name is {{"InvalidAccessError"}} and terminate these steps.
+
Ensure that |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}}
+ describes a valid point on the P-256 curve. If its value is invalid, return [=a promise
+ rejected with=] an {{"InvalidAccessError"}} {{DOMException}}.
-
If the |options| argument does not include a non-null value for the
- {{PushSubscriptionOptions/applicationServerKey}} attribute, and the push service
- requires one to be given, reject |promise| with a {{DOMException}} whose name is
- {{"NotSupportedError"}} and terminate these steps.
+
Let |registration:ServiceWorkerRegistration| be the {{PushManager}}'s associated
+ service worker registration.
+
+
If |registration|'s [=service worker registration/active worker=] is null, return [=a
+ promise rejected with=] an {{"InvalidStateError"}} {{DOMException}}.
+
+
Let |sw| be |registration|'s [=service worker registration/active worker=].
+
Let |global:Window| be [=this=]'s [=relevant global object=].
-
If |registration|'s [=service worker registration/active worker=] is null, reject
- |promise| with a {{DOMException}} whose name is {{"InvalidStateError"}} and terminate these
- steps.
+
Return |promise| and continue [=in parallel=].
Let |permission| be [=request permission to use=] "push".
-
If |permission| is "denied", reject |promise| with a {{DOMException}} whose name is
- {{"NotAllowedError"}} and terminate these steps.
+
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.
-
If the Service Worker is already subscribed, run the following substeps:
+
If |sw| is already subscribed, run the following substeps:
-
If there is an error, reject |promise| with a {{DOMException}} whose name is
- {{"AbortError"}} and terminate these steps.
+
If |subscription| 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.
-
Let |subscription| be the retrieved subscription.
+
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=].
-
Compare the |options| argument with the `options` attribute of |subscription|. If
- any attribute on |options| contains a different value to that stored for
- |subscription|, then reject |promise| with an {{InvalidStateError}} and terminate these
- steps. The contents of {{BufferSource}} values are compared for equality rather than
- references.
+
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.
-
When the request has been completed, resolve |promise| with |subscription|.
+
When the request has been completed, [=queue a global task=] on the [=networking
+ task source=] using |global| to [=resolve=] |promise| with |subscription| and terminate
+ this algorithm.
Let |subscription| be the result of running the [=create a push subscription=] steps
given |options|.
-
If there is an error, reject |promise| with a {{DOMException}} whose name is
- {{"AbortError"}} and terminate these steps.
+
If |subscription| is an error, [=queue a global task=] on the [=networking task
+ source=] using |global| to [=reject=] |promise| with a {{"AbortError"}} {{DOMException}}
+ and terminate these steps.
-
Resolve |promise| with a {{PushSubscription}} providing the details of the new
+
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|.
The subscribe() method when invoked MUST run the following steps:
-
If the user agent requires |options|'s {{PushSubscriptionOptionsInit/userVisibleOnly}}
- member to be `true`, return [=a promise rejected with=] an {{"NotAllowedError"}}
+
If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} value set to `false` and
+ the user agent requires it to be `true`, return [=a promise rejected with=] an {{"NotAllowedError"}}
{{DOMException}}.
If the |options| argument does not include a non-null value for the
From a8018439d848985e0064d56a673781df2d564239 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcos=20C=C3=A1ceres?=
Date: Tue, 21 Jun 2022 17:34:30 +1000
Subject: [PATCH 3/5] Review feedback
---
index.html | 87 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 50 insertions(+), 37 deletions(-)
diff --git a/index.html b/index.html
index c93ff59..895da10 100644
--- a/index.html
+++ b/index.html
@@ -199,10 +199,11 @@
Let |subscription:PushSubscription| be a new {{PushSubscription}}.
Let |options:PushSubscriptionOptions| be a newly created {{PushSubscriptionOptions}}
- object, initializing its attributes with the corresponding values of |optionsDictionary|.
+ object, initializing its attributes with the corresponding members and values of
+ |optionsDictionary|.
Set |subscription|'s {{PushSubscription/options}} attribute to |options|.
@@ -217,11 +218,21 @@
key can be retrieved by calling the {{PushSubscription/getKey()}} method of the
{{PushSubscription}} with an argument of {{PushEncryptionKeyName/"auth"}}.
-
Make a request to the push service to create a new push subscription.
- Include the {{PushSubscriptionOptions/applicationServerKey}} attribute of |options| when
- it has been set.
+
Request a new push subscription. Include the
+ {{PushSubscriptionOptions/applicationServerKey}} attribute of |options| when it has been
+ set. Rethrow any [=exceptions=].
-
When the request has completed, return |subscription|.
+
Set |subscription|'s {{PushSubscription/endpoint}} attribute to the [=URL=]
+ provided by the push subscription.
+
+
If provided by the push subscription, set |subscription|'s
+ {{PushSubscription/expirationTime}}.
+
+
+
+
Return |subscription|.
@@ -558,15 +569,21 @@
The subscribe() method when invoked MUST run the following steps:
-
If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} value set to `false` and
- the user agent requires it to be `true`, return [=a promise rejected with=] an {{"NotAllowedError"}}
- {{DOMException}}.
+
If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} value
+ set to `false` and the user agent requires it to be `true`, return [=a promise rejected
+ with=] an {{"NotAllowedError"}} {{DOMException}}.
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, return [=a promise rejected with=] a {{"NotSupportedError"}}
{{DOMException}}.
+
Let |promise| be [=a new promise=].
+
+
Let |global:Window| be [=this=]' [=relevant global object=].
+
+
Return |promise| and continue [=in parallel=].
+
If the |options| argument includes a non-null value for the
{{PushSubscriptionOptions/applicationServerKey}} attribute, run the following sub-steps:
@@ -576,42 +593,40 @@
{{PushSubscriptionOptionsInit/applicationServerKey}} using the base64url encoding
[[RFC7515]].
-
If decoding fails, return [=a promise rejected with=] a {{"InvalidCharacterError"}}
- {{DOMException}}.
+
If decoding fails, [=queue a global task=] on the [=user interaction task source=]
+ using |global| to [=reject=] |promise| with an {{"InvalidCharacterError"}}
+ {{DOMException}} and terminate these steps.
Ensure that |options|'s {{PushSubscriptionOptionsInit/applicationServerKey}}
- describes a valid point on the P-256 curve. If its value is invalid, return [=a promise
- rejected with=] an {{"InvalidAccessError"}} {{DOMException}}.
+ describes a valid point on the P-256 curve. If its value is invalid, [=queue a global
+ task=] on the [=user interaction task source=] using |global| to [=reject=] |promise|
+ with an {{"InvalidAccessError"}} {{DOMException}} and terminate these steps.
-
Let |registration:ServiceWorkerRegistration| be the {{PushManager}}'s associated
- service worker registration.
+
If |registration|'s [=service worker registration/active worker=] is null, return [=a
- promise rejected with=] an {{"InvalidStateError"}} {{DOMException}}.
+
If |registration|'s [=service worker registration/active worker=] is null, [=queue a
+ global task=] on the [=user interaction task source=] using |global| to [=reject=]
+ |promise| with an {{"InvalidStateError"}} {{DOMException}} and terminate these steps.
Let |sw| be |registration|'s [=service worker registration/active worker=].
-
Let |promise| be [=a new promise=].
-
-
Let |global:Window| be [=this=]'s [=relevant global object=].
-
-
Return |promise| and continue [=in parallel=].
-
Let |permission| be [=request permission to use=] "push".
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.
+ interaction task source=] using |global| to [=reject=] |promise| with a
+ {{"NotAllowedError"}} {{DOMException}} and terminate these steps.
-
If |sw| is already subscribed, run the following substeps:
+
If |sw| is already subscribed, run the following sub-steps:
-
Try to retrieve the push subscription associated with the |sw|. 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.
-
If |subscription| 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.
+
Compare the |options| argument with the `options` attribute of |subscription|. The
contents of {{BufferSource}} values are compared for equality rather than
@@ -624,16 +639,14 @@
When the request has been completed, [=queue a global task=] on the [=networking
task source=] using |global| to [=resolve=] |promise| with |subscription| and terminate
- this algorithm.
+ these steps.
-
Let |subscription| be the result of running the [=create a push subscription=] steps
- given |options|.
-
-
If |subscription| is an error, [=queue a global task=] on the [=networking task
- source=] using |global| to [=reject=] |promise| with a {{"AbortError"}} {{DOMException}}
- and terminate these steps.
+
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.
Otherwise, [=queue a global task=] on the [=networking task source=] using |global| to
[=resolve=] |promise| with a {{PushSubscription}} providing the details of the new
From 1e439f497d11767dcfd4d0c03544c6a0380b6e96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcos=20C=C3=A1ceres?=
Date: Tue, 28 Jun 2022 17:20:12 +1000
Subject: [PATCH 4/5] remove type
---
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.html b/index.html
index 895da10..6fb97b7 100644
--- a/index.html
+++ b/index.html
@@ -580,7 +580,7 @@
Let |promise| be [=a new promise=].
-
Let |global:Window| be [=this=]' [=relevant global object=].
+
Let |global| be [=this=]' [=relevant global object=].
The subscribe() method when invoked MUST run the following steps:
-
If the |options| argument has a {{PushSubscriptionOptionsInit/userVisibleOnly}} value
- set to `false` and the user agent requires it to be `true`, return [=a promise rejected
- with=] an {{"NotAllowedError"}} {{DOMException}}.
-
-
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, return [=a promise rejected with=] a {{"NotSupportedError"}}
- {{DOMException}}.
-
Let |promise| be [=a new promise=].
Let |global| be [=this=]' [=relevant global object=].
Return |promise| and continue [=in parallel=].
+
+
+
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}}
+
+
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}}.
If the |options| argument includes a non-null value for the
{{PushSubscriptionOptions/applicationServerKey}} attribute, run the following sub-steps:
@@ -593,14 +609,14 @@
{{PushSubscriptionOptionsInit/applicationServerKey}} using the base64url encoding
[[RFC7515]].
-
If decoding fails, [=queue a global task=] on the [=user interaction task source=]
- using |global| to [=reject=] |promise| with an {{"InvalidCharacterError"}}
- {{DOMException}} and terminate these steps.
+
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.
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 [=user interaction task source=] using |global| to [=reject=] |promise|
- with an {{"InvalidAccessError"}} {{DOMException}} and terminate these steps.
+ task=] on the [=networking task source=] using |global| to [=reject=] |promise| with an
+ {{"InvalidAccessError"}} {{DOMException}} and terminate these steps.
@@ -608,8 +624,8 @@
registration.
If |registration|'s [=service worker registration/active worker=] is null, [=queue a
- global task=] on the [=user interaction task source=] using |global| to [=reject=]
- |promise| with an {{"InvalidStateError"}} {{DOMException}} and terminate these steps.
+ global task=] on the [=networking task source=] using |global| to [=reject=] |promise| with
+ an {{"InvalidStateError"}} {{DOMException}} and terminate these steps.
Let |sw| be |registration|'s [=service worker registration/active worker=].