Web ロックス API

W3C 初回公開作業草案

この文書の詳細情報
このバージョン:
https://www.w3.org/TR/2023/WD-web-locks-20230105/
最新公表バージョン:
https://www.w3.org/TR/web-locks/
編集者草案:
https://w3c.github.io/web-locks/
履歴:
https://www.w3.org/standards/history/web-locks
テストスイート:
https://github.com/web-platform-tests/wpt/tree/master/web-locks
フィードバック:
GitHub
仕様内インライン
編集者:
(Google Inc.)
(Mozilla)

概要

本書は、スクリプトがリソースに対して非同期的にロックを取得し、作業を行っている間ロックを保持し、その後解放することを可能にするウェブプラットフォームAPIを定義しています。ロックが保持されている間、同一オリジン内の他のスクリプトは同じリソースに対してロックを取得できません。これにより、ウェブアプリケーション内のコンテキスト(ウィンドウやワーカー)がリソースの利用を協調することができます。

この文書のステータス

このセクションは、本書の公開時点における文書のステータスについて説明しています。現在のW3C 公開物の一覧と、最新の技術レポート改訂版は W3C 技術レポート索引(https://www.w3.org/TR/)でご覧いただけます。

本書は Web Applications Working Group により作業草案として公開されました。本書は W3C 勧告となることを意図しています。

本書は Web Applications Working Group により、勧告トラック を利用して初回公開作業草案として公開されました。 この仕様へのフィードバックやコメントは歓迎します。GitHub issues をご利用ください。過去の議論は public-webapps@w3.org アーカイブ にてご参照いただけます。

初回公開作業草案としての公開は、W3C およびそのメンバーによる承認を意味するものではありません。本書は草案であり、随時更新・置換・廃止される場合があります。本書を進行中の作業以外として引用することは不適切です。

本書は、W3C 特許ポリシーの下で活動するグループによって作成されました。W3C は、グループの成果物に関連して提出された 特許開示の公開リスト を管理しています。そのページには特許開示方法の説明も記載されています。特許に関する実際の知識を持つ個人は、必須クレームが含まれていると考える場合、W3C特許ポリシー第6節に従って情報を開示しなければなりません。

本書は 2021年11月2日 W3Cプロセス文書 に基づいて管理されています。

1. 導入

このセクションは規範的ではありません。

ロック要求は、スクリプトによって特定のリソース名モードに対して行われます。スケジューリングアルゴリズムは、現在および過去の要求の状態を見て、最終的にロック要求を付与します。ロックは付与された要求であり、リソース名モードを持ちます。これはスクリプトに返されるオブジェクトとして表現されます。ロックが保持されている限り(名前やモードによっては)他のロック要求が付与されない可能性があります。ロックはスクリプトによって解放することができ、解放されると他のロック要求が付与される場合があります。

このAPIは必要に応じて利用できるオプション機能を提供します:

協調的な調整は、エージェントストレージバケットを共有する範囲内で行われます。これは複数のエージェントクラスタにまたがる場合があります。

1.1. 利用概要

APIの利用方法は以下の通りです:

  1. ロックを要求します。

  2. 非同期タスクでロックを保持したまま作業を行います。

  3. タスクが完了するとロックは自動的に解放されます。

1.2. 動機づけとなるユースケース

ウェブベースのドキュメントエディタは、高速アクセスのために状態をメモリに保持し、変更を(レコードの一連として)Indexed Database APIなどのストレージAPIに永続化して耐障害性やオフライン利用を実現し、サーバーにも保存して複数デバイス間で利用できるようにします。同じドキュメントが2つのタブで編集のために開かれている場合、どちらか一方だけがドキュメントの変更や同期を行えるように、タブ間で作業を協調する必要があります。これには、どちらがアクティブに変更(およびストレージAPIとの状態同期)を行うかを調整し、アクティブなタブが離脱(ナビゲート、閉じる、クラッシュ)した際に他のタブがアクティブになれるよう把握する必要があります。

データ同期サービスでは、「プライマリタブ」が指定されます。このタブだけが特定の操作(ネットワーク同期、キュー済みデータのクリーンアップなど)を実行します。プライマリタブはロックを保持し続け、決して解放しません。他のタブはロックの取得を試み、その試みはキューされます。「プライマリタブ」がクラッシュしたり閉じられたりすると、他のタブのいずれかがロックを取得して新しいプライマリとなります。

Indexed Database APIは、オリジン内の複数の名前付きストレージパーティション間で共有読み取りと排他的書き込みアクセスを可能にするトランザクションモデルを定義しています。この概念をプリミティブとして公開すれば、リソースの利用可能性に基づいて任意のWebプラットフォームの活動をスケジューリングでき、例えば他のストレージ型(Cache [Service-Workers]など)やストレージ型をまたいだトランザクションの合成、さらには非ストレージAPI(例えばネットワークフェッチ)にも応用できます。

2. 概念

本仕様においては、以下の通りとします:

ユーザーエージェントは、ロックタスクキューを持ち、これは新しい並列キューの開始の結果です。

以下にエンキューされるタスクソースは、Web Locksタスクソースです。

2.1. リソース名

リソース名は、Webアプリケーションが抽象的なリソースを表すために選択するJavaScript文字列です。

リソース名はスケジューリングアルゴリズム以外には外部的な意味を持たず、agent が同じ storage bucket を共有する場合にグローバルです。Webアプリケーションは、任意のリソース命名規則を自由に使用できます。

U+002D HYPHEN-MINUS (-)で始まるリソース名は予約されており、これらを要求すると例外が発生します。

2.2. ロックマネージャ

ロックマネージャは、ロックロック要求の状態をカプセル化します。各ストレージバケットは、Web Locks API用に関連付けられたストレージボトルを通じて1つのロックマネージャを含みます。

注: 同じユーザーエージェント内で開かれたストレージバケットを共有するページやワーカー(エージェント)は、ロックマネージャを共有します。たとえそれらが無関係なブラウジングコンテキストであってもです。

ロックマネージャを取得するには、環境設定オブジェクト environmentを与えて、次の手順を実行します:
  1. mapを、environmentと"web-locks"を与えてローカルストレージボトルマップの取得を実行した結果とします。

  2. mapが失敗なら、失敗を返します。

  3. bottlemapの関連付けられたストレージボトルとします。

  4. bottleの関連付けられたロックマネージャを返します。

ここで[Storage]との統合方法を調整してください。与えられた環境からロックマネージャを正しく取得する方法も含みます。

2.3. モードとスケジューリング

モードは、"exclusive" または"shared"のいずれかです。モードは一般的な読取・書込ロックパターンをモデル化できます。"exclusive"ロックが保持されている場合、その名前の他のロックは付与されません。"shared"ロックが保持されている場合、その名前の他の"shared"ロックは付与できますが、"exclusive"ロックは付与できません。APIのデフォルトモードは"exclusive"です。

追加のプロパティ(タイムアウト、公平性など)がスケジューリングに影響する場合があります。

2.4. ロック

ロックは、共有リソースへの排他的アクセスを表します。

ロックは、agentエージェント)を持ちます。

ロックは、clientId(不透明な文字列)を持ちます。

ロックは、managerロックマネージャ)を持ちます。

ロックは、nameリソース名)を持ちます。

ロックは、mode("exclusive"または"shared")を持ちます。

ロックは、waiting promise(Promise)を持ちます。

ロックは、released promise(Promise)を持ちます。

ロックマネージャは、保持ロック集合順序付きセット)を持ち、それはロックの集合です。

ロック lockwaiting promiseが解決(fulfillまたはreject)されたとき、以下の手順をロックタスクキューにエンキューする

  1. ロックを解放する lock

  2. 解決する lockreleased promiseに、lockwaiting promiseを与える。

2.5. ロック要求

ロック要求は、 保留中のロックの要求を表します。

ロック要求は、 構造体であり、 項目 agentclientIdmanagernamemodecallbackpromisesignal を持ちます。

ロック要求キューは、 キューであり、 ロック要求のキューです。

ロックマネージャは、 ロック要求キュー・マップを持ちます。 これはマップであり、 リソース名から ロック要求キューへの対応を持ちます。

ロック要求キューを取得するには、 ロック要求キュー・マップ queueMapから リソース名 nameに対して、以下の手順を実行します:

  1. もしqueueMap[name]が存在しない場合、 設定し、queueMap[name]に新しい空の ロック要求キューを割り当てます。

  2. queueMap[name]を返します。

ロック要求 request付与可能かどうかは、以下の手順がtrueを返す場合です:

  1. managerrequestmanagerとします。

  2. queueMapmanagerロック要求キュー・マップとします。

  3. namerequestnameとします。

  4. queueを、ロック要求キューの取得queueMapnameに対して実行した結果とします。

  5. heldmanager保持ロック集合とします。

  6. moderequestmodeとします。

  7. もしqueue空でないかつ requestqueueの最初の項目でない場合、falseを返します。

  8. もしmodeが"exclusive"の場合、 held内のいずれのロックnamenameと等しくなければtrue、そうでなければfalseを返します。

  9. それ以外の場合、modeは"shared"です。 held内のいずれのロックmodeが"exclusive"であり、 かつnamenameと等しくなければtrue、そうでなければfalseを返します。

2.6. ロックの終了

ドキュメントアンロード時クリーンアップ手順documentに対して実行された場合、 その残りのロックと要求の終了を そのagentで実行します。

agentが終了した場合、 残りのロックと要求の終了を そのagentで実行します。

これは現在ワーカーのみを対象としており、ワーカー終了時に手順を実行する規範的な方法がないため定義が曖昧です。

残りのロックと要求の終了agentで実行するには、以下の手順をロックタスクキューにエンキューします:

  1. ロック要求 requestのうち、agentagentに等しいものについて:

    1. 要求の中止requestに対して実行します。

  2. ロック lockのうち、agentagentに等しいものについて:

    1. ロックの解放lockに対して実行します。

3. API

[SecureContext]
interface mixin NavigatorLocks {
  readonly attribute LockManager locks;
};
Navigator includes NavigatorLocks;
WorkerNavigator includes NavigatorLocks;

environment settings objectLockManagerオブジェクトを持ちます。

Navigator/locks

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

locksゲッターの手順は thisrelevant settings objectが持つ LockManagerオブジェクトを返します。

3.2. LockManager クラス

LockManager

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
[SecureContext, Exposed=(Window,Worker)]
interface LockManager {
  Promise<any> request(DOMString name,
                       LockGrantedCallback callback);
  Promise<any> request(DOMString name,
                       LockOptions options,
                       LockGrantedCallback callback);

  Promise<LockManagerSnapshot> query();
};

callback LockGrantedCallback = Promise<any> (Lock? lock);

enum LockMode { "shared", "exclusive" };

dictionary LockOptions {
  LockMode mode = "exclusive";
  boolean ifAvailable = false;
  boolean steal = false;
  AbortSignal signal;
};

dictionary LockManagerSnapshot {
  sequence<LockInfo> held;
  sequence<LockInfo> pending;
};

dictionary LockInfo {
  DOMString name;
  LockMode mode;
  DOMString clientId;
};

LockManagerのインスタンスは、スクリプトからロック要求を行ったり ロックマネージャの状態をクエリすることができます。

3.2.1. request() メソッド

LockManager/request

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
promise = navigator . locks . request(name, callback)
promise = navigator . locks . request(name, options, callback)

request() メソッドはロックを要求するために呼び出されます。

name(最初の引数)はリソース名文字列です。

callback(最後の引数)は、ロックが付与されたときに呼び出されるコールバック関数です。これはスクリプトで指定され、通常はasync関数です。ロックはコールバック関数が完了するまで保持されます。非asyncコールバック関数が渡された場合、即座にresolveされるPromiseでラップされるため、ロックは同期コールバックの間だけ保持されます。

返されるpromiseは、ロックが解放された後にコールバックの結果でresolve(またはreject)されるか、要求が中止された場合はrejectされます。

例:

try {
  const result = await navigator.locks.request('resource', async lock => {
    // この時点でロックが保持されています。
    await do_something();
    await do_something_else();
    return "ok";
    // ここでロックが解放されます。
  });
  // |result|にはコールバックの戻り値が入ります。
} catch (ex) {
  // コールバックがthrowされた場合、ここで捕捉されます。
}

コールバックがいかなる理由で終了してもロックは解放されます ― returnした場合もthrowした場合も同様です。

options辞書を第2引数として指定できます。callback引数は常に最後です。

options . mode

mode オプションは"exclusive" (指定しない場合のデフォルト)または"shared"のいずれかです。 複数のタブやワーカーが同じリソースに対し"shared"モードのロックを保持できますが、 "exclusive"モードでは1つのタブ/ワーカーだけがそのリソースのロックを保持できます。

この主な用途は、複数のリーダーが同時にリソースへアクセスできるようにする一方、変更を防ぐことです。 リーダーロックが解放された後、排他的ライターがロックを取得して変更し、その後また排他的ライターや共有リーダーが続くことができます。

await navigator.locks.request('resource', {mode: 'shared'}, async lock => {
  // ここでロックが保持されています。他のコンテキストも共有モードでロックを保持できるが、排他モードでは保持できません。
});
options . ifAvailable

ifAvailable オプションがtrueの場合、追加の待機なしでロックが付与可能な場合のみ取得します。これは依然として同期ではないことに注意してください。多くのユーザーエージェントではロックの可否確認にプロセス間通信が必要です。ロックが付与されない場合、コールバックにはnullが渡されます(これは想定内なので、要求はrejectされません)。

await navigator.locks.request('resource', {ifAvailable: true}, async lock => {
  if (!lock) {
    // 取得できなかった。必要に応じて対応する。
    return;
  }
  // この時点でロックが保持されています。
});
options . signal

signal オプションにはAbortSignalを設定できます。 これにより、例えば要求がタイムリーに付与されない場合など、ロック要求を中止できます:

const controller = new AbortController();
setTimeout(() => controller.abort(), 200); // 最大200ms待つ

try {
  await navigator.locks.request(
    'resource', {signal: controller.signal}, async lock => {
      // この時点でロックが保持されています。
  });
  // ロック解放後。
} catch (ex) {
  // |ex|が"AbortError"というエラー名のDOMExceptionであれば、タイマーが発火した場合です。
}

ロックが付与される前にabortが発生した場合、要求のPromiseはAbortErrorでrejectされます。ロックが付与された後はsignalは無視されます。

options . steal

steal オプションがtrueの場合、そのリソースの保持中のロックはすべて解放され(そのロックのreleased promiseAbortErrorで解決されます)、 この要求が付与され、キュー済み要求も先取りされます。

Webアプリケーションが回復不能な状態、例えばService Workerなどがロックを保持するタブに応答がないことを検出した場合、このオプションでロックを「奪う」ことが可能です。

request(name, callback)および request(name, options, callback) メソッドの手順は次の通りです:

  1. optionsが渡されなかった場合、optionsにデフォルトメンバーを持つ新しいLockOptions辞書を設定します。

  2. environmentthisrelevant settings objectとします。

  3. environment関連グローバルオブジェクト関連付けられたDocument完全にアクティブでない場合、"InvalidStateError" DOMExceptionrejectされたpromiseを返します。

  4. managerenvironmentロックマネージャの取得の結果とします。それが失敗を返した場合、"SecurityError" DOMExceptionrejectされたpromiseを返します。

  5. nameがU+002Dハイフン(-)で始まる場合、"NotSupportedError" DOMExceptionrejectされたpromiseを返します。

  6. options["steal"]とoptions["ifAvailable"]が両方trueの場合、"NotSupportedError" DOMExceptionrejectされたpromiseを返します。

  7. options["steal"]がtrueで、options["mode"]が"exclusive"でない場合、"NotSupportedError" DOMExceptionrejectされたpromiseを返します。

  8. options["signal"]が存在する場合、 options["steal"]またはoptions["ifAvailable"]のいずれかがtrueなら、"NotSupportedError" DOMExceptionrejectされたpromiseを返します。

  9. options["signal"]が存在し、かつabortedなら、options["signal"]のabort reasonrejectされたpromiseを返します。

  10. promise新しいpromiseとします。

  11. ロックを要求するpromise、現在のagentenvironmentidmanagercallbacknameoptions["mode"]、options["ifAvailable"]、options["steal"]、options["signal"]で実行します。

  12. promiseを返します。

3.2.2. query() メソッド

LockManager/query

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
state = await navigator . locks . query()

query() メソッドは、オリジンのロックマネージャの状態スナップショットを生成するために利用できます。これにより、Webアプリケーションはロック利用状況をログやデバッグ目的で検査できます。

返されるPromiseはstate(JSONライクなデータ構造)で解決され、以下の形式です:

{
  held: [
    { name: "resource1", mode: "exclusive",
      clientId: "8b1e730c-7405-47db-9265-6ee7c73ac153" },
    { name: "resource2", mode: "shared",
      clientId: "8b1e730c-7405-47db-9265-6ee7c73ac153" },
    { name: "resource2", mode: "shared",
      clientId: "fad203a5-1f31-472b-a7f7-a3236a1f6d3b" },
  ],
  pending: [
    { name: "resource1", mode: "exclusive",
      clientId: "fad203a5-1f31-472b-a7f7-a3236a1f6d3b" },
    { name: "resource1", mode: "exclusive",
      clientId: "d341a5d0-1d8d-4224-be10-704d1ef92a15" },
  ]
}

clientIdフィールドは個別のコンテキスト(フレームやワーカー)に対応し、Clientid 属性で返される値と同じです。

query()メソッドの手順は次の通りです:

  1. environmentthisrelevant settings objectとします。

  2. environment関連グローバルオブジェクト関連付けられたDocument完全にアクティブでない場合、"InvalidStateError" DOMExceptionrejectされたpromiseを返します。

  3. managerenvironmentロックマネージャの取得の結果とします。それが失敗を返した場合、"SecurityError" DOMExceptionrejectされたpromiseを返します。

  4. promise新しいpromiseとします。

  5. ロック状態のスナップショット手順managerpromiseロックタスクキューにエンキューします。

  6. promiseを返します。

3.3. Lock クラス

Lock

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
[SecureContext, Exposed=(Window,Worker)]
interface Lock {
  readonly attribute DOMString name;
  readonly attribute LockMode mode;
};

Lockオブジェクトは関連するロックを持ちます。

Lock/name

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

nameゲッターの手順は、関連するロックnameを返します。

Lock/mode

すべての現行エンジンで利用可能です。

Firefox96以上Safari15.4以上Chrome69以上
Opera?Edge79以上
Edge (レガシー)?IE利用不可
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

modeゲッターの手順は、関連するロックmodeを返します。

4. アルゴリズム

4.1. ロックの要求

ロックを要求するには promise, agent, clientId, manager, callback, name, mode, ifAvailable, steal, signalを与えて次を行う:
  1. requestを新しいロック要求agent, clientId, manager, name, mode, callback, promise, signal)とする。

  2. signalが存在する場合、次のアルゴリズム要求中止のsignal通知として requestsignalsignalに追加する。

  3. 以下の手順を ロックタスクキューにエンキュー:

    1. queueMapmanagerロック要求キュー・マップとする。

    2. queuequeueMapからnameロック要求キューを取得した結果とする。

    3. heldmanager保持ロック集合とする。

    4. stealがtrueなら、次の手順を実行:

      1. held内のlockに対し:

        1. locknamenameなら、次を行う:

          1. lockheldから削除。

          2. lockreleased promiseを "AbortError" DOMExceptionでreject。

      2. requestqueueに先頭に追加。

    5. それ以外の場合、次の手順を実行:

      1. ifAvailableがtrueかつrequestgrantableでない場合、 以下の手順を callbackrelevant settings objectresponsible event loopにエンキュー:

        1. rcallbackを 引数にnullで呼び出した結果とする。

        2. promiserでresolveし、この手順を中止。

      2. requestqueueにエンキュー。

    6. ロック要求キューを処理する queue

  4. requestを返す。

4.2. ロックの解放

ロックの解放 lockには次を行う:
  1. アサート:これらの手順はロックタスクキュー上で実行されている。

  2. managerlockmanagerとする。

  3. queueMapmanagerロック要求キュー・マップとする。

  4. namelockリソース名とする。

  5. queuequeueMapからnameロック要求キューを取得した結果とする。

  6. lockmanager保持ロック集合から削除。

  7. ロック要求キューを処理する queue

4.3. 要求の中止

要求の中止 requestには次を行う:
  1. アサート:これらの手順はロックタスクキュー上で実行されている。

  2. managerrequestmanagerとする。

  3. namerequestnameとする。

  4. queueMapmanagerロック要求キュー・マップとする。

  5. queuequeueMapからnameロック要求キューを取得した結果とする。

  6. requestqueueから削除。

  7. ロック要求キューを処理する queue

要求中止のsignal通知 requestsignalには次を行う:
  1. 以下の手順を 要求の中止 requestに対しロックタスクキューにエンキュー。

  2. requestpromisesignalabort reasonでreject。

4.4. 指定されたリソース名のロック要求キューの処理

ロック要求キューを処理する queueには次を行う:
  1. アサート:これらの手順はロックタスクキュー上で実行されている。

  2. queue内の各requestについて:

    1. requestgrantableでなければreturn。

      注: キューの最初の項目だけがgrantableです。したがって、grantableでない場合、以降の項目も自動的にgrantableではありません。

    2. requestqueueから削除。

    3. agentrequestagentとする。

    4. managerrequestmanagerとする。

    5. clientIdrequestclientIdとする。

    6. namerequestnameとする。

    7. moderequestmodeとする。

    8. callbackrequestcallbackとする。

    9. prequestpromiseとする。

    10. signalrequestsignalとする。

    11. waiting新しいPromiseとする。

    12. lockを新しいロックagent agentclientId clientIdmanager managermode modename namereleased promise pwaiting promise waiting )とする。

    13. lockmanager保持ロック集合に追加。

    14. 以下の手順を callbackrelevant settings objectresponsible event loopにエンキュー:

      1. signalが存在する場合、次の手順を実行:

        1. signalabortedなら、次を行う:

          1. 以下の手順を ロックタスクキューにエンキュー:

            1. ロックの解放 lock

          2. return。

        2. signal to abort the request アルゴリズムをsignalから削除。

      2. rcallbackを 新しいLockオブジェクト (lockを関連付け)を唯一の引数として呼び出した結果とする。

      3. waitingrでresolve。

4.5. ロック状態のスナップショット

ロック状態のスナップショットmanagerpromiseで行うには:
  1. アサート:これらの手順はロックタスクキュー上で実行されている。

  2. pendingを新しいリストとする。

  3. managerロック要求キュー・マップvaluesごとにqueueについて:

    1. queue内の各requestについて:

      1. 追加 «[ "name" → requestname, "mode" → requestmode, "clientId" → requestclientId ]» をpendingに追加する。

  4. heldを新しいリストとする。

  5. manager保持ロック集合の各lockについて:

    1. 追加 «[ "name" → lockname, "mode" → lockmode, "clientId" → lockclientId ]» をheldに追加する。

  6. promiseを«[ "held" → held, "pending" → pending ]»でresolve。

5. 利用上の注意事項

このセクションは規範的ではありません。

5.1. デッドロック

デッドロックは並行計算の概念であり、特定のロックマネージャに限定されたデッドロックがこのAPIによって生じることがあります。

デッドロックを防ぐには注意が必要です。一つの方法は、常に複数のロックを厳密な順序で取得することです。

6. セキュリティとプライバシーに関する考慮事項

6.1. ロックのスコープ

ロックマネージャのスコープ定義は、プライバシー境界を定めるため重要です。ロックは一時的な状態保持機構として使われ、ストレージAPI同様通信手段にもなり得るため、ストレージ機能以上の権限を持ってはなりません。ユーザーエージェントがこれらサービスのいずれかに細かい粒度を課す場合、他のサービスにも同じ粒度を課す必要があります。例えば、プライバシー理由でトップレベルページ(ファーストパーティ)とクロスオリジンiframe(サードパーティ)で異なるストレージ区画を同一オリジンに対し公開する場合、ロックも同様に分割しなければなりません。

これにより、Webアプリ開発者にも合理的な期待が与えられます。ストレージリソース上でロックを取得した場合、同一オリジンのすべてのブラウジングコンテキストが同じ状態を観測できなければなりません。

6.2. プライベートブラウジング

プライベートモードのブラウジングセッションは、このAPIの観点では別のユーザーエージェントとみなされます。つまり、こうしたセッション外で要求・保持されたロックは、セッション内の要求・保持に影響しませんし、その逆も同様です。これにより、ウェブサイトがセッションが「シークレットモード」であることを判定したり、こうしたセッション間で通信手段を提供したりすることが防止されます。

6.3. 実装リスク

実装では、ロックがオリジンをまたがってはいけないことを保証しなければなりません。これを怠ると、2つのオリジンで動作するスクリプト間の通信のためのサイドチャネルが生じたり、一方のオリジンのスクリプトがもう一方の挙動を妨害する(例えばサービス拒否)ことが可能となります。

6.4. チェックリスト

W3C TAGはセキュリティ・プライバシー自己レビュー質問票を仕様編集者向けに作成しています。以下、その質問内容を再確認します:

