diff --git a/spec.bs b/spec.bs index eff0b70..0994fcf 100644 --- a/spec.bs +++ b/spec.bs @@ -1415,6 +1415,50 @@ The Shared Storage API will integrate into the [=Storage Model|Storage API=] as 1. Return the result of running [=shared storage database/store an entry in the database=] with |queue|, |databaseMap|, |environment|, |key|, and |value|. +
+ + To batch update entries in the database, given a [=shared storage database/shared storage database queue=] |queue|, a [=storage proxy map=] |databaseMap|, an [=environment settings object=] |environment|, and a [=list=] of {{SharedStorageModifierMethod}} |methods|, run the following steps on |queue|: + + 1. Let |originalDatabaseMap| be |databaseMap|. + + 1. Let |innerMethodFailed| be false. + 1. For each |method| in |methods|: + 1. If |method| is a {{SharedStorageSetMethod}}: + 1. Let |key| be |method|'s [=SharedStorageSetMethod/key=]. + 1. Let |value| be |method|'s [=SharedStorageSetMethod/value=]. + 1. Let |ignoreIfPresent| be |method|'s [=SharedStorageSetMethod/ignore if present=]. + 1. Let |result| be the result of running [=shared storage database/set an entry in the database=] with |queue|, |databaseMap|, |environment|, |key|, |value|, and |ignoreIfPresent|. + 1. If |result| is false: + 1. Set |innerMethodFailed| to true. + 1. Break. + 1. Else if |method| is a {{SharedStorageAppendMethod}}: + 1. Let |key| be |method|'s [=SharedStorageAppendMethod/key=]. + 1. Let |value| be |method|'s [=SharedStorageAppendMethod/value=]. + 1. Let |result| be the result of running [=shared storage database/append an entry in the database=] with |queue|, |databaseMap|, |environment|, |key|, and |value|. + 1. If |result| is false: + 1. Set |innerMethodFailed| to true. + 1. Break. + 1. Else if |method| is a {{SharedStorageDeleteMethod}}: + 1. Let |key| be |method|'s [=SharedStorageDeleteMethod/key=]. + 1. Let |result| be the result of running [=shared storage database/delete an entry from the database=] with |queue|, |databaseMap|, |environment|, and |key|. + 1. If |result| is false: + 1. Set |innerMethodFailed| to true. + 1. Break. + 1. Else: + 1. [=Assert=]: |method| is a {{SharedStorageClearMethod}}. + 1. Let |result| be the result of running [=shared storage database/clear all entries in the database=] with |queue|, |databaseMap|, and |environment|. + 1. If |result| is false: + 1. Set |innerMethodFailed| to true. + 1. Break. + 1. If |innerMethodFailed|: + 1. Set |databaseMap| to |originalDatabaseMap|. + 1. Return false. + 1. Return true. +
+ +
+ This algorithm uses a naive rollback mechanism. For production environments, consider more efficient techniques that avoid full database copies. +
Extension to the {{Window}} interface {#window-extension} ===================================================== @@ -1712,57 +1756,26 @@ Note: The [=determine if a navigable has fully revoked network=] algorithm ensur 1. If the result of running [=SharedStorageWorkletGlobalScope/check whether addModule is finished=] for {{SharedStorage}}'s associated {{SharedStorageWorkletGlobalScope}} is false, return a [=promise rejected=] with a {{TypeError}}. 1. If |context| is null, return a [=promise rejected=] with a {{TypeError}}. 1. If |context|'s [=active window=]'s [=associated document=] is not [=fully active=], return a [=promise rejected=] with a {{TypeError}}. + 1. For each |method| in |methods|: + 1. If |method|["{{SharedStorageModifierMethodOptions/withLock}}"] [=map/exists=]: + 1. Return a [=promise rejected=] with a {{TypeError}}. + + Note: `batchUpdate()` executes as a transactional operation. To avoid potential deadlocks from finer-grained locking, inner methods within `batchUpdate()` cannot utilize the `withLock` option. Instead of ignoring this option, an error is thrown to enforce the restriction and prevent misuse. 1. Let |environment| be |context|'s [=active window=]'s [=relevant settings object=]. 1. Let |databaseMap| be the result of running [=obtain a shared storage bottle map=] given |environment| and |environment|'s [=environment settings object/origin=]. 1. If |databaseMap| is failure, then return a [=promise rejected=] with a {{TypeError}}. - 1. Let |unfinishedUpdatesCount| be |methods|'s [=list/size=]. - 1. Let |hasFailure| be false. 1. Let |onLockGrantedCallback| be an algorithm to perform the following steps: - 1. For each |method| in |methods|: - 1. Let |methodResultPromise| be a new [=promise=]. - 1. If |method| is a {{SharedStorageSetMethod}}: - 1. Let |key| be |method|'s [=SharedStorageSetMethod/key=]. - 1. Let |value| be |method|'s [=SharedStorageSetMethod/value=]. - 1. Let |methodOptions| be a new {{SharedStorageSetMethodOptions}}. - 1. Set |methodOptions|["{{SharedStorageSetMethodOptions/ignoreIfPresent}}"] to |method|'s [=SharedStorageSetMethod/ignore if present=]. - 1. If |method|'s [=SharedStorageModifierMethod/with lock=] is not null, set |methodOptions|["{{SharedStorageModifierMethodOptions/withLock}}"] to |method|'s [=SharedStorageModifierMethod/with lock=]. - 1. Set |methodResultPromise| to the result of invoking {{SharedStorage/set()|set}}(|key|, |value|, |methodOptions|). - 1. Else if |method| is a {{SharedStorageAppendMethod}}: - 1. Let |key| be |method|'s [=SharedStorageAppendMethod/key=]. - 1. Let |value| be |method|'s [=SharedStorageAppendMethod/value=]. - 1. Let |methodOptions| be a new {{SharedStorageModifierMethodOptions}}. - 1. If |method|'s [=SharedStorageModifierMethod/with lock=] is not null, set |methodOptions|["{{SharedStorageModifierMethodOptions/withLock}}"] to |method|'s [=SharedStorageModifierMethod/with lock=]. - 1. Set |methodResultPromise| to the result of invoking {{SharedStorage/append()|append}}(|key|, |value|, |methodOptions|). - 1. Else if |method| is a {{SharedStorageDeleteMethod}}: - 1. Let |key| be |method|'s [=SharedStorageDeleteMethod/key=]. - 1. Let |methodOptions| be a new {{SharedStorageModifierMethodOptions}}. - 1. If |method|'s [=SharedStorageModifierMethod/with lock=] is not null, set |methodOptions|["{{SharedStorageModifierMethodOptions/withLock}}"] to |method|'s [=SharedStorageModifierMethod/with lock=]. - 1. Set |methodResultPromise| to the result of invoking {{SharedStorage/delete()|delete}}(|key|, |methodOptions|). - 1. Else: - 1. [=Assert=]: |method| is a {{SharedStorageClearMethod}}. - 1. Let |methodOptions| be a new {{SharedStorageModifierMethodOptions}}. - 1. If |method|'s [=SharedStorageModifierMethod/with lock=] is not null, set |methodOptions|["{{SharedStorageModifierMethodOptions/withLock}}"] to |method|'s [=SharedStorageModifierMethod/with lock=]. - 1. Set |methodResultPromise| to the result of invoking {{SharedStorage/clear()|clear}}(|methodOptions|). - 1. [=Upon fulfillment=] of |methodResultPromise|: - 1. Decrement |unfinishedUpdatesCount| by 1. - 1. If |unfinishedUpdatesCount| is 0, run [=finish a batch update=] given |promise| and |hasFailure|. - 1. [=Upon rejection=] of |methodResultPromise|: - 1. Decrement |unfinishedUpdatesCount| by 1. - 1. Set |hasFailure| to true. - 1. If |unfinishedUpdatesCount| is 0, run [=finish a batch update=] given |promise| and |hasFailure|. - 1. If |unfinishedUpdatesCount| is 0, run [=finish a batch update=] given |promise| and |hasFailure|. + 1. [=Enqueue the following steps=] on |queue|: + 1. Let |result| be the result of running [=shared storage database/batch update entries in the database=] with |queue|, |databaseMap|, |environment|, and |methods|. + 1. If |result| is false and if |globalObject| is a {{SharedStorageWorkletGlobalScope}}: + 1. [=Queue a global task=] on the [=DOM manipulation task source=], given |realm|'s [=global object=], to [=reject=] |promise| with a {{TypeError}}. + 1. Abort these steps. + 1. [=Queue a global task=] on the [=DOM manipulation task source=], given |realm|'s [=global object=], to [=resolve=] |promise| with undefined. 1. If |options|["{{SharedStorageModifierMethodOptions/withLock}}"] [=map/exists=], run [=handle callback within a shared storage lock=] given |environment|'s [=environment settings object/origin=], |options|["{{SharedStorageModifierMethodOptions/withLock}}"], |onLockGrantedCallback|. 1. Else, run |onLockGrantedCallback|. 1. Return |promise|. -
- To finish a batch update, given a [=promise=] |promise| and a [=/boolean=] |hasFailure|, perform the following steps: - - 1. If |hasFailure| is true, [=reject=] |promise| with a {{TypeError}}. - 1. Else, [=resolve=] |promise| with undefined. -
- ## Setter/Deleter Methods ## {#setter}