ストレージ

現行標準 — 最終更新日

参加方法:
GitHub whatwg/storage (新規 issue, オープンな issue)
Matrix でチャット
コミット:
GitHub whatwg/storage/commits
このコミット時点のスナップショット
@storagestandard
テスト:
web-platform-tests storage/ (進行中の作業)
翻訳 (非標準):
日本語
简体中文
한국어

概要

ストレージ標準は、永続ストレージとクォータ推定のためのAPI、およびプラットフォームストレージアーキテクチャを定義します。

1. 序章

長年にわたり、ウェブにはIndexedDB、localStorageshowNotification()など、ストレージに使用できるさまざまなAPIが成長してきました。ストレージ標準は、以下を定義することでこれらのAPIを統合します:

従来、ユーザーのデバイスでストレージスペースが不足すると、これらのAPIで保存されたデータはユーザーが介入できないまま失われていました。しかし、永続バケットは、ユーザーの同意なしに削除することはできません。これにより、ネイティブプラットフォームでユーザーが享受していたデータ保証がウェブにもたらされます。

ストレージを永続化する簡単な方法は、persist() メソッドを呼び出すことです。このメソッドは、エンドユーザーに許可をリクエストし、それが許可されるとストレージを永続化に変更します:

navigator.storage.persist().then(persisted => {
  if (persisted) {
    /* … */
  }
});

エンドユーザーに事前通知なしにユーザーエージェント駆動のダイアログを表示しないためには、少し複雑なコードを書くことができます:

Promise.all([
  navigator.storage.persisted(),
  navigator.permissions.query({name: "persistent-storage"})
]).then(([persisted, permission]) => {
  if (!persisted && permission.state == "granted") {
    navigator.storage.persist().then( /* … */ );
  } else if (!persisted && permission.state == "prompt") {
    showPersistentStorageExplanation();
  }
});

estimate() メソッドは、アプリケーションのコンテンツを保存するのに十分なスペースが残っているかどうかを判断するために使用できます:

function retrieveNextChunk(nextChunkInfo) {
  return navigator.storage.estimate().then(info => {
    if (info.quota - info.usage > nextChunkInfo.size) {
      return fetch(nextChunkInfo.url);
    } else {
      throw new Error("insufficient space to store next chunk");
    }
  }).then( /* … */ );
}

2. 用語

この仕様はインフラ標準に依存します。[INFRA]

この仕様はHTML、IDL、および許可標準の用語を使用します。[HTML] [WEBIDL] [PERMISSIONS]

3. 全体像

ユーザーエージェント には、さまざまな種類の準永続的な状態があります:

認証情報

HTMLフォームを通じて送信されたユーザー名やパスワードなどのエンドユーザー認証情報

許可

位置情報など、さまざまな機能の許可

ネットワーク

HTTPキャッシュ、クッキー、認証エントリ、TLSクライアント証明書

ストレージ
Indexed DB、キャッシュAPI、サービスワーカーレジストレーション、localStoragesessionStorage、アプリケーションキャッシュ、通知など

この標準は主にストレージに関わるものです。

4. モデル

ローカルまたはセッションストレージAPIを定義する標準は、ストレージエンドポイントを定義し、それを登録するためにこの標準を変更します。それらは、ローカルストレージボトルマップの取得またはセッションストレージボトルマップの取得アルゴリズムを呼び出します。これにより以下のものが得られます:

そのようなAPIの標準を定義している場合、この標準に対して助言やレビューを求める問題を提出することを検討してください。

ストレージモデルの図(次の段落で説明されています)。

このデータを分離するため、この標準はストレージシェッドを定義し、ストレージ棚ストレージキーで分割します。ストレージ棚はさらにストレージバケットで構成され、将来的には異なるストレージポリシーを可能にするため、複数のストレージバケットで構成される可能性があります。そして最後に、ストレージバケットは、ストレージボトルで構成され、各ストレージエンドポイントに対応する1つがあります。

4.1. ストレージエンドポイント

ストレージエンドポイントとは、この標準で定義されたインフラストラクチャ、特にストレージボトルを使用して、ストレージニーズを追跡するローカルまたはセッションストレージAPIです。

ストレージエンドポイントには、識別子があり、これはストレージ識別子です。

ストレージエンドポイントには、さらにタイプがあり、これはセットストレージタイプです。

ストレージエンドポイントには、さらにクォータがあり、これはヌルまたはこのストレージエンドポイントに対応する各クォータ(バイト単位)を表す推奨値です。

ストレージ識別子は、ASCII文字列です。

ストレージタイプは、"local"または"session"です。


登録されたストレージエンドポイントは、以下の表で定義されたセットストレージエンドポイントです。

識別子 タイプ クォータ
"caches" « "local" » null
"indexedDB" « "local" » null
"localStorage" « "local" » 5 × 220(つまり、5メビバイト)
"serviceWorkerRegistrations" « "local" » null
"sessionStorage" « "session" » 5 × 220(つまり、5メビバイト)

前述のように、標準はこれらのストレージ識別子を使用して、ローカルストレージボトルマップの取得セッションストレージボトルマップの取得を行うことができます。今後、一部のAPIは両方のストレージタイプに適用されると予想されています。

4.2. ストレージキー

ストレージキーとは、タプルであり、オリジンオリジン)で構成されます。[HTML]

これは変更される可能性があります。詳しくはクライアントサイドストレージの分割を参照してください。

ストレージキーを取得するには、環境 environment が与えられた場合、次の手順を実行します:

  1. keyを、environmentを使用してストレージ目的以外でストレージキーを取得するを実行した結果とします。

  2. keyオリジン不透明なオリジンである場合、失敗を返します。

  3. ユーザーがストレージを無効にしている場合、失敗を返します。

  4. keyを返します。

ストレージ目的以外でストレージキーを取得するには、環境 environment が与えられた場合、次の手順を実行します:

  1. environment環境設定オブジェクトである場合、environmentオリジンoriginとします。それ以外の場合、environment作成URLオリジンoriginとします。

  2. originで構成されたタプルを返します。

ストレージキー Aストレージキー B等しいかどうかを判定するには、次の手順を実行します:

  1. AオリジンBオリジン同一オリジンでない場合、falseを返します。

  2. trueを返します。

4.3. ストレージシェッド

ストレージシェッドとは、マップであり、ストレージキーストレージ棚にマッピングします。初期状態では空です。


ユーザーエージェントは、ストレージシェッドを保持します。これはストレージシェッドです。ユーザーエージェントのストレージシェッドには、すべてのローカルストレージデータが含まれます。

移動可能ナビゲーブルは、ストレージシェッドを保持します。これはストレージシェッドです。移動可能ナビゲーブルストレージシェッドには、すべてのセッションストレージデータが含まれます。

移動可能ストレージシェッドのレガシークローンを実行するには、移動可能ナビゲーブル A移動可能ナビゲーブル B が与えられた場合、次の手順を実行します:

  1. keyshelf について、Aストレージシェッドを反復します:

    1. "session"を使用してストレージ棚を作成するを実行した結果をnewShelfとします。

    2. newShelfバケットマップ["default"]のボトルマップ["sessionStorage"]のマップを、shelfバケットマップ["default"]のボトルマップ["sessionStorage"]のマップクローンに設定します。

    3. Bストレージシェッド[key]をnewShelfに設定します。

これはレガシーと見なされ、利点があったとしても実装の複雑さを上回るものではありません。そのため、HTML以外では拡張されたり使用されたりすることはありません。[HTML]

4.4. ストレージ棚

ストレージ棚は、ストレージシェッド内の各ストレージキーごとに存在します。これはストレージバケットへマッピングするマップであるバケットマップを保持します。

現在のところ、"default"はバケットマップ内に存在する唯一のキーです。詳しくはissue #2を参照してください。これは初めてストレージ棚取得されたときにが与えられます。

ストレージ棚を取得するには、ストレージシェッド shed環境設定オブジェクト environment、およびストレージタイプ typeが与えられた場合、次の手順を実行します:

  1. keyを、environmentを使用してストレージキーを取得するを実行した結果とします。

  2. keyが失敗の場合、失敗を返します。

  3. shed[key]が存在しない場合、shed[key]をストレージ棚を作成するtypeで実行した結果に設定します。

  4. shed[key]を返します。

ローカルストレージ棚を取得するには、環境設定オブジェクト environmentが与えられた場合、次の値を返します: ユーザーエージェントのストレージシェッドenvironment、および"local"を用いてストレージ棚を取得するを実行した結果。

ストレージ棚を作成するには、ストレージタイプ typeが与えられた場合、次の手順を実行します:

  1. shelfを新しいストレージ棚に設定します。

  2. shelfバケットマップ["default"]をストレージバケットを作成するtypeで実行した結果に設定します。

  3. shelfを返します。

4.5. ストレージバケット

ストレージバケットは、ストレージエンドポイントがデータを保存する場所です。

ストレージバケットには、ストレージ識別子ストレージボトルにマッピングするボトルマップがあります。


ローカルストレージバケットは、ローカルストレージAPI用のストレージバケットです。

ローカルストレージバケットには、"best-effort"または"persistent"であるモードがあります。初期状態では"best-effort"です。


セッションストレージバケットは、セッションストレージAPI用のストレージバケットです。


ストレージバケットを作成するには、ストレージタイプ typeが与えられた場合、次の手順を実行します:

  1. bucketをnullに設定します。

  2. typeが"local"の場合、bucketを新しいローカルストレージバケットに設定します。

  3. それ以外の場合:

    1. アサート: typeが"session"である。

    2. bucketを新しいセッションストレージバケットに設定します。

  4. endpointについて、登録されたストレージエンドポイントの中で、そのタイプtype含む場合、bucketボトルマップ[endpoint識別子]を、ストレージボトルの新しいインスタンスに設定し、そのクォータendpointクォータに設定します。

  5. bucketを返します。

4.6. ストレージボトル

ストレージボトルとは、単一のストレージエンドポイント専用に区切られたストレージバケットの一部です。ストレージボトルには、初期状態で空のマップであるマップがあります。また、ストレージボトルには、初期状態で空のセットであるプロキシマップ参照セットがあります。また、ストレージボトルには、保持できるバイト数の保守的な推定値を表す数値またはヌルであるクォータがあります。ヌルは制限がないことを示します。それでも、包含するストレージ棚ストレージクォータに制約されます。

ストレージボトルマップは、実際に保存されるデータが存在する場所です。ユーザーエージェントは、このデータを保存し、エージェントエージェントクラスターの境界を越えて、この標準およびこの標準を使用する標準が内容にアクセスできるようにするために、実装定義の方法で利用可能にすることが期待されています。


ストレージボトルマップを取得するには、ストレージタイプ type環境設定オブジェクト environment、およびストレージ識別子 identifierが与えられた場合、次の手順を実行します:

  1. shedをnullに設定します。

  2. typeが"local"の場合、shedをユーザーエージェントのストレージシェッドに設定します。

  3. それ以外の場合:

    1. アサート: typeが"session"である。

    2. shedenvironmentグローバルオブジェクト関連付けられたDocumentノードナビゲーブル移動可能ナビゲーブルストレージシェッドに設定します。

  4. shelfshedenvironment、およびtypeを使用してストレージ棚を取得するを実行した結果に設定します。

  5. shelfが失敗の場合、失敗を返します。

  6. bucketshelfバケットマップ["default"]に設定します。

  7. bottlebucketボトルマップ[identifier]に設定します。

  8. proxyMapを新しいストレージプロキシマップに設定し、そのバッキングマップbottleマップに設定します。

  9. bottleプロキシマップ参照セットproxyMap追加します。

  10. proxyMapを返します。

ローカルストレージボトルマップを取得するには、環境設定オブジェクト environmentおよびストレージ識別子 identifierが与えられた場合、"local"、environment、およびidentifierを使用してストレージボトルマップを取得するを実行した結果を返します。

セッションストレージボトルマップを取得するには、環境設定オブジェクト environmentおよびストレージ識別子 identifierが与えられた場合、"session"、environment、およびidentifierを使用してストレージボトルマップを取得するを実行した結果を返します。

4.7. ストレージプロキシマップ

ストレージプロキシマップは、マップと同等ですが、すべての操作がそのバッキングマップ上で実行されます。

これにより、バッキングマップを置き換えることが可能になります。これはissue #4および潜在的にStorage Access APIに必要です。

4.8. ストレージタスクソース

ストレージタスクソースは、タスクソースであり、タスクに関連するすべてのストレージエンドポイント、特にストレージエンドポイントクォータに関連するものに使用されます。

ストレージタスクをキューに追加するには、グローバルオブジェクト global と一連の手順 steps が与えられた場合、グローバルタスクをキューに追加するストレージタスクソースglobalおよびstepsを指定して実行します。

5. 永続性の許可

ローカルストレージバケットは、ユーザー(またはユーザーに代わってユーザーエージェント)が"persistent-storage"の使用を許可する強力な機能への許可を与えた場合にのみ、そのモードを"persistent"に変更できます。

あるオリジンに許可が与えられると、永続性の許可を使用して、ユーザーエージェントのクリアポリシーからストレージを保護できます。永続性としてマークされたストレージは、オリジンまたはユーザーの関与なしにユーザーエージェントによってクリアされることはありません。これにより、オフライン時にユーザーが必要とするリソースや、ユーザーがローカルで作成するリソースに特に有用です。

"persistent-storage"の強力な機能の許可関連アルゴリズムおよびタイプはデフォルトですが、以下を除きます:

許可状態

"persistent-storage"の許可状態は、与えられた環境設定オブジェクトのすべてで同じオリジンを持つ必要があります。

許可の取り消しアルゴリズム
  1. "persistent-storage"で現在の許可状態を取得するの結果が"granted"の場合、終了します。

  2. shelfを、ローカルストレージ棚を取得する現在の設定オブジェクトで実行した結果に設定します。

  3. shelfバケットマップ["default"]のモードを"best-effort"に設定します。

6. 使用量とクォータ

ストレージ棚ストレージ使用量は、使用されているバイト数の実装定義によるおおよその推定値です。

これは正確な量ではありません。ユーザーエージェントは、重複排除、圧縮、およびその他の技術を使用して、ストレージ棚が使用するバイト数を正確に把握できないようにすることが奨励されています。

(これは追跡ベクトルです。) ストレージ棚ストレージクォータは、保持できるバイト数の実装定義による保守的な推定値です。この量はデバイス上の総ストレージスペースより少なくなければなりません。また、デバイス上で利用可能なストレージスペースの関数であってはなりません。

ユーザーエージェントは、クォータを決定する際にナビゲーション頻度、訪問の新しさ、ブックマーク、および"persistent-storage"に対する許可を考慮することが強く推奨されます。

利用可能なストレージスペースを直接的または間接的に明らかにすることは、フィンガープリントやオリジンの範囲外で情報を漏洩する可能性があります。

7. 管理

ユーザーエージェントがストレージバケットをクリアする場合、完全にクリアする必要があります。スクリプトがアクセス可能な状態で実行中の場合、ユーザーからの指示がない限り、ユーザーエージェントはストレージバケットをクリアしないようにするべきです。

ストレージバケットを削除した結果、包含するストレージ棚バケットマップになった場合、そのストレージ棚および対応するストレージキーを包含するストレージシェッドから削除します。

7.1. ストレージプレッシャー

ユーザーエージェントがストレージプレッシャーを受ける場合、ネットワーク状態およびローカルストレージバケットのうちモードが"best-effort"であるものをクリアするべきです。理想的には、ユーザーへの影響が最小限になるような方法で削除を優先します。

ユーザーエージェントが引き続きストレージプレッシャーを受ける場合、ユーザーエージェントはユーザーに通知し、残りのローカルストレージバケット(すなわち、モードが"persistent"であるもの)をクリアする方法を提供するべきです。

セッションストレージバケットは、移動可能ナビゲーブルが閉じられる際にクリアする必要があります。

ユーザーエージェントが移動可能ナビゲーブルの復活を許可する場合、たとえば移動可能ナビゲーブルを再オープンするか、ユーザーエージェントを再起動後に引き続き使用する場合、クリアにはより複雑なヒューリスティックが必要になります。

7.2. ユーザーインターフェースガイドライン

ユーザーエージェントは、個々のウェブサイトのためにネットワーク状態およびストレージをクリアする機能をユーザーに提供するべきです。ユーザーインターフェースでネットワーク状態とストレージを区別してはなりません。これにより、ネットワーク状態を使用してストレージを復活させることができなくなり、ユーザーが意識する必要のある概念の数を減らすことができます。

資格情報は分離するべきです。たとえば、自動生成されたパスワードなど、ユーザーが復活させることができないデータを含む場合があります。同様に、ユーザーの不便を避けるために許可も分離するのが最善です。

8. API

[SecureContext]
interface mixin NavigatorStorage {
  [SameObject] readonly attribute StorageManager storage;
};
Navigator includes NavigatorStorage;
WorkerNavigator includes NavigatorStorage;

環境設定オブジェクトには、関連付けられたStorageManager オブジェクトがあります。[HTML]

storageゲッターの手順は、this関連する設定オブジェクトStorageManager オブジェクトを返すことです。

[SecureContext,
 Exposed=(Window,Worker)]
interface StorageManager {
  Promise<boolean> persisted();
  [Exposed=Window] Promise<boolean> persist();

  Promise<StorageEstimate> estimate();
};

dictionary StorageEstimate {
  unsigned long long usage;
  unsigned long long quota;
};

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

  1. promise新しい Promise とします。

  2. globalthis関連するグローバルオブジェクト とします。

  3. shelfローカルストレージ棚を取得するthis関連する設定オブジェクト を使用して実行した結果とします。

  4. もし shelf が失敗であれば、reject promiseTypeError とします。

  5. それ以外の場合、次の手順を 並行して 実行します:

    1. persisted を、もし shelfバケットマップ["default"] の モード が "persistent" であれば true、それ以外の場合は false とします。

      内部エラーがある場合、false になります。

    2. ストレージタスクをキューに追加 し、global を使用して resolve promisepersisted とします。

  6. promise を返します。

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

  1. promise新しい Promise とします。

  2. globalthis関連するグローバルオブジェクト とします。

  3. shelfローカルストレージ棚を取得するthis関連する設定オブジェクト を使用して実行した結果とします。

  4. もし shelf が失敗であれば、reject promiseTypeError とします。

  5. それ以外の場合、次の手順を 並行して 実行します:

    1. permission使用許可をリクエストして "persistent-storage" とします。

      ユーザーエージェントは、同じ オリジン に対して同じ時期にこの質問を2回させないようにすることが推奨されます。このアルゴリズムはそのようなシナリオに対応していません。

    2. bucketshelfバケットマップ["default"] とします。

    3. persisted を、bucketモード が "persistent" であれば true、それ以外の場合は false とします。

      内部エラーがある場合、false になります。

    4. もし persisted が false であり、permission が "granted" であれば:

      1. bucketモード を "persistent" に設定します。

      2. 内部エラーがなければ、persisted を true に設定します。

    5. ストレージタスクをキューに追加 し、global を使用して resolve promisepersisted とします。

  6. promise を返します。

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

  1. promise新しい Promise とします。

  2. globalthis関連するグローバルオブジェクト とします。

  3. shelfローカルストレージ棚を取得するthis関連する設定オブジェクト を使用して実行した結果とします。

  4. もし shelf が失敗であれば、reject promiseTypeError とします。

  5. それ以外の場合、次の手順を 並行して 実行します:

    1. usageストレージ使用量 として shelf について取得します。

    2. quotaストレージクォータ として shelf について取得します。

    3. dictionary を新しい StorageEstimate 辞書とし、そのusage メンバーを usage とし、quota メンバーを quota とします。

    4. もし usage および quota を取得する際に内部エラーが発生した場合、ストレージタスクをキューに追加 し、global を使用して reject promiseTypeError とします。

      内部エラーは非常に稀であるべきであり、何らかの低レベルのプラットフォームまたはハードウェアの障害を示します。ただし、ウェブの規模および実装やプラットフォームの多様性を考慮すると、予期しない事象が発生する可能性があります。

    5. それ以外の場合、ストレージタスクをキューに追加 し、global を使用して resolve promisedictionary とします。

  6. promise を返します。

謝辞

最後に、以下の皆様に感謝申し上げます: Adrian Bateman、 Aislinn Grigas、 Alex Russell、 Ali Alabbas、 Andrew Sutherland、 Andrew Williams、 Austin Sullivan、 Ben Kelly、 Ben Turner、 Dale Harvey、 David Grogan、 Domenic Denicola、 fantasai、 Jake Archibald、 Jeffrey Yasskin、 Jesse Mykolyn、 Jinho Bang、 Jonas Sicking、 Joshua Bell、 Kenji Baheux、 Kinuko Yasuda、 Luke Wagner、 Michael Nordman、 Mike Taylor、 Mounir Lamouri、 Shachar Zohar、 黃強 (Shawn Huang)、 簡冠庭 (Timothy Guan-tin Chien)、そして Victor Costan、 素晴らしい貢献をありがとうございます!

この現行標準は、Anne van KesterenApple, annevk@annevk.nl)により執筆されています。

知的財産権

Copyright © WHATWG (Apple, Google, Mozilla, Microsoft)。この作品はCreative Commons Attribution 4.0 International Licenseの下でライセンスされています。その一部がソースコードに組み込まれている場合、その部分はBSD 3-Clause Licenseの下でライセンスされています。

これは現行標準です。特許レビュー版に関心のある方は、現行標準レビュー版をご覧ください。

索引

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

他の参照で定義されている用語

参照

規範的参照

[ECMASCRIPT]
ECMAScript 言語仕様. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; et al. HTML標準. 現行標準. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. インフラ標準. 現行標準. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[URL]
Anne van Kesteren. URL標準. 現行標準. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL標準. 現行標準. URL: https://webidl.spec.whatwg.org/

IDL 索引

[SecureContext]
interface mixin NavigatorStorage {
  [SameObject] readonly attribute StorageManager storage;
};
Navigator includes NavigatorStorage;
WorkerNavigator includes NavigatorStorage;

[SecureContext,
 Exposed=(Window,Worker)]
interface StorageManager {
  Promise<boolean> persisted();
  [Exposed=Window] Promise<boolean> persist();

  Promise<StorageEstimate> estimate();
};

dictionary StorageEstimate {
  unsigned long long usage;
  unsigned long long quota;
};

MDN

Navigator/storage

In all current engines.

Firefox57+Safari15.2+Chrome55+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

WorkerNavigator/storage

In all current engines.

Firefox57+Safari15.2+Chrome55+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

StorageManager/estimate

In all current engines.

Firefox57+Safari17+Chrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

StorageManager/persist

In all current engines.

Firefox57+Safari15.2+Chrome55+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

StorageManager/persisted

In all current engines.

Firefox57+Safari15.2+Chrome55+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

StorageManager

In all current engines.

Firefox57+Safari15.2+Chrome55+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?