7. 謝辞

この提案の作成にあたり、下記の方々に深く感謝します。 Alex Russell, Andreas Butler, Anne van Kesteren, Boris Zbarsky, Chris Messina, Darin Fisher, Domenic Denicola, Gus Caplan, Harald Alvestrand, Jake Archibald, Kagami Sascha Rosylight, L. David Baron, Luciano Pacheco, Marcos Caceres, Ralph Chelala, Raymond Toy, Ryan Fioravanti, そして Victor Costan 。

Bikeshed(この仕様書作成に用いたツール)および著者全般への助言をしてくださったTab Atkins, Jr.にも特別感謝いたします。

適合性

文書の規約

適合性要件は、記述的な断定とRFC 2119の用語の組み合わせで表現されます。 規範部分で使われる “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” というキーワードは、 RFC 2119で説明されているとおりに解釈されます。 ただし、可読性のため、これらの語はすべて大文字では記載されません。

本仕様書のすべてのテキストは規範的です(明確に非規範と示されたセクション、例、注意書きを除く)。[RFC2119]

この仕様書の例は「例えば」などの語で始まるか、規範テキストから class="example" を使って区別されています。例:

これは非規範的な例です。

非規範的な注意書きは「注」で始まり、class="note"で規範テキストから区別されています。例:

注:これは非規範的な注意書きです。

適合アルゴリズム

アルゴリズムの一部として命令形で記載された要件(たとえば「先頭の空白文字を取り除く」や「falseを返してこれらの手順を中止する」など)は、 そのアルゴリズムの導入で使われたキーワード("must", "should", "may"など)が持つ意味で解釈されます。

アルゴリズムや具体的な手順として表現された適合性要件は、最終結果が同等である限り、いかなる方法でも実装可能です。 特に、本仕様書で定義されるアルゴリズムは理解しやすいことを意図しており、性能を意図したものではありません。 実装者は最適化を推奨します。

索引

本仕様書で定義されている用語

参照によって定義される用語

参考文献

規範参考文献

[CSS21]
Bert Bos; 他. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011年6月7日. REC. URL: https://www.w3.org/TR/CSS21/
[DOM]
Anne van Kesteren. DOM Standard. 現行標準. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 他. HTML Standard. 現行標準. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. 現行標準. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[Storage]
Anne van Kesteren. Storage Standard. 現行標準. URL: https://storage.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. 現行標準. URL: https://webidl.spec.whatwg.org/

参考情報

[IndexedDB-2]
Ali Alabbas; Joshua Bell. Indexed Database API 2.0. 2018年1月30日. REC. URL: https://www.w3.org/TR/IndexedDB-2/
[Service-Workers]
Jake Archibald; Marijn Kruisselbrink. Service Workers. 2022年7月12日. CR. URL: https://www.w3.org/TR/service-workers/

IDL索引

[SecureContext]
interface mixin NavigatorLocks {
  readonly attribute LockManager locks;
};
Navigator includes NavigatorLocks;
WorkerNavigator includes NavigatorLocks;

[SecureContext, Exposed=(Window,Worker)]
interface LockManager {
  Promise<any> request(DOMString name,
                       LockGrantedCallback callback);
  Promise<any> request(DOMString name,
                       LockOptions options,
                       LockGrantedCallback callback);

  Promise<LockManagerSnapshot> query();
};

callback LockGrantedCallback = Promise<any> (Lock? lock);

enum LockMode { "shared", "exclusive" };

dictionary LockOptions {
  LockMode mode = "exclusive";
  boolean ifAvailable = false;
  boolean steal = false;
  AbortSignal signal;
};

dictionary LockManagerSnapshot {
  sequence<LockInfo> held;
  sequence<LockInfo> pending;
};

dictionary LockInfo {
  DOMString name;
  LockMode mode;
  DOMString clientId;
};

[SecureContext, Exposed=(Window,Worker)]
interface Lock {
  readonly attribute DOMString name;
  readonly attribute LockMode mode;
};

課題索引

ここで [Storage] との統合を改善し、与えられた環境からどのようにロックマネージャを正しく取得するかを含めて明確にしてください。
これは現在ワーカー専用であり、ワーカー終了時にステップを実行する規範的な方法がないため、定義が曖昧です。