索引数据库 API 3.0

W3C 工作草案,

有关本文档的更多详细信息
此版本:
https://www.w3.org/TR/2025/WD-IndexedDB-3-20250731/
最新发布版本:
https://www.w3.org/TR/IndexedDB/
编者草案:
https://w3c.github.io/IndexedDB/
先前版本:
历史:
https://www.w3.org/standards/history/IndexedDB-3/
测试套件:
https://github.com/web-platform-tests/wpt/tree/master/IndexedDB
反馈:
GitHub
编者:
(Google)
(Microsoft)
前编者:
Ali Alabbas (曾就职于 Microsoft)

摘要

本文档定义了用于保存简单值和分层对象的记录数据库的 API。每条记录都由一个键和某个值组成。此外,数据库会对其存储的记录维护索引。应用程序开发人员直接使用 API 通过其键或使用索引来定位记录。可以在此 API 之上构建查询语言。索引数据库可以使用持久性 B 树数据结构来实现。

本文档的状态

本节描述了本文档在发布时的状态。可以在 W3C 标准和草案索引中找到当前的 W3C 出版物列表和本技术报告的最新修订版。

本文档由 Web 应用程序工作组作为工作草案发布,使用推荐标准跟踪。作为工作草案发布并不意味着 W3C 及其成员的认可。

这是一个草案文件,可能随时被其他文件更新、替换或废弃。将本文档引用为正在进行的工作以外的内容是不合适的。

本文档由一个根据 W3C 专利政策运作的小组制作。W3C 维护一个与该小组的可交付成果相关的任何专利披露的公开列表;该页面还包括披露专利的说明。任何实际知晓某项专利的个人,如果认为该专利包含基本权利要求,则必须根据 W3C 专利政策第 6 节披露该信息。

本文档受 2023 年 11 月 3 日 W3C 流程文档的约束。

这是 Indexed Database API 的第三版。 第一版, 标题仅为“Indexed Database API”, 于 2015 年 1 月 8 日成为 W3C 推荐标准。 第二版, 标题为“Indexed Database API 2.0”, 于 2018 年 1 月 30 日成为 W3C 推荐标准。

Indexed Database API 3.0 旨在取代 Indexed Database API 2.0。

1. 引言

为了满足 Web 应用程序的离线数据需求,用户代理需要在本地存储大量对象。[WEBSTORAGE] 对于存储键及其对应值的配对非常有用。 但是,它不提供键的有序检索、对值的高效搜索,或为一个键存储重复值的功能。

本规范提供了一个具体的 API 来执行高级的键值数据管理,这是大多数复杂查询处理器核心所在。它通过使用事务性数据库来存储键及其对应的值(每个键可对应一个或多个值),并提供一种以确定性顺序遍历键的方法来实现这一点。这通常通过使用持久性 B-树数据结构来实现,这种结构被认为在插入和删除以及对大量数据记录进行有序遍历方面非常高效。

以下示例使用该 API 访问一个 "library" 数据库。它有一个 "books" 对象存储区,该存储区以书籍记录的 "isbn" 属性作为主键来保存这些记录。

书籍记录有一个 "title" 属性。本示例人为地要求书名是唯一的。代码通过创建一个名为 "by_title" 的索引并设置 unique 选项来强制执行此要求。该索引用于按标题查找书籍,并将阻止添加具有非唯一标题的书籍。

书籍记录还有一个 "author" 属性,该属性不要求是唯一的。代码创建了另一个名为 "by_author" 的索引,以允许按此属性进行查找。

代码首先打开一个到数据库的连接。upgradeneeded 事件处理程序代码在需要时创建对象存储区和索引。success 事件处理程序代码保存已打开的连接,以供后续示例使用。

const request = indexedDB.open("library");
    let db;
    
    request.onupgradeneeded = function() {
      // 数据库先前不存在,因此创建对象存储和索引。
      const db = request.result;
      const store = db.createObjectStore("books", {keyPath: "isbn"});
      const titleIndex = store.createIndex("by_title", "title", {unique: true});
      const authorIndex = store.createIndex("by_author", "author");
    
      // 使用初始数据填充。
      store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
      store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
      store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
    };
    
    request.onsuccess = function() {
      db = request.result;
    };
    

以下示例使用事务填充数据库。

const tx = db.transaction("books", "readwrite");
    const store = tx.objectStore("books");
    
    store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
    store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
    store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
    
    tx.oncomplete = function() {
      // 所有请求都已成功,事务已提交。
    };
    

以下示例使用索引按标题在数据库中查找单本书籍。

const tx = db.transaction("books", "readonly");
    const store = tx.objectStore("books");
    const index = store.index("by_title");
    
    const request = index.get("Bedrock Nights");
    request.onsuccess = function() {
      const matching = request.result;
      if (matching !== undefined) {
        // 找到了一个匹配项。
        report(matching.isbn, matching.title, matching.author);
      } else {
        // 未找到匹配项。
        report(null);
      }
    };
    

以下示例使用索引和游标按作者查找数据库中的所有书籍。

const tx = db.transaction("books", "readonly");
    const store = tx.objectStore("books");
    const index = store.index("by_author");
    
    const request = index.openCursor(IDBKeyRange.only("Fred"));
    request.onsuccess = function() {
      const cursor = request.result;
      if (cursor) {
        // 为每个匹配的记录调用。
        report(cursor.value.isbn, cursor.value.title, cursor.value.author);
        cursor.continue();
      } else {
        // 没有更多匹配的记录了。
        report(null);
      }
    };
    

以下示例展示了当请求失败时处理错误的一种方法。

const tx = db.transaction("books", "readwrite");
    const store = tx.objectStore("books");
    const request = store.put({title: "Water Buffaloes", author: "Slate", isbn: 987654});
    request.onerror = function(event) {
      // "by_title" 索引的唯一性约束失败。
      report(request.error);
      // 可以调用 event.preventDefault() 来防止事务中止。
    };
    tx.onabort = function() {
      // 否则,事务将因请求失败而自动中止。
      report(tx.error);
    };
    

当不再需要数据库连接时,可以将其关闭。

db.close();
    

将来,数据库可能已经增长到包含其他对象存储区和索引。以下示例展示了从旧版本迁移的一种方法。

const request = indexedDB.open("library", 3); // 请求版本 3。
    let db;
    
    request.onupgradeneeded = function(event) {
      const db = request.result;
      if (event.oldVersion < 1) {
        // 版本 1 是数据库的第一个版本。
        const store = db.createObjectStore("books", {keyPath: "isbn"});
        const titleIndex = store.createIndex("by_title", "title", {unique: true});
        const authorIndex = store.createIndex("by_author", "author");
      }
      if (event.oldVersion < 2) {
        // 版本 2 引入了一个按年份索引书籍的新索引。
        const bookStore = request.transaction.objectStore("books");
        const yearIndex = bookStore.createIndex("by_year", "year");
      }
      if (event.oldVersion < 3) {
        // 版本 3 引入了一个新的对象存储区用于存放杂志,并带有两个索引。
        const magazines = db.createObjectStore("magazines");
        const publisherIndex = magazines.createIndex("by_publisher", "publisher");
        const frequencyIndex = magazines.createIndex("by_frequency", "frequency");
      }
    };
    
    request.onsuccess = function() {
      db = request.result; // db.version 将是 3。
    };
    
一个数据库可以被多个客户端(页面和 worker)同时使用——事务确保它们在读写时不会发生冲突。 如果一个新客户端想要升级数据库(通过 upgradeneeded 事件),它必须等到所有其他客户端都关闭了与当前版本数据库的连接后才能进行。

为避免阻塞新客户端进行升级,客户端可以监听 versionchange 事件。当另一个客户端想要升级数据库时,此事件会触发。要允许此操作继续,请通过 执行最终关闭此客户端与数据库的连接的操作来响应 versionchange 事件。

一种方法是重新加载页面:

db.onversionchange = function() {
      // 首先,保存任何未保存的数据:
      saveUnsavedData().then(function() {
        // 如果文档未被激活使用,那么在没有用户交互的情况下重新加载
        // 页面可能是合适的。
        if (!document.hasFocus()) {
          location.reload();
          // 重新加载将关闭数据库,并使用新的 JavaScript
          // 和数据库定义重新加载。
        } else {
          // 如果文档具有焦点,重新加载页面可能会造成太大干扰。
          // 或许可以要求用户手动操作:
          displayMessage("请重新加载此页面以获取最新版本。");
        }
      });
    };
    
    function saveUnsavedData() {
      // 具体如何操作取决于您的应用。
    }
    
    function displayMessage() {
      // 向用户显示一个非模态消息。
    }
    

另一种方法是调用连接close() 方法。但是,您需要确保您的应用意识到这一点,因为后续访问数据库的尝试将会失败。

db.onversionchange = function() {
      saveUnsavedData().then(function() {
        db.close();
        stopUsingTheDatabase();
      });
    };
    
    function stopUsingTheDatabase() {
      // 将应用置于不再使用数据库的状态。
    }
    

新客户端(尝试升级的那个)可以使用 blocked 事件来检测是否有其他客户端阻止升级发生。blocked 事件会在其他客户端的 versionchange 事件触发后,仍然保持对数据库的连接时触发。

const request = indexedDB.open("library", 4); // 请求版本 4。
    let blockedTimeout;
    
    request.onblocked = function() {
      // 给其他客户端一些时间来异步保存数据。
      blockedTimeout = setTimeout(function() {
        displayMessage("升级被阻止 - 请关闭显示此站点的其他标签页。");
      }, 1000);
    };
    
    request.onupgradeneeded = function(event) {
      clearTimeout(blockedTimeout);
      hideMessage();
      // ...
    };
    
    function hideMessage() {
      // 隐藏先前显示的消息。
    }
    

只有当另一个客户端未能与数据库断开连接时,用户才会看到上述消息。理想情况下,用户永远不会看到这个。

2. 结构

一个 名称 是一个与 DOMString 等效的字符串; 也就是说,一个任意长度的 16 位代码单元的任意序列, 包括空字符串。名称总是作为 16 位代码单元的不透明序列进行比较。

注意: 因此,名称比较对大小写的变化 以及其他细微变化(如规范化形式、 控制字符的包含或省略以及 Unicode 文本中的其他变化)都很敏感。[Charmod-Norm]

如果实现使用的存储机制不支持 任意字符串,则该实现可以使用转义机制 或类似方法将提供的名称映射到它 可以存储的字符串。

要从一个列表 names 创建一个排序的名称列表,请执行以下步骤:

  1. sortednames 使用 代码单元小于 算法按升序排序的结果。

  2. 返回一个与 sorted 关联的新的 DOMStringList

详情 这与 ArrayString 上的 sort() 方法相匹配。 此排序比较每个字符串中的 16 位代码单元, 产生一个高效、一致且确定性的 排序顺序。结果列表不会匹配任何特定的 字母表或词典顺序,特别是对于 由代理对表示的代码点。

2.1. 数据库

每个存储键都有一组关联的数据库。一个数据库有零个或多个对象存储区,用于存放存储在数据库中的数据。

一个数据库有一个名称,用于在 特定的存储键中标识它。该名称是一个名称, 并在数据库的整个生命周期内保持不变。

一个数据库有一个版本。当一个数据库 首次创建时,其版本 为 0(零)。

注意: 每个数据库一次只有一个版本;一个数据库不能 同时存在于多个版本中。改变版本的唯一方法是 使用升级事务

一个数据库最多有一个关联的 升级事务, 它要么是 null,要么是一个升级事务,并且初始为 null。

2.1.1. 数据库连接

脚本不直接与数据库交互。相反, 脚本通过连接进行间接访问。 一个连接对象可用于 操作该数据库的对象。它也是获取 该数据库事务的唯一途径。

打开数据库的行为会创建一个 连接。 在任何给定时间,可能会有多个连接到 一个给定的数据库

一个连接只能访问与 创建该连接的全局作用域的存储键 相关联的数据库

注意: 这不受对Documentdomain的更改的影响。

一个连接有一个版本,它在 创建连接时设置。它在 连接的生命周期内保持不变, 除非升级被中止,在这种情况下, 它被设置为数据库的前一个版本。一旦 连接关闭, 版本不会改变。

每个连接都有一个关闭待定标志,初始为 false。

当一个连接最初 创建时,它处于打开状态。连接可以通过多种方式关闭。 如果创建连接的执行上下文被 销毁(例如由于用户导航离开该 页面),连接将被关闭。连接也可以 使用关闭数据库连接的步骤显式关闭。当 连接关闭时,如果其关闭待定标志尚未设置,则总是 设置为 true。

在特殊情况下,用户代理可能会关闭一个连接, 例如由于无法访问文件系统、 权限更改或清除存储键的 存储。如果发生这种情况, 用户代理必须使用连接并设置 强制标志为 true 来运行关闭数据库连接

一个连接有一个对象存储区集合, 它在创建连接时初始化为关联 数据库中的对象存储区集合。 该集合的内容将保持不变,除非升级事务活动的

一个连接获取父级算法返回 null。

如果尝试升级或删除 数据库,将在一个打开的 连接上触发一个类型为 versionchange 的事件。这给了连接关闭 的机会,以允许升级或删除继续进行。

如果连接被异常关闭,将在该连接上触发一个类型为 close 的事件。

2.2. 对象存储区

一个对象存储区是 在数据库中存储数据的主要存储机制。

每个数据库都有一组对象存储区对象存储区的集合可以更改,但只能使用升级事务, 即响应 upgradeneeded 事件。当 一个新数据库被创建时,它不包含任何对象存储区

一个对象存储区有一个记录列表,用于保存 存储在对象存储区中的数据。每个记录由一个 和一个组成。该列表根据键按 升序排序。在给定的对象 存储区中,永远不能有多个具有相同键的记录。

一个对象存储区有一个名称,它是一个名称。 在任何时候,该名称在其所属的数据库中都是唯一的。

一个对象存储区可选地有一个键路径。如果 对象存储区有键路径,则称其使用内联键。 否则称其使用外联键

一个对象存储区可选地有一个键生成器

一个对象存储区可以从以下三个来源之一为一个记录派生一个

  1. 一个键生成器。键生成器 每次需要键时都会生成一个单调 递增的数字。

  2. 键可以通过键路径派生。

  3. 当一个存储 在对象存储区中时,也可以显式指定键。

2.2.1. 对象存储区句柄

脚本不直接与对象存储区交互。而是在 一个事务内, 脚本通过一个 对象存储区句柄进行间接访问。

一个对象存储区句柄有一个关联的对象存储区 和一个关联的事务。在不同的 事务中,多个句柄可能 与同一个对象存储区关联, 但一个事务内, 一个特定的对象存储区 必须只有一个对象存储区句柄与之关联。

一个对象存储区句柄有一个索引集, 它在创建对象存储区句柄时 初始化为引用关联的 对象存储区索引集合。 该集合的内容将保持不变,除非 一个升级事务活动的

一个对象存储区句柄有一个名称, 它在创建对象存储区句柄时 初始化为关联的 对象存储区名称。 该名称将保持不变,除非一个升级事务活动的

2.3.

每个记录都与一个相关联。用户代理必须 支持任何可序列化对象。这包括简单类型, 如String 原始值和Date 对象,以及 ObjectArray 实例、File 对象、Blob 对象、ImageData 对象等等。记录的是 按值而非按引用存储和检索的;对一个值的后续更改 对存储在数据库中的记录没有影响。

记录的是由 StructuredSerializeForStorage 操作输出的记录

2.4.

为了高效地检索存储在索引 数据库中的记录,每个记录都根据其 进行组织。

一个有一个关联的类型, 它是以下之一: 数字日期字符串二进制, 或 数组

一个还有一个关联的, 它将是: 如果类型是数字日期,则为一个unrestricted double, 如果类型是字符串,则为一个DOMString, 如果类型是二进制,则为一个字节序列, 如果类型是数组,则为一个其他列表

一个 ECMAScript [ECMA-262] 值可以通过 遵循将值转换为键的步骤来转换为一个

注意: 以下 ECMAScript 类型是有效的键:

尝试将其他 ECMAScript 值转换为 将会失败。

一个数组键是一个,其类型数组。 一个数组键子键是 该 数组键项目

比较两个键 ab,请执行以下步骤:

  1. taa类型

  2. tbb类型

  3. 如果 ta 不等于 tb,则执行以下步骤:

    1. 如果 ta数组,则返回 1。

    2. 如果 tb数组,则返回 -1。

    3. 如果 ta二进制,则返回 1。

    4. 如果 tb二进制,则返回 -1。

    5. 如果 ta字符串,则返回 1。

    6. 如果 tb字符串,则返回 -1。

    7. 如果 ta日期,则返回 1。

    8. 断言tb日期

    9. 返回 -1。

  4. vaa

  5. vbb

  6. 根据 ta 进行切换:

    数字
    日期
    1. 如果 va 大于 vb,则返回 1。

    2. 如果 va 小于 vb,则返回 -1。

    3. 返回 0。

    字符串
    1. 如果 va 代码单元小于 vb, 则返回 -1。

    2. 如果 vb 代码单元小于 va, 则返回 1。

    3. 返回 0。

    二进制
    1. 如果 va 字节小于 vb,则返回 -1。

    2. 如果 vb 字节小于 va,则返回 1。

    3. 返回 0。

    数组
    1. lengthva大小vb大小中较小的一个。

    2. i 为 0。

    3. i 小于 length 时,则:

      1. c 为递归地比较两个键 va[i] 和 vb[i] 的结果。

      2. 如果 c 不为 0,返回 c

      3. i 增加 1。

    4. 如果 va大小大于 vb大小,则返回 1。

    5. 如果 va大小小于 vb大小,则返回 -1。

    6. 返回 0。

a 大于 b,如果 比较两个键 ab 的结果 是 1。

a 小于 b,如果 比较两个键 ab 的结果 是 -1。

a 等于 b,如果 比较两个键 ab 的结果是 0。

注意: 根据上述规则,负无穷是的最低 可能值。 数字键小于日期键。 日期键小于字符串键。 字符串键小于二进制键。 二进制键小于数组键。 没有最高的可能值。 这是因为任何候选最高的数组 后跟另一个会更高。

注意: 二进制键的成员被比较为无符号字节值 (范围在 0 到 255 之间,含),而不是有符号byte 值(范围在 -128 到 127 之间,含)。

2.5. 键路径

一个键路径是一个字符串或字符串列表, 它定义了如何从一个中提取一个。一个有效的键路径是以下之一:

注意: 键路径内不允许有空格。

键路径值只能从由StructuredSerializeForStorage显式复制的属性以及以下特定于类型的属性访问:

类型 属性
Blob size, type
File name, lastModified
Array length
String length

2.6. 索引

有时,通过对象存储区记录以外的其他方式来检索记录是很有用的。一个索引允许使用对象存储区记录的属性来查找对象存储区中的记录

索引是一种专门的持久化键值存储,并有一个引用的对象存储区。索引有一个记录列表,用于保存存储在索引中的数据。每当引用的对象存储区中的记录被插入、更新或删除时,索引中的记录会自动填充。可以有多个索引引用同一个对象存储区,在这种情况下,对对象存储区的更改会导致所有此类索引都被更新。

索引的记录中的始终是其引用的对象存储区中的值。是使用键路径从引用的对象存储区的派生出来的。如果在索引引用的对象存储区中,一个键为X记录的值为A,并且在A评估索引的键路径产生的结果为Y,那么该索引将包含一个键为Y、值为X的记录。

例如,如果一个索引的引用的对象存储区包含一个键为123、值为{ name: "Alice", title: "CEO" }的记录,并且该索引的键路径是"name",那么该索引将包含一个键为"Alice"、值为123的记录。

索引中的记录被称为有一个引用的值。这是索引引用的对象存储区中,其键等于该索引记录的值的记录的值。因此,在上面的例子中,索引中键为Y、值为X的记录,其引用的值A

在前面的例子中,索引中键为"Alice"、值为123的记录,其引用的值{ name: "Alice", title: "CEO" }

注意: 索引中的每条记录都引用其引用的对象存储区中的一条且仅一条记录。但是,索引中可以有多条记录引用对象存储区中的同一条记录。也可能索引中没有记录引用对象存储区中的某条给定记录。

索引中的记录总是根据记录的键进行排序。然而,与对象存储区不同,一个给定的索引可以包含多个具有相同键的记录。这些记录会额外根据索引记录的值(即引用的对象存储区中记录的键)进行排序。

一个索引有一个名称,它是一个名称。在任何时候,该名称在索引的引用的对象存储区内是唯一的。

一个索引有一个唯一标志。当为 true 时,该索引强制要求索引中没有两条记录具有相同的键。如果试图插入或修改索引的引用对象存储区中的记录,使得对记录的新值评估索引的键路径产生的结果在索引中已存在,则对对象存储区的修改尝试将失败。

一个索引有一个multiEntry 标志。当评估索引的键路径的结果产生一个数组键时,此标志会影响索引的行为。如果其multiEntry 标志为 false,则会将一个数组键的单个记录添加到索引中。如果其multiEntry 标志为 true,则会为每个子键向索引中添加一个记录

2.6.1. 索引句柄

脚本不直接与索引交互。而是在一个事务中,脚本通过一个索引句柄进行间接访问。

一个索引句柄有一个关联的索引和一个关联的对象存储区句柄。一个索引句柄事务是其关联的对象存储区句柄事务。在不同的事务中,多个句柄可能与同一个索引关联,但一个事务内,一个特定的索引必须只有一个索引句柄与之关联。

一个索引句柄有一个名称,它在创建索引句柄时初始化为关联的索引名称。该名称将保持不变,除非一个升级事务活动的

2.7. 事务

一个事务用于 与数据库中的数据进行交互。每当 从数据库读取或写入数据时,都是通过使用事务来完成的。

事务 为应用程序和系统故障提供了一些保护。一个事务可用于存储多个数据 记录或有条件地修改某些数据记录。一个 事务 代表了一组原子性和持久性的数据访问 和数据修改操作。

所有事务都通过一个连接创建,该连接是 事务的连接

一个事务 有一个范围,它是一个集合,包含了该事务 可以与之交互的对象存储区

注意: 一个事务范围保持 固定,除非该事务是一个升级事务

如果任何对象存储区同时存在于两个事务的范围中,则这两个事务具有重叠范围

一个事务 有一个模式,它决定了 可以对该事务执行哪些类型的交互。该模式 在事务创建时设置,并在事务的生命周期内保持不变。一个事务模式是 以下之一:

"readonly"

该事务只允许读取数据。此类型的事务不能进行任何修改。 这样做的好处是,即使它们的范围重叠(即它们 使用相同的对象存储区),也可以同时启动多个只读事务。 一旦数据库被打开,就可以随时创建此类型的事务。

"readwrite"

该事务允许从现有对象存储区读取、修改和删除数据。但是,不能添加或删除对象存储区和索引。 如果它们的范围重叠,则不能同时启动多个 "readwrite" 事务,因为这意味着它们可以在事务中途修改彼此的数据。 一旦数据库被打开,就可以随时创建此类型的事务。

"versionchange"

该事务允许从现有对象存储区读取、修改和删除数据,并且还可以创建和删除对象存储区和索引。 它是唯一可以这样做的事务类型。此类型的事务不能手动创建,而是在 upgradeneeded 事件被触发时自动创建。

一个事务 有一个持久性提示。这是给用户代理的一个提示,关于在提交事务时是优先考虑性能还是持久性。 持久性提示是 以下之一:

"strict"

只有在验证所有未完成的更改都已成功写入持久性存储介质后,用户代理才可以认为事务已成功提交

"relaxed"

一旦所有未完成的更改都已写入操作系统,用户代理就可以认为事务已成功提交,而无需后续验证。

"default"

用户代理应为存储桶使用其默认的持久性行为。如果未另外指定,这是事务的默认值。

注意: 在典型的实现中,"strict" 是给用户代理的一个提示,让其在complete 事件被触发之前刷新任何操作系统的 I/O 缓冲区。虽然这在后续发生操作系统崩溃或断电的情况下提供了更高的更改将被持久化的信心,但刷新缓冲区可能会花费大量时间并消耗便携式设备的电池寿命。

鼓励 Web 应用程序对临时数据(如缓存或快速变化的记录)使用"relaxed", 而在降低数据丢失风险的重要性超过对性能和功耗影响的情况下使用"strict"。 鼓励实现权衡来自应用程序的持久性提示与对用户和设备的影响。

一个事务 可选地有一个清理事件循环, 它是一个事件循环

一个事务 有一个请求列表,其中包含已对该事务发出的待处理请求

一个事务 有一个错误,如果 事务中止,则会设置该错误。

一个事务获取父级 算法返回 该事务的连接

一个只读事务是 一个事务,其模式为"readonly"。

一个读/写事务 是一个事务,其 模式为"readwrite"。

2.7.1. 事务生命周期

一个事务 有一个状态,它是以下之一:

活动

事务在首次创建时, 以及在从与事务关联的请求分派事件期间,处于此状态。

当事务处于此状态时,可以对其发出新的请求

非活动

事务在创建后控制权返回到事件循环时,以及在未分派事件时,处于此状态。

当事务处于此状态时,不能对其发出请求

正在提交

一旦与事务关联的所有请求都已完成, 事务将在尝试提交时进入此状态。

当事务处于此状态时,不能对其发出请求

已完成

一旦事务提交或中止,它将进入此状态。

当事务处于此状态时,不能对其发出请求

事务预计是短暂的。下面描述的 自动提交功能 鼓励了这一点。

注意: 开发者仍然可以使事务长时间保持存活; 但是,不建议使用这种模式,因为它可能导致糟糕的用户体验。

一个事务生命周期 如下:

  1. 一个事务被创建时带有一个范围和一个 模式。当一个事务被创建时,其 状态 最初是活动的

  2. 当实现能够强制执行事务的范围模式的约束时( 定义见下文),实现必须 排队一个数据库任务以异步地启动该 事务。

    一旦事务被启动, 实现就可以开始执行针对该事务发出的请求。请求必须按照 它们针对事务发出的顺序执行。同样,它们的结果也必须按照请求针对特定事务发出的顺序返回。 对于不同事务中请求的结果返回顺序,没有任何保证。

    注意: 事务模式确保 针对不同事务发出的两个请求可以以任何顺序执行,而不会影响最终存储在数据库中的数据。

  3. 当与事务关联的每个请求处理时, 将触发一个successerror 事件。 在事件被分派期间,事务 状态被 设置为活动的, 允许对该事务发出额外的请求。一旦事件分派完成,事务的 状态将 再次设置为非活动的

  4. 一个事务可以在其完成之前的任何时候被中止, 即使该事务当前不是活动的或尚未 启动

    abort()的显式调用 将启动一个中止。 在脚本未处理的失败请求之后,也会启动中止。

    当一个事务被中止时,实现必须撤销(回滚)在该事务期间对数据库所做的任何更改。 这包括对对象存储区内容的更改, 以及对象存储区索引的添加和删除。

  5. 当针对非活动 事务发出的所有请求都已完成并且其返回的结果已处理, 没有新的请求针对该事务发出,并且该事务未被中止时, 实现必须尝试提交该事务。

    commit()的显式调用 将启动一个提交, 而无需等待请求结果被脚本处理。

    提交时,事务状态被设置为 正在提交。 实现必须原子性地将针对该事务发出的请求所做的任何更改写入数据库。 也就是说,要么必须写入所有更改,要么如果发生错误(例如磁盘写入错误), 实现不得将任何更改写入数据库,并且将遵循中止事务的步骤。

  6. 当一个事务被提交中止时, 其状态 被设置为已完成

只要事务是活动的,实现就必须允许 请求发出。 即使事务尚未被启动,也是如此。 在事务被启动之前, 实现不得执行这些请求;但是,实现必须跟踪这些请求及其顺序。

一个事务 从其被创建到其状态被设置为已完成为止, 被称为是存活的

清理 Indexed Database 事务,请运行以下步骤。 如果清理了任何事务,它们将返回 true,否则返回 false。

  1. 如果没有事务清理事件循环与当前 事件循环匹配,则返回 false。

  2. 对于每个清理事件循环 与当前事件循环匹配的事务 transaction

    1. transaction状态设置为非活动的

    2. 清除 transaction清理事件循环

  3. 返回 true。

注意: 这些步骤由[HTML]调用。它们确保 由脚本调用transaction()创建的 事务 在调用该脚本的任务完成后被停用。这些步骤对每个事务最多运行一次。

一个类型为complete的事件会在 一个已成功提交事务上触发。

一个类型为abort的事件会在 一个已中止事务上触发。

2.7.2. 事务调度

以下约束定义了何时可以启动一个事务

实现可以施加额外的约束。例如,实现不需要并行启动重叠读/写事务,或者可以限制已启动事务的数量。

注意: 这些约束意味着以下几点:

2.7.3. 升级事务

一个升级事务是一个模式为“versionchange”的事务

当一个连接被打开到一个数据库时,如果指定了一个大于当前版本版本,则在运行升级数据库的步骤时会自动创建一个升级事务。这个事务将在upgradeneeded事件处理程序内部处于活动状态。

注意: 一个升级事务允许在数据库中创建、重命名和删除对象存储区索引

一个升级事务是排他性的。打开数据库连接的步骤确保当一个升级事务活动时,只有一个到该数据库的连接是打开的。 upgradeneeded事件不会被触发,因此升级事务也不会启动,直到所有其他到同一数据库连接都关闭。这确保了所有先前的事务都已完成

只要一个升级事务活动的,尝试打开更多到同一数据库连接都会被延迟,并且任何通过调用transaction()来使用同一连接启动额外事务的尝试都会抛出异常。这确保了没有其他事务并发活动,也确保了只要升级事务活动的,就不会有新的事务排队到同一个数据库

这进一步确保了一旦一个升级事务完成,数据库中的对象存储区索引集合在所有后续连接事务的生命周期内保持不变。

2.8. 请求

数据库的每个异步操作都是通过一个请求来完成的。每个请求代表一个操作。

一个请求有一个处理标志,初始为 false。当与请求关联的操作已执行时,此标志设置为 true。

当一个请求处理标志为 true 时,称该请求已被处理

一个请求有一个完成标志,初始为 false。当与请求关联的操作的结果可用时,此标志设置为 true。

一个请求有一个对象。

一个请求有一个结果和一个错误,在其完成标志为 true 之前,两者都不可访问。

一个请求有一个事务,初始为 null。当一个请求使用异步执行请求的步骤被置于一个事务中时,该属性将被设置。

当发出一个请求时,会返回一个新的请求,其完成标志设置为 false。如果一个请求成功完成,其完成标志被设置为 true,其结果被设置为请求的结果,并且一个类型为success的事件会在该请求上触发。

如果在执行操作时发生错误,请求的完成标志被设置为 true,请求的错误被设置为该错误,并且一个类型为error的事件会在该请求上触发。

一个请求获取父级算法返回该请求的事务

注意: 请求通常不被重用,但有例外。当一个游标被迭代时,迭代的成功会在用于打开游标的同一个请求对象上报告。当需要一个升级事务时,同一个打开请求会用于upgradeneeded事件和打开操作本身的最终结果。在某些情况下,请求的完成标志将被设置为 false,然后再次设置为 true,并且结果可能会改变,或者可能会设置错误

2.8.1. 打开请求

一个打开请求是一种特殊类型的请求,用于打开一个连接或删除一个数据库。除了successerror事件外,blockedupgradeneeded事件也可能在一个打开请求上触发以指示进度。

一个打开请求始终为 null。

一个打开请求事务为 null,除非一个upgradeneeded事件已被触发。

一个打开请求获取父级算法返回 null。

2.8.2. 连接队列

打开请求在一个连接队列中处理。该队列包含与一个存储键和一个名称关联的所有打开请求。添加到连接队列的请求按顺序处理,每个请求必须运行到完成,下一个请求才能被处理。一个打开请求可能会被其他连接阻塞,要求这些连接关闭,然后该请求才能完成并允许处理后续请求。

注意: 一个连接队列不是与一个事件循环关联的任务队列,因为请求是在任何特定的浏览上下文之外处理的。将事件传递给已完成的打开请求仍然通过与发出请求的上下文的事件循环关联的任务队列进行。

2.9. 键范围

可以使用键范围对象存储区索引中检索记录。一个键范围是用于键的某种数据类型上的连续区间。

一个键范围有一个关联的下界(null 或一个)。

一个键范围有一个关联的上界(null 或一个)。

一个键范围有一个关联的下界开放标志。除非另有说明,否则为 false。

一个键范围有一个关联的上界开放标志。除非另有说明,否则为 false。

一个键范围下界可以等于上界。一个键范围下界不能大于上界

一个仅包含 key键范围,其下界上界都等于 key

如果满足以下两个条件,则一个key在键范围 range 内:

注意:

一个无界键范围是一个键范围,其下界上界都等于 null。所有一个无界键范围内。

将一个值转换为键范围,使用value和可选的null disallowed flag,请运行以下步骤:

  1. 如果value是一个键范围,则返回value

  2. 如果value是 undefined 或 null,那么如果null disallowed flag为 true,则抛出一个“DataErrorDOMException,否则返回一个无界键范围

  3. key为使用value将一个值转换为键的结果。重新抛出任何异常。

  4. 如果key是“invalid value”或“invalid type”,则抛出一个“DataErrorDOMException

  5. 返回一个仅包含key键范围

一个潜在有效的键范围是一个 ECMAScript 值,其类型可转换为键范围。具体值是否能成功转换为键范围,即将一个值转换为键范围时是否会抛出异常,与此无关。

注意: 例如,一个分离的 BufferSource是一个潜在有效的键范围,当与将一个值转换为键范围一起使用时会抛出异常。

要确定一个值何时是一个潜在有效的键范围,使用 ECMAScript value,请运行以下步骤:

  1. 如果value是一个键范围,返回 true。

  2. key为使用value将一个值转换为键的结果。

  3. 如果key是“invalid type”,返回 false。

  4. 否则返回 true。

注意: getAll()getAllKeys()方法使用是一个潜在有效的键范围来处理它们的第一个参数。如果参数是一个潜在有效的键范围getAll()getAllKeys()会使用该参数运行将一个值转换为键范围。否则,第一个参数将使用IDBGetAllOptions

getAll()getAllKeys()会对当与将一个值转换为键一起使用时返回“invalid value”的DateArrayArrayBuffer第一个参数抛出异常。例如,使用一个 NaN Date作为第一个参数运行getAll()会抛出异常,而不是成功使用一个带有默认值的IDBGetAllOptions字典。

2.10. 游标

一个游标用于按特定方向迭代索引对象存储区中的一系列记录。

一个游标有一个源句柄,即打开该游标的索引句柄对象存储区句柄

一个游标有一个事务,即来自游标源句柄事务

一个游标有一个记录范围,这些记录位于索引对象存储区中。

一个游标有一个,即来自游标源句柄索引对象存储区。游标的指示与游标正在迭代的记录相关联的索引对象存储区。如果游标的源句柄索引句柄,则游标的该索引句柄关联的索引。否则,游标的该对象存储区句柄关联的对象存储区

一个游标有一个方向,它决定了在迭代时是按记录键的单调递增还是递减顺序移动,以及在迭代索引时是否跳过重复值。游标的方向还决定了游标的初始位置是在其的开头还是末尾。一个游标的方向是以下之一:

"next"

此方向使游标在的开头打开。迭代时,游标应按键的单调递增顺序产生所有记录,包括重复项。

"nextunique"

此方向使游标在的开头打开。迭代时,游标不应产生具有相同键的记录,但除此之外,按键的单调递增顺序产生所有记录。对于每个具有重复值的键,只产生第一条记录。当对象存储区或其唯一标志设置为 true 的索引时,此方向的行为与“next”完全相同。

"prev"

此方向使游标在的末尾打开。迭代时,游标应按键的单调递减顺序产生所有记录,包括重复项。

"prevunique"

此方向使游标在的末尾打开。迭代时,游标不应产生具有相同键的记录,但除此之外,按键的单调递减顺序产生所有记录。对于每个具有重复值的键,只产生第一条记录。当对象存储区或其唯一标志设置为 true 的索引时,此方向的行为与“prev”完全相同。

一个游标在其范围内有一个位置。在游标的整个范围被迭代完之前,游标正在迭代的记录列表可能会发生变化。为了处理这种情况,游标维护其位置不是作为一个索引,而是作为先前返回记录的。对于向前迭代的游标,下次要求游标迭代到下一条记录时,它会返回大于先前返回键的最低键的记录。对于向后迭代的游标,情况相反,它会返回小于先前返回键的最高键的记录。

对于迭代索引的游标,情况要复杂一些,因为多条记录可以有相同的键,因此也按排序。当迭代索引时,游标还有一个对象存储区位置,它指示索引中先前找到的记录。在查找下一条合适的记录时,会同时使用位置对象存储区位置

一个游标有一个和一个,它们代表最后迭代的记录

一个游标有一个获取值标志。当此标志为 false 时,游标要么正在加载下一个值,要么已到达其范围的末尾。当它为 true 时,表示游标当前持有一个值,并准备好迭代到下一个值。

如果游标的对象存储区,则游标的有效对象存储区是该对象存储区,游标的有效键是游标的位置。如果游标的索引,则游标的有效对象存储区是该索引引用的对象存储区,而有效键是游标的对象存储区位置

一个游标有一个请求,即用于打开游标的请求

一个游标还有一个仅键标志,它指示游标的是否通过 API 公开。

2.11. 键生成器

当创建对象存储时,可以指定使用 键生成器。如果在将记录插入对象存储时未另行指定,键生成器将用于生成键。

一个键生成器有一个当前数字当前数字总是一个小于或等于 253 (9007199254740992) + 1 的正整数。 一个键生成器当前数字的初始值为 1,在关联的对象存储创建时设置。 当前数字随着键的生成而递增,并可能通过使用显式键更新为特定值。

注意: 每个使用键生成器的对象存储都使用一个独立的生成器。也就是说,与一个对象存储的交互绝不会影响任何其他对象存储的键生成器。

修改键生成器的当前数字被视为数据库操作的一部分。这意味着如果操作失败并且操作被回滚,当前数字将恢复到操作开始前的值。 这既适用于因使用键生成器时当前数字增加 1 而发生的修改,也适用于因存储记录时在调用中指定了键值而发生的修改。

同样,如果一个事务被中止,事务范围内每个对象存储的键生成器的当前数字将恢复到事务开始前的值。

键生成器的当前数字永远不会减少,除非是数据库操作被回滚的结果。从对象存储中删除记录绝不会影响对象存储的键生成器。即使从对象存储中清除所有记录,例如使用clear()方法,也不会影响对象存储键生成器的当前数字

当存储记录时,如果在存储记录的调用中未指定,则会生成一个键。

对象存储 store 生成一个键,请运行以下步骤:

  1. generatorstore键生成器

  2. keygenerator当前数字

  3. 如果 key 大于 253 (9007199254740992),则返回失败。

  4. generator当前数字增加 1。

  5. 返回 key

当存储记录时,如果在存储记录的调用中指定了,则关联的键生成器可能会被更新。

对象存储 store 使用 key 可能更新键生成器,请运行以下步骤:

  1. 如果 key类型不是数字,则中止这些步骤。

  2. valuekey

  3. value 设置为 value 和 253 (9007199254740992) 中的较小值。

  4. value 设置为不大于 value 的最大整数。

  5. generatorstore键生成器

  6. 如果 value 大于或等于 generator当前数字,则将 generator当前数字设置为 value + 1。

注意: 对于使用内联键的对象存储,可以通过设置对象存储的键路径指向的存储值上的属性来指定键;对于使用外联键的对象存储,可以通过向存储记录的调用传递一个键参数来指定键。

只有类型数字的指定键才能影响键生成器的当前数字类型日期数组(无论其包含的其他键如何)、二进制字符串(无论它们是否可以被解析为数字)的键对键生成器的当前数字没有影响。 类型数字小于 1 的键不会影响当前数字,因为它们总是低于当前数字

当键生成器的当前数字达到超过 253 (9007199254740992) 的值时,任何后续使用键生成器生成新的尝试都将导致 “ConstraintErrorDOMException。 仍然可以通过指定显式键向对象存储中插入记录,但是,再次为此类记录使用键生成器的唯一方法是删除该对象存储并创建一个新的。

注意: 这个限制的出现是因为大于 9007199254740992 的整数不能唯一地表示为 ECMAScript Number。 例如,在 ECMAScript 中 9007199254740992 + 1 === 9007199254740992

只要以正常方式使用键生成器,这个限制就不会成为问题。如果你日夜不停地每秒生成 1000 个新键,你也要超过 285000 年才会遇到这个限制。

一个实际的结果是,为对象存储生成的第一个键总是 1(除非先插入了更高的数字键),并且为对象存储生成的键总是一个高于该存储中最高数字键的正整数。除非事务回滚,否则同一个键永远不会为同一个对象存储生成两次。

每个对象存储都有自己的键生成器:

store1 = db.createObjectStore("store1", { autoIncrement: true });
store1.put("a"); // 将获得键 1
store2 = db.createObjectStore("store2", { autoIncrement: true });
store2.put("a"); // 将获得键 1
store1.put("b"); // 将获得键 2
store2.put("b"); // 将获得键 2

如果由于约束违规或 IO 错误导致插入失败,键生成器不会更新。

transaction.onerror = function(e) { e.preventDefault() };
store = db.createObjectStore("store1", { autoIncrement: true });
index = store.createIndex("index1", "ix", { unique: true });
store.put({ ix: "a"}); // 将获得键 1
store.put({ ix: "a"}); // 将会失败
store.put({ ix: "b"}); // 将获得键 2

从 objectStore 中删除项目绝不会影响键生成器。包括调用 clear() 时。

store = db.createObjectStore("store1", { autoIncrement: true });
store.put("a"); // 将获得键 1
store.delete(1);
store.put("b"); // 将获得键 2
store.clear();
store.put("c"); // 将获得键 3
store.delete(IDBKeyRange.lowerBound(0));
store.put("d"); // 将获得键 4

插入带有显式键的项目会影响键生成器,当且仅当该键是数字且高于最后生成的键时。

store = db.createObjectStore("store1", { autoIncrement: true });
store.put("a"); // 将获得键 1
store.put("b", 3); // 将使用键 3
store.put("c"); // 将获得键 4
store.put("d", -10); // 将使用键 -10
store.put("e"); // 将获得键 5
store.put("f", 6.00001); // 将使用键 6.0001
store.put("g"); // 将获得键 7
store.put("f", 8.9999); // 将使用键 8.9999
store.put("g"); // 将获得键 9
store.put("h", "foo"); // 将使用键 "foo"
store.put("i"); // 将获得键 10
store.put("j", [1000]); // 将使用键 [1000]
store.put("k"); // 将获得键 11
// 如果 objectStore 使用了 keyPath 并且显式键在对象中内联传递,
// 所有这些行为都将相同

中止事务会回滚事务期间发生的对键生成器的任何增加。这是为了使所有回滚保持一致,因为由于崩溃而发生的回滚永远没有机会提交增加的键生成器值。

db.createObjectStore("store", { autoIncrement: true });
trans1 = db.transaction(["store"], "readwrite");
store_t1 = trans1.objectStore("store");
store_t1.put("a"); // 将获得键 1
store_t1.put("b"); // 将获得键 2
trans1.abort();
trans2 = db.transaction(["store"], "readwrite");
store_t2 = trans2.objectStore("store");
store_t2.put("c"); // 将获得键 1
store_t2.put("d"); // 将获得键 2

以下示例说明了尝试使用内联键生成器将对象保存到对象存储时的不同行为。

如果以下条件为真:

那么键生成器提供的值将用于填充键值。在下面的示例中,对象存储的键路径是 "foo.bar"。实际对象没有 bar 属性的值,即 { foo: {} }。当对象保存在对象存储中时,bar 属性被赋值为 1,因为这是键生成器生成的下一个

const store = db.createObjectStore("store", { keyPath: "foo.bar",
                                              autoIncrement: true });
store.put({ foo: {} }).onsuccess = function(e) {
  const key = e.target.result;
  console.assert(key === 1);
};

如果以下条件为真:

那么将使用与键路径属性关联的值。不会使用自动生成的。在下面的示例中,对象存储键路径是 "foo.bar"。实际对象的 bar 属性的值为 10,即 { foo: { bar: 10} }。当对象保存在对象存储中时,bar 属性保持其值 10,因为这是键值。

const store = db.createObjectStore("store", { keyPath: "foo.bar",
                                              autoIncrement: true });
store.put({ foo: { bar: 10 } }).onsuccess = function(e) {
  const key = e.target.result;
  console.assert(key === 10);
};

以下示例说明了当指定的内联通过键路径定义但没有匹配的属性时的场景。然后,键生成器提供的值将用于填充键值,并且系统负责创建所需数量的属性以满足层次结构链上的属性依赖关系。在下面的示例中,对象存储键路径是 "foo.bar.baz"。实际对象没有 foo 属性的值,即 { zip: {} }。当对象保存在对象存储中时,会创建 foobarbaz 属性,每个属性都是另一个的子属性,直到可以为 foo.bar.baz 赋值。foo.bar.baz 的值是对象存储生成的下一个键。

const store = db.createObjectStore("store", { keyPath: "foo.bar.baz",
                                              autoIncrement: true });
store.put({ zip: {} }).onsuccess = function(e) {
  const key = e.target.result;
  console.assert(key === 1);
  store.get(key).onsuccess = function(e) {
    const value = e.target.result;
    // value 将是: { zip: {}, foo: { bar: { baz: 1 } } }
    console.assert(value.foo.bar.baz === 1);
  };
};

尝试在原始值上存储属性将失败并引发错误。在下面的第一个示例中,对象存储的键路径是 "foo"。实际对象是一个值为 4 的原始值。尝试在该原始值上定义属性会失败。

const store = db.createObjectStore("store", { keyPath: "foo", autoIncrement: true });

// 键生成将尝试在此原始值上创建并存储键路径属性。
store.put(4); // 将抛出 DataError

2.12. 记录快照

一个记录快照 包含从对象存储记录索引记录复制的键和值。

一个记录快照有一个 ,它是一个

一个记录快照有一个 ,它是一个

注意: 对于索引记录,快照的是记录的引用值的副本。 对于对象存储记录,快照的记录的值

一个记录快照还有一个主键,它是一个

注意: 对于索引记录,快照的主键是记录的,也就是索引的引用对象存储中记录的键。 对于对象存储记录,快照的主键是同一个,也就是记录的键

3. 异常

本文档中使用的每个异常都是一个具有特定类型的 DOMException。异常类型和属性(如旧版代码值)在[WEBIDL]中定义。

下表列出了本文档中使用的 DOMException 以及对异常类型用法的描述。

类型 描述
AbortError 请求被中止。
ConstraintError 事务中的一个变更操作因未满足约束而失败。
DataCloneError 正在存储的数据无法通过内部结构化克隆算法进行克隆。
DataError 提供给操作的数据不符合要求。
InvalidAccessError 对一个对象执行了无效的操作。
InvalidStateError 在一个不允许的对象上或在不允许的时间调用了一个操作,或者对一个已被删除或移除的源对象发出了请求。
NotFoundError 操作失败,因为找不到请求的数据库对象。
NotReadableError 操作失败,因为无法读取包含请求数据的底层存储。
QuotaExceededError 操作失败,因为没有足够的剩余存储空间,或者达到了存储配额,并且用户拒绝为数据库提供更多空间。
SyntaxError keyPath 参数包含一个无效的键路径。
ReadOnlyError 在只读事务中尝试了变更操作。
TransactionInactiveError 对一个当前不活动或已完成的事务发出了请求。
UnknownError 操作因与数据库本身无关或任何其他错误未涵盖的暂时性原因而失败。
VersionError 尝试使用比现有版本更低的版本打开数据库。

注意: 鉴于多个 Indexed DB 操作可能抛出相同类型的错误,甚至单个操作也可能因多种原因抛出相同类型的错误,鼓励实现提供更具体的消息,以帮助开发人员识别错误的原因。

4. API

API 方法返回时不会阻塞调用线程。所有异步操作都会立即返回一个 IDBRequest 实例。此对象最初不包含有关操作结果的任何信息。一旦信息可用,就会在请求上触发一个事件,并且信息可以通过 IDBRequest 实例的属性获得。

这些任务的任务源数据库访问任务源

将数据库任务排入队列,请在数据库访问任务源上执行将任务排入队列

4.1. IDBRequest 接口

IDBRequest 接口提供了使用事件处理程序 IDL 属性 [HTML] 访问对数据库数据库对象的异步请求结果的方法。

每个发出异步请求的方法都会返回一个 IDBRequest 对象,该对象通过事件与请求的应用程序进行通信。这种设计意味着任何数据库上都可以同时有任意数量的活动请求。

在下面的示例中,我们异步打开一个数据库。注册了各种事件处理程序以响应各种情况。

const request = indexedDB.open('AddressBook', 15);
request.onsuccess = function(evt) {...};
request.onerror = function(evt) {...};
[Exposed=(Window,Worker)]
interface IDBRequest : EventTarget {
  readonly attribute any result;
  readonly attribute DOMException? error;
  readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
  readonly attribute IDBTransaction? transaction;
  readonly attribute IDBRequestReadyState readyState;

  // 事件处理程序:
  attribute EventHandler onsuccess;
  attribute EventHandler onerror;
};

enum IDBRequestReadyState {
  "pending",
  "done"
};
request . result

当请求完成时,返回结果,如果请求失败则返回 undefined。如果请求仍在处理中,则抛出“InvalidStateErrorDOMException

request . error

当请求完成时,返回错误(一个DOMException),如果请求成功则返回 null。如果请求仍在处理中,则抛出“InvalidStateErrorDOMException

request . source

返回请求所针对的 IDBObjectStoreIDBIndexIDBCursor,如果它是一个打开请求,则返回 null。

request . transaction

返回请求所在的 IDBTransaction。如果这是一个打开请求,那么当它处于活动状态时,它会返回一个升级事务,否则返回 null。

request . readyState

在请求完成之前返回“pending”,然后返回“done”。

result getter 的步骤是:
  1. 如果完成标志为 false,则抛出一个“InvalidStateErrorDOMException

  2. 返回结果,如果请求导致错误,则返回 undefined。

error getter 的步骤是:
  1. 如果完成标志为 false,则抛出一个“InvalidStateErrorDOMException

  2. 返回错误,如果没有发生错误,则返回 null。

source getter 的步骤是返回,如果未设置,则返回 null。

transaction getter 的步骤是返回事务

注意:对于某些请求,例如从 open() 返回的请求transaction getter 可以返回 null。

readyState getter 的步骤是,如果完成标志为 false,则返回“pending”,否则返回“done”。

onsuccess 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型success

onerror 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型error 事件。

IDBDatabase 上返回打开请求的方法使用扩展接口来允许监听 blockedupgradeneeded 事件。

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // 事件处理程序:
  attribute EventHandler onblocked;
  attribute EventHandler onupgradeneeded;
};

onblocked 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型blocked

onupgradeneeded 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型upgradeneeded

4.2. 事件接口

本规范使用以下自定义接口触发事件:

[Exposed=(Window,Worker)]
interface IDBVersionChangeEvent : Event {
  constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict = {});
  readonly attribute unsigned long long oldVersion;
  readonly attribute unsigned long long? newVersion;
};

dictionary IDBVersionChangeEventInit : EventInit {
  unsigned long long oldVersion = 0;
  unsigned long long? newVersion = null;
};

oldVersion getter 的步骤是返回其初始化时的值。它表示数据库的先前版本。

newVersion getter 的步骤是返回其初始化时的值。它表示数据库的新版本,如果数据库正在被删除,则为 null。请参阅升级数据库的步骤。

事件的构造如 DOM § 2.5 构造事件中所定义。

触发版本更改事件,名为 e,在 target 上,给定 oldVersionnewVersion,请执行以下步骤:

  1. event 为使用 IDBVersionChangeEvent 创建事件的结果。

  2. eventtype 属性设置为 e

  3. eventbubblescancelable 属性设置为 false。

  4. eventoldVersion 属性设置为 oldVersion

  5. eventnewVersion 属性设置为 newVersion

  6. legacyOutputDidListenersThrowFlag 为 false。

  7. target 上使用 legacyOutputDidListenersThrowFlag 派发 event

  8. 返回 legacyOutputDidListenersThrowFlag

    注意: 此算法的返回值并非总是被使用。

4.3. IDBFactory 接口

数据库对象通过 IDBFactory 接口上的方法进行访问。一个实现了此接口的单一对象存在于支持 Indexed DB 操作的环境的全局作用域中。

partial interface mixin WindowOrWorkerGlobalScope {
      [SameObject] readonly attribute IDBFactory indexedDB;
    };
    

indexedDB 属性为应用程序提供了一种访问索引数据库功能的机制。

[Exposed=(Window,Worker)]
    interface IDBFactory {
      [NewObject] IDBOpenDBRequest open(DOMString name,
                                        optional [EnforceRange] unsigned long long version);
      [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name);
    
      Promise<sequence<IDBDatabaseInfo>> databases();
    
      short cmp(any first, any second);
    };
    
    dictionary IDBDatabaseInfo {
      DOMString name;
      unsigned long long version;
    };
    
request = indexedDB . open(name)

尝试使用当前版本(如果不存在则为 1)打开到指定名称数据库连接。如果请求成功,requestresult 将是连接

request = indexedDB . open(name, version)

尝试使用指定的 version 打开到指定名称数据库连接。如果数据库已存在且版本较低,并且有打开的连接未响应 versionchange 事件而关闭,则请求将被阻塞,直到所有连接都关闭,然后将发生升级。如果数据库已存在且版本较高,则请求将失败。如果请求成功,requestresult 将是连接

request = indexedDB . deleteDatabase(name)

尝试删除指定名称的数据库。如果数据库已存在并且有打开的连接未响应 versionchange 事件而关闭,则请求将被阻塞,直到所有连接都关闭。如果请求成功,requestresult 将为 null。

result = await indexedDB . databases()

返回一个 Promise,该 Promise 解析为一个对象列表,提供存储键内数据库名称和版本的快照。

此 API 旨在供 Web 应用程序内省数据库的使用情况,例如清理网站代码的早期版本。请注意,结果是一个快照;对于此上下文或其他上下文创建、升级或删除数据库的请求,无法保证数据收集的顺序或响应的传递。

open(name, version) 方法的步骤如下:

  1. 如果 version 为 0(零),则抛出一个 TypeError

  2. environment相关设置对象

  3. storageKey 为运行获取存储键(给定 environment)的结果。如果返回失败,则抛出一个“SecurityErrorDOMException 并中止这些步骤。

  4. request 为一个新的打开请求

  5. 并行运行这些步骤:

    1. result打开数据库连接的结果,使用 storageKeyname、如果给定则为 version 否则为 undefined,以及 request

      如果未提供 version 会发生什么? 如果未提供 version 并且已存在具有该名称的数据库,则将打开一个连接而不会更改版本。如果未提供 version 并且不存在具有该名称的数据库,则将创建一个新的数据库,其版本等于 1。
    2. request已处理标志设置为 true。

    3. 将一个数据库任务排入队列以运行以下步骤:

      1. 如果 result 是一个错误,则:

        1. request结果设置为 undefined。

        2. request错误设置为 result

        3. request完成标志设置为 true。

        4. request触发一个名为 error 的事件,并将其 bubblescancelable 属性初始化为 true。

      2. 否则:

        1. request结果设置为 result

        2. request完成标志设置为 true。

        3. request触发一个名为 success 的事件。

        注意: 如果上述步骤导致运行了升级事务,这些步骤将在该事务完成后运行。这确保了在即将发生另一次版本升级的情况下,首先在连接上触发 success 事件,以便脚本有机会为 versionchange 事件注册一个监听器。

        为什么不使用触发成功事件触发错误事件的步骤? (此时)没有与请求关联的事务,因此那些在派发前激活关联事务并在派发后停用事务的步骤不适用。
  6. 返回一个新的 IDBOpenDBRequest 对象给 request

deleteDatabase(name) 方法的步骤如下:

  1. environment相关设置对象

  2. storageKey 为运行获取存储键(给定 environment)的结果。如果返回失败,则抛出一个“SecurityErrorDOMException 并中止这些步骤。

  3. request 为一个新的打开请求

  4. 并行运行这些步骤:

    1. result删除数据库的结果,使用 storageKeynamerequest

    2. request已处理标志设置为 true。

    3. 将一个数据库任务排入队列以运行以下步骤:

      1. 如果 result 是一个错误, 将 request错误设置为 result, 将 request完成标志设置为 true, 并在 request触发一个名为 error 的事件,并将其 bubblescancelable 属性初始化为 true。

      2. 否则, 将 request结果设置为 undefined, 将 request完成标志设置为 true, 并在请求上使用 result 和 null 触发一个名为 success 的版本更改事件。

        为什么不使用触发成功事件触发错误事件的步骤? 没有与请求关联的事务,因此那些在派发前激活关联事务并在派发后停用事务的步骤不适用。

        此外,这里的 success 事件是一个 IDBVersionChangeEvent, 它包含了 oldVersionnewVersion 的详细信息。

  5. 返回一个新的 IDBOpenDBRequest 对象给 request

databases() 方法的步骤如下:

  1. environment相关设置对象

  2. storageKey 为运行获取存储键(给定 environment)的结果。如果返回失败,则返回一个被拒绝的 Promise,并附带一个“SecurityErrorDOMException

  3. p一个新的 Promise

  4. 并行运行这些步骤:

    1. databasesstorageKey 中的数据库集合。如果由于任何原因无法确定,则将一个数据库任务排入队列拒绝 p,并附带一个适当的错误(例如一个“UnknownErrorDOMException) 并终止这些步骤。

    2. result 为一个新的列表

    3. 对于 databases 中的每个 db

      1. 如果 db版本为 0,则继续

      2. info 为一个新的 IDBDatabaseInfo 字典。

      3. infoname 字典成员设置为 db名称

      4. infoversion 字典成员设置为 db版本

      5. 追加 inforesult

    4. 将一个数据库任务排入队列以使用 result 解析 p

  5. 返回 p

🚧 databases() 方法是本版本中的新方法。 Chrome 71、Edge 79、Firefox 126 和 Safari 14 支持该方法。 🚧
result = indexedDB . cmp(key1, key2)

将两个值作为进行比较。如果 key1key2 之前,则返回 -1;如果 key2key1 之前,则返回 1;如果 键相等,则返回 0。

如果任一输入不是有效的,则抛出“DataErrorDOMException

cmp(first, second) 方法的步骤如下:

  1. a 为使用 first 将值转换为键的结果。重新抛出任何异常。

  2. 如果 a 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  3. b 为使用 second 将值转换为键的结果。重新抛出任何异常。

  4. 如果 b 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  5. 返回使用 ab 比较两个键的结果。

4.4. IDBDatabase 接口

IDBDatabase 接口表示到数据库连接

如果一个 IDBDatabase 对象的关联连接关闭待定标志为 false,并且它有一个或多个类型为 aborterrorversionchange 的事件监听器注册,那么该对象不能被垃圾回收。如果一个 IDBDatabase 对象被垃圾回收,其关联的连接必须被关闭

[Exposed=(Window,Worker)]
interface IDBDatabase : EventTarget {
  readonly attribute DOMString name;
  readonly attribute unsigned long long version;
  readonly attribute DOMStringList objectStoreNames;

  [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
                                          optional IDBTransactionMode mode = "readonly",
                                          optional IDBTransactionOptions options = {});
  undefined close();

  [NewObject] IDBObjectStore createObjectStore(
    DOMString name,
    optional IDBObjectStoreParameters options = {});
  undefined deleteObjectStore(DOMString name);

  // 事件处理程序:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

enum IDBTransactionDurability { "default", "strict", "relaxed" };

dictionary IDBTransactionOptions {
  IDBTransactionDurability durability = "default";
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};
connection . name

返回数据库的名称

connection . version

返回数据库的版本

name getter 的步骤是返回对象关联的数据库名称

注意: 即使对象的关闭待定标志为 true,name 属性仍然返回此名称。换句话说,此属性的值在 IDBDatabase 实例的生命周期内保持不变。

version getter 的步骤是返回对象的版本

这与数据库版本相同吗? 只要连接是打开的,这就与所连接的数据库版本相同。但是一旦连接关闭,此属性将不会反映后续升级事务所做的更改。
connection . objectStoreNames

返回数据库中对象存储的名称列表。

store = connection . createObjectStore(name [, options])

使用给定的 nameoptions 创建一个新的对象存储,并返回一个新的 IDBObjectStore

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

connection . deleteObjectStore(name)

删除具有给定 name对象存储

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

objectStoreNames getter 的步骤是:
  1. names对象的对象存储集对象存储名称列表

  2. 返回使用 names 创建排序名称列表的结果(一个 DOMStringList)。

这与数据库对象存储名称相同吗? 只要连接是打开的,这就与所连接的数据库对象存储名称相同。但一旦连接关闭,此属性将不会反映后续升级事务所做的更改。

createObjectStore(name, options) 方法的步骤是:

  1. database对象关联的数据库

  2. transactiondatabase升级事务,如果它不为 null,否则抛出一个“InvalidStateErrorDOMException

  3. 如果 transaction状态不是活动, 则抛出一个“TransactionInactiveErrorDOMException

  4. keyPathoptionskeyPath 成员,如果它不是 undefined 或 null,否则为 null。

  5. 如果 keyPath 不为 null 且不是一个有效的键路径,则抛出一个“SyntaxErrorDOMException

  6. 如果一个名为 name对象存储已经存在于 database 中,则抛出一个“ConstraintErrorDOMException

  7. autoIncrementoptionsautoIncrement 成员。

  8. 如果 autoIncrement 为 true 且 keyPath 是一个空字符串或任何序列(无论是否为空),则抛出一个 “InvalidAccessErrorDOMException

  9. storedatabase 中的一个新的对象存储。将被创建的对象存储名称设置为 name。如果 autoIncrement 为 true,则被创建的对象存储使用一个键生成器。如果 keyPath 不为 null,则将被创建的对象存储键路径设置为 keyPath

  10. 返回一个与 storetransaction 关联的新的对象存储句柄

此方法在已连接数据库中创建并返回一个具有给定名称的新对象存储。请注意,此方法只能在升级事务中调用。

此方法同步修改调用它的 IDBDatabase 实例上的 objectStoreNames 属性。

在某些实现中,在 createObjectStore() 方法返回后,实现可能会在排队创建对象存储的任务时遇到问题。例如,在实现中,有关新创建的对象存储的元数据是异步插入数据库的,或者实现可能需要因配额原因请求用户许可。此类实现仍必须创建并返回一个 IDBObjectStore 对象,并且一旦实现确定创建对象存储失败,它必须使用中止事务的步骤并使用适当的错误来中止事务。例如,如果由于配额原因创建对象存储失败,则必须使用“QuotaExceededErrorDOMException 作为错误。

deleteObjectStore(name) 方法的步骤是:

  1. database对象关联的数据库

  2. transactiondatabase升级事务,如果它不为 null,否则抛出一个“InvalidStateErrorDOMException

  3. 如果 transaction状态不是活动, 则抛出一个“TransactionInactiveErrorDOMException

  4. storedatabase名为 name对象存储, 如果不存在则抛出一个“NotFoundErrorDOMException

  5. 对象的对象存储集中移除 store

  6. 如果存在与 storetransaction 关联的对象存储句柄,则从其 索引集中移除所有条目。

  7. 销毁 store

此方法销毁已连接数据库中具有给定名称的对象存储。请注意,此方法只能在升级事务中调用。

此方法同步修改调用它的 IDBDatabase 实例上的 objectStoreNames 属性。

transaction = connection . transaction(scope [, mode [, options ] ])

返回一个新的事务,具有给定的 scope(可以是一个对象存储名称或一个名称数组)、 mode(“readonly” 或“readwrite”), 以及包括 durability (“default”、 “strict” 或“relaxed”)在内的附加 options

默认的 mode 是“readonly”, 默认的 durability 是“default”。

connection . close()

一旦所有正在运行的事务 完成后,关闭连接

transaction(storeNames, mode, options) 方法的步骤是:

  1. 如果一个活动的升级事务连接相关联, 则抛出 一个“InvalidStateErrorDOMException

  2. 如果对象的关闭待定标志为 true,则抛出一个 “InvalidStateErrorDOMException

  3. scopestoreNames 中唯一字符串的集合(如果它是一个序列),否则为一个包含等于 storeNames 的一个字符串的集合。

  4. 如果 scope 中的任何字符串不是已连接数据库对象存储的名称,则抛出一个 “NotFoundErrorDOMException

  5. 如果 scope 为空,则抛出一个 “InvalidAccessErrorDOMException

  6. 如果 mode 不是“readonly” 或“readwrite”, 则抛出 一个 TypeError

  7. transaction 为一个新创建事务,使用此连接modeoptionsdurability 成员,以及 scope 中命名的对象存储的集合。

  8. transaction清理事件循环设置为 当前的事件循环

  9. 返回一个表示 transactionIDBTransaction 对象。

🚧 durability 选项是本版本中的新功能。 Chrome 82、Edge 82、Firefox 126 和 Safari 15 支持该选项。 🚧

注意: 创建的 transaction 将遵循生命周期 规则。

close() 方法的步骤是:

  1. 使用此连接运行关闭数据库连接

注意: 连接在所有未完成的事务完成之前不会实际关闭。后续对 close() 的调用将无效。

onabort 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型abort

onclose 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型close

onerror 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型error

onversionchange 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型versionchange

4.5. IDBObjectStore 接口

IDBObjectStore 接口表示一个对象存储句柄

[Exposed=(Window,Worker)]
interface IDBObjectStore {
  attribute DOMString name;
  readonly attribute any keyPath;
  readonly attribute DOMStringList indexNames;
  [SameObject] readonly attribute IDBTransaction transaction;
  readonly attribute boolean autoIncrement;

  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);
  [NewObject] IDBRequest clear();
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any queryOrOptions,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any queryOrOptions,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllRecords(optional IDBGetAllOptions options = {});
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  IDBIndex index(DOMString name);

  [NewObject] IDBIndex createIndex(DOMString name,
                                   (DOMString or sequence<DOMString>) keyPath,
                                   optional IDBIndexParameters options = {});
  undefined deleteIndex(DOMString name);
};

dictionary IDBIndexParameters {
  boolean unique = false;
  boolean multiEntry = false;
};

dictionary IDBGetAllOptions {
  any query = null;
  [EnforceRange] unsigned long count;
  IDBCursorDirection direction = "next";
};
store . name

返回存储的名称

store . name = newName

将存储的名称更新为 newName

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

store . keyPath

返回存储的键路径,如果没有则返回 null。

store . indexNames

返回存储中索引的名称列表。

store . transaction

返回关联的事务

store . autoIncrement

如果存储具有键生成器,则返回 true,否则返回 false。

name getter 的步骤是返回对象的名称

这与对象存储名称相同吗? 只要事务尚未完成, 这与关联的对象存储名称相同。但是一旦事务 完成,此 属性将不会反映后续升级事务所做的更改。

name setter 的步骤是:

  1. name给定的值

  2. transaction对象的事务

  3. store对象的对象存储

  4. 如果 store 已被删除, 则抛出 一个“InvalidStateErrorDOMException

  5. 如果 transaction 不是一个升级事务, 则抛出 一个“InvalidStateErrorDOMException

  6. 如果 transaction状态不是活动的, 则抛出 一个“TransactionInactiveErrorDOMException

  7. 如果 store名称等于 name, 则终止这些步骤。

  8. 如果在 store数据库中已存在一个名为 name对象存储,则抛出一个 “ConstraintErrorDOMException

  9. store名称设置为 name

  10. 对象的名称设置为 name

keyPath getter 的步骤是 返回对象的对象存储键路径, 如果没有则返回 null。键路径根据 [WEBIDL] 转换为 DOMString (如果是字符串)或 sequence<DOMString> (如果是字符串列表)。

注意: 返回的值与创建对象存储时使用的实例不同。 但是,如果此属性返回一个对象(特别是Array), 则每次检查时都会返回相同的对象实例。更改对象的属性对对象存储没有影响。

indexNames getter 的步骤是:
  1. names对象的索引集索引名称列表

  2. 返回使用 names 创建排序名称列表的结果(一个 DOMStringList)。

这与对象存储索引名称列表相同吗? 只要事务尚未完成, 这与关联的对象存储索引名称列表相同。但是一旦 事务 完成,此 属性将不会反映后续升级事务所做的更改。

transaction getter 的步骤是 返回对象的事务

autoIncrement getter 的步骤是 如果对象的 对象存储具有键生成器,则返回 true, 否则返回 false。

以下方法如果在只读事务中调用,会抛出“ReadOnlyErrorDOMException; 如果在事务不是活动的时调用,会抛出“TransactionInactiveErrorDOMException
request = store . put(value [, key])
request = store . add(value [, key])

使用给定的 valuekeystore 中添加或更新一条记录

如果存储使用内联键并且指定了 key,将会抛出“DataErrorDOMException

如果使用 put(), 任何具有该的现有记录都将被替换。如果使用 add(), 并且具有该记录已经存在,则 request 将失败,requesterror 将被设置为“ConstraintErrorDOMException

如果成功,requestresult 将是记录

request = store . delete(query)

删除 store 中具有给定或在 query 中给定键范围内的记录

如果成功,requestresult 将是 undefined

request = store . clear()

删除 store 中的所有记录

如果成功,requestresult 将是 undefined

put(value, key) 方法的步骤是 返回使用此对象valuekeyno-overwrite flag 为 false 运行添加或放置的结果。

add(value, key) 方法的步骤是 返回使用此对象valuekeyno-overwrite flag 为 true 运行添加或放置的结果。

要使用 handlevaluekeyno-overwrite flag 添加或放置,请运行以下步骤:

  1. transactionhandle事务

  2. storehandle对象存储

  3. 如果 store 已被删除, 则抛出 一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. 如果 transaction 是一个只读事务, 则抛出 一个“ReadOnlyErrorDOMException

  6. 如果 store 使用内联键并且给定了 key, 则抛出 一个“DataErrorDOMException

  7. 如果 store 使用外联键且没有键生成器 并且未给定 key,则抛出一个 “DataErrorDOMException

  8. 如果给定了 key,则:

    1. r 为使用 key 将值转换为键的结果。重新抛出任何异常。

    2. 如果 r 是“无效值”或“无效类型”,则抛出 一个“DataErrorDOMException

    3. keyr

  9. targetRealm 为用户代理定义的领域

  10. clone 为在 transaction 期间于 targetRealm 中对 value克隆。 重新抛出任何异常。

    为什么要创建值的副本? 值在存储时被序列化。在此处将其视为副本 允许本规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察, 实现可以对此进行优化。
  11. 如果 store 使用内联键,则:

    1. kpk 为使用 clonestore键路径从值中提取键的结果。重新抛出任何 异常。

    2. 如果 kpk 无效,则抛出 一个“DataErrorDOMException

    3. 如果 kpk 不是失败,则令 keykpk

    4. 否则(kpk 是失败):

      1. 如果 store 没有键生成器,则抛出 一个“DataErrorDOMException

      2. 如果使用 clonestore键路径检查键是否可以注入值中返回 false,则抛出一个“DataErrorDOMException

  12. operation 为一个算法,用于使用 storeclonekeyno-overwrite flag 运行将记录存储到对象存储中

  13. 返回使用 handleoperation 运行异步执行请求的结果(一个 IDBRequest)。

delete(query) 方法的步骤是:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. 如果 transaction 是一个只读事务, 则抛出 一个“ReadOnlyErrorDOMException

  6. range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  7. operation 为一个算法,用于使用 storerange 运行从对象存储中删除记录

  8. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用于标识要删除的记录

注意: 与其他接受键或键范围的方法不同,此方法允许将 null 作为键给出。这是为了 减少一个小错误导致清空整个对象存储的风险。

clear() 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. 如果 transaction 是一个只读事务, 则抛出 一个“ReadOnlyErrorDOMException

  6. operation 为一个算法,用于使用 store 运行清除对象存储

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

如果在事务 不是活动的时调用,以下方法会抛出“TransactionInactiveErrorDOMException
request = store . get(query)

检索与 query 中给定的键范围匹配的第一个记录

如果成功,requestresult 将是该,如果没有匹配的记录,则为 undefined

request = store . getKey(query)

检索与 query 中给定的键范围匹配的第一个记录

如果成功,requestresult 将是该,如果没有匹配的记录,则为 undefined

request = store . getAll(query [, count])
request = store . getAll({query, count, direction})

检索与 query 中给定的键范围匹配的记录(如果给定,最多 count 条)。 将 direction 选项设置为“next” 以检索前 count 个值,或设置为“prev” 以返回后 count 个值。

如果成功,requestresult 将是一个包含Array

request = store . getAllKeys(query [, count])
request = store . getAllKeys({query, count, direction})

检索与 query 中给定的键范围匹配的记录(如果给定,最多 count 个)。 将 direction 选项设置为“next” 以检索前 count 个键,或设置为“prev” 以返回后 count 个键。

如果成功,requestresult 将是一个包含Array

request = store . getAllRecords({query, count, direction})

检索记录

query 选项指定要匹配的键范围count 选项限制匹配的记录数。 将 direction 选项设置为“next” 以检索前 count 条记录,或设置为“prev” 以返回后 count 条记录。

如果成功,requestresult 将是一个 Array, 每个成员都是一个 IDBRecord

request = store . count(query)

检索与 query 中给定的键范围匹配的记录数量。

如果成功,requestresult 将是该计数。

get(query) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. operation 为一个算法,用于使用当前领域记录storerange 运行从对象存储中检索值

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用于标识要检索的记录值。如果 指定了范围,该方法将检索该范围内的第一个现有值。

注意: 如果具有给定键的记录不存在,或者记录存在但其值为 undefined,此方法将产生相同的结果。 如果你需要区分这两种情况,你可以使用 openCursor() 并传入相同的键。如果记录存在,这将返回一个值为 undefined 的游标;如果 不存在这样的记录,则不返回游标。

getKey(query) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. operation 为一个算法,用于使用 storerange 运行从对象存储中检索键

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用于标识要检索的记录键。如果指定了范围,该方法将检索 该范围内的第一个现有键。

getAll(queryOrOptions, count) 方法的步骤如下:

  1. 返回使用当前领域记录此对象、“value”、 queryOrOptions 以及(如果给定)count 创建检索多个项目的请求的结果。 重新抛出任何异常。

getAllKeys(queryOrOptions, count) 方法的步骤如下:

  1. 返回使用当前领域记录此对象、“key”、 queryOrOptions 以及(如果给定)count 创建检索多个项目的请求的结果。 重新抛出任何异常。

getAllRecords(options) 方法的 步骤如下:

  1. 返回使用当前领域记录此对象、“record” 和 options 创建检索多个项目的请求的结果。 重新抛出任何异常。

count(query) 方法的 步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. operation 为一个算法,用于使用 storerange 运行计算范围内的记录数

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用于标识要计数的记录。如果为 null 或未给出,则使用无界键范围

如果在事务 不是活动的时调用,以下方法会抛出“TransactionInactiveErrorDOMException
request = store . openCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标, 按 direction 排序。如果 query 为 null,则匹配 store 中的所有记录

如果成功,requestresult 将是一个 IDBCursorWithValue, 指向第一个匹配的记录,如果没有匹配的记录,则为 null。

request = store . openKeyCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标,并将仅键标志设置为 true, 按 direction 排序。如果 query 为 null,则匹配 store 中的所有记录

如果成功,requestresult 将是一个 IDBCursor, 指向第一个匹配的记录,如果没有匹配的记录,则为 null。

openCursor(query, direction) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. cursor 为一个新的游标, 其源句柄设置为此对象位置为 undefined, 方向 设置为 direction已获取值标志设置为 false, 为 undefined, 范围设置为 range,以及 仅键标志设置为 false。

  7. operation 为一个算法,用于使用当前领域记录cursor 运行迭代游标

  8. request 为使用此对象operation 运行异步执行请求的结果。

  9. cursor请求设置为 request

  10. 返回 request

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用作游标范围。 如果为 null 或未给出,则使用无界键范围

openKeyCursor(query, direction) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. cursor 为一个新的游标, 其源句柄设置为此对象位置为 undefined, 方向 设置为 direction已获取值标志设置为 false, 为 undefined, 范围设置为 range,以及 仅键标志设置为 true。

  7. operation 为一个算法,用于使用当前领域记录cursor 运行迭代游标

  8. request 为使用此对象operation 运行异步执行请求的结果。

  9. cursor请求设置为 request

  10. 返回 request

注意: query 参数可以是一个键范围(一个 IDBKeyRange), 用作游标范围。如果为 null 或未给出,则使用无界键范围

index = store . index(name)

返回 store 中名为 name索引IDBIndex

index = store . createIndex(name, keyPath [, options])

store 中使用给定的 namekeyPathoptions 创建一个新的索引,并返回一个新的 IDBIndex。 如果 keyPathoptions 定义的约束无法 满足 store 中已有的数据,则升级事务将 以“ConstraintErrorDOMException 中止

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

store . deleteIndex(name)

删除 store 中名为 name索引

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

createIndex(name, keyPath, options) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 transaction 不是一个升级事务, 则抛出 一个“InvalidStateErrorDOMException

  4. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  5. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  6. 如果 store 中已存在一个名为 name索引,则抛出一个 “ConstraintErrorDOMException

  7. 如果 keyPath 不是一个有效的键路径,则抛出 一个“SyntaxErrorDOMException

  8. uniqueoptionsunique 成员。

  9. multiEntryoptionsmultiEntry 成员。

  10. 如果 keyPath 是一个序列且 multiEntry 为 true,则抛出一个“InvalidAccessErrorDOMException

  11. indexstore 中的一个新索引。 将 index名称设置为 name键路径设置为 keyPath唯一标志设置为 unique,以及 多条目标记设置为 multiEntry

  12. index 添加到对象的索引集中。

  13. 返回一个与 index此对象关联的新索引句柄

此方法在对象存储中创建并返回一个具有 给定名称的新索引。 请注意,此方法必须仅在升级事务中调用。

请求创建的索引可以包含对该索引的引用对象存储中允许的数据的约束,例如 要求索引的键路径引用的值是唯一的。如果引用的对象存储 已包含违反这些约束的数据,这不得导致 createIndex() 的实现抛出异常或影响其返回值。实现仍必须 创建并返回一个 IDBIndex 对象, 并且实现必须将数据库任务排队以中止用于 createIndex() 调用的升级事务

此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。 尽管此方法不返回 IDBRequest 对象,但索引创建本身作为升级事务内的异步请求进行处理。

在某些实现中,实现可能会在 createIndex 方法返回后异步地遇到创建索引的问题。例如,在将有关新创建索引的元数据排队以异步插入数据库的实现中,或者在实现可能需要出于配额原因请求用户许可的情况下。此类 实现仍必须创建并返回一个 IDBIndex 对象, 并且一旦实现确定创建索引失败,它必须运行中止事务的步骤,并使用适当的错误。例如, 如果由于配额原因创建索引失败, 则必须使用“QuotaExceededErrorDOMException 作为错误;如果由于唯一标志约束而无法创建索引, 则必须使用“ConstraintErrorDOMException 作为错误。

索引的异步创建可以在以下示例中观察到:

const request1 = objectStore.put({name: "betty"}, 1);
    const request2 = objectStore.put({name: "betty"}, 2);
    const index = objectStore.createIndex("by_name", "name", {unique: true});
    

在调用 createIndex() 时,两个请求都尚未执行。当第二个请求执行时, 会创建一个重复的名称。由于索引创建被视为异步请求,索引的唯一性约束 不会导致第二个请求失败。相反,当 创建索引并且约束失败时,事务将 被中止

index(name) 方法的步骤如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果 transaction状态已完成, 则抛出一个“InvalidStateErrorDOMException

  5. index对象的 索引集名为 name索引(如果存在),否则抛出 一个“NotFoundErrorDOMException

  6. 返回一个与 index此对象关联的索引句柄

注意: 在同一个 IDBObjectStore 实例上使用相同名称对此方法的每次调用都会返回相同的 IDBIndex 实例。

注意: 返回的 IDBIndex 实例特定于此 IDBObjectStore 实例。如果在 不同的 IDBObjectStore 实例上使用相同名称调用此方法,则会返回 不同的 IDBIndex 实例。

deleteIndex(name) 方法的步骤 如下:

  1. transaction对象的事务

  2. store对象的对象存储

  3. 如果 transaction 不是一个升级事务, 则抛出 一个“InvalidStateErrorDOMException

  4. 如果 store 已被删除,则抛出一个 “InvalidStateErrorDOMException

  5. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  6. indexstore名为 name索引(如果存在),否则抛出一个“NotFoundErrorDOMException

  7. 对象的索引集中移除 index

  8. 销毁 index

此方法销毁对象存储中具有 给定名称的索引。请注意,此方法 必须仅在升级事务中调用。

此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。 尽管此方法不返回 IDBRequest 对象,但索引销毁本身作为升级事务内的异步请求进行处理。

4.6. IDBIndex 接口

IDBIndex 接口表示一个索引句柄

[Exposed=(Window,Worker)]
    interface IDBIndex {
      attribute DOMString name;
      [SameObject] readonly attribute IDBObjectStore objectStore;
      readonly attribute any keyPath;
      readonly attribute boolean multiEntry;
      readonly attribute boolean unique;
    
      [NewObject] IDBRequest get(any query);
      [NewObject] IDBRequest getKey(any query);
      [NewObject] IDBRequest getAll(optional any queryOrOptions,
                                    optional [EnforceRange] unsigned long count);
      [NewObject] IDBRequest getAllKeys(optional any queryOrOptions,
                                        optional [EnforceRange] unsigned long count);
      [NewObject] IDBRequest getAllRecords(optional IDBGetAllOptions options = {});
      [NewObject] IDBRequest count(optional any query);
    
      [NewObject] IDBRequest openCursor(optional any query,
                                        optional IDBCursorDirection direction = "next");
      [NewObject] IDBRequest openKeyCursor(optional any query,
                                           optional IDBCursorDirection direction = "next");
    };
    
index . name

返回索引的名称

index . name = newName

将存储的名称更新为 newName

如果不是在升级事务中调用,则抛出“InvalidStateErrorDOMException

index . objectStore

返回索引所属的 IDBObjectStore

index . keyPath

返回索引的键路径

index . multiEntry

如果索引的multiEntry 标志为 true,则返回 true。

index . unique

如果索引的unique 标志为 true,则返回 true。

name getter 的步骤是 返回对象的名称

这与索引名称相同吗? 只要事务尚未完成, 这与关联的索引名称相同。但是一旦事务 完成,此 属性将不会反映后续升级事务所做的更改。

name setter 的步骤如下:

  1. name给定的值

  2. transaction对象的事务

  3. index对象的索引

  4. 如果 transaction 不是一个升级事务, 则抛出 一个“InvalidStateErrorDOMException

  5. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  6. 如果 indexindex对象存储已 被删除,则抛出一个“InvalidStateErrorDOMException

  7. 如果 index名称 等于 name,则终止这些步骤。

  8. 如果 index对象存储中已存在一个名为 name索引,则抛出一个“ConstraintErrorDOMException

  9. index名称设置为 name

  10. 对象的名称设置为 name

objectStore getter 的步骤是 返回对象的对象存储句柄

keyPath getter 的步骤是 返回对象的索引键路径键路径根据[WEBIDL] 被转换为DOMString (如果是字符串)或 sequence<DOMString> (如果是字符串列表)。

注意: 返回的值与创建索引时使用的实例不同。但是, 如果此属性返回一个对象(特别是Array), 则每次检查时它都会返回相同的对象实例。更改对象的属性对索引没有影响。

multiEntry getter 的步骤是 返回对象的索引multiEntry 标志

unique getter 的步骤是 返回对象的索引unique 标志

如果在事务 不是活动的时调用,以下方法会抛出“TransactionInactiveErrorDOMException
request = index . get(query)

检索与 query 中给定的键范围匹配的第一个记录

如果成功,requestresult 将是,如果 没有匹配的记录,则为 undefined

request = index . getKey(query)

检索与 query 中给定的键范围匹配的第一个记录

如果成功,requestresult 将是,如果 没有匹配的记录,则为 undefined

request = index . getAll(query [, count])
request = index . getAll({query, count, direction})

检索与 query 中给定的键范围匹配的记录(如果 给定,则最多为 count 个)。 将 direction 选项设置为“next” 以检索前 count 个值,设置为“prev” 以返回后 count 个值。 将 direction 选项设置为“nextunique” 或“prevunique” 以在检索到具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,requestresult 将是一个Array

request = index . getAllKeys(query [, count])
request = index . getAllKeys({query, count, direction})

检索与 query 中给定的键范围匹配的记录(如果 给定,则最多为 count 个)。 将 direction 选项设置为“next” 以检索前 count 个键,设置为“prev” 以返回后 count 个键。 将 direction 选项设置为“nextunique” 或“prevunique” 以在检索到具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,requestresult 将是一个Array

request = index . getAllRecords({query, count, direction})

检索记录和索引

query 选项指定要匹配的键范围count 选项限制匹配的记录数。 将 direction 选项设置为“next” 以检索前 count 条记录,设置为“prev” 以返回后 count 条记录。 将 direction 选项设置为“nextunique” 或“prevunique” 以在检索到具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,requestresult 将是一个 Array, 每个成员都是一个 IDBRecord。 使用 IDBRecord 的 key 获取记录的索引。使用 IDBRecord 的 primaryKey 获取记录的

request = index . count(query)

检索与 query 中给定的键范围匹配的记录数。

如果成功,requestresult 将是 计数。

get(query) 方法的步骤如下:

  1. transaction对象的事务

  2. index对象的索引

  3. 如果 indexindex对象存储已 被删除,则抛出一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. operation 为一个算法,用于使用当前 Realm 记录indexrange 运行从索引中检索引用值

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是键范围(一个 IDBKeyRange), 用于标识要检索的引用值。如果 指定了范围,该方法将检索该范围内的第一个现有记录。

注意: 当具有给定键的记录不存在时,此方法产生的结果与记录存在但其值为 undefined 时相同。 如果需要区分这两种情况,可以使用 openCursor() 并使用相同的键。如果记录存在,这将返回一个值为 undefined 的游标,如果 不存在这样的记录,则不返回游标。

getKey(query) 方法的步骤如下:

  1. transaction对象的事务

  2. index对象的索引

  3. 如果 indexindex对象存储已被删除, 则抛出 一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. operation 为一个算法,用于使用 indexrange 运行从索引中检索值

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是键范围(一个 IDBKeyRange), 用于标识要检索的记录键。 如果指定了范围,该方法将检索该范围内的第一个现有键。

getAll(queryOrOptions, count) 方法的步骤如下:

  1. 返回使用当前 Realm 记录此对象、"值"、 queryOrOptions 和(如果给定)count 创建检索多个项目的请求的结果。 重新抛出任何异常。

getAllKeys(queryOrOptions, count) 方法的步骤如下:

  1. 返回使用当前 Realm 记录此对象、"键"、 queryOrOptions 和(如果给定)count 创建检索多个项目的请求的结果。 重新抛出任何异常。

getAllRecords(options) 方法的步骤 如下:

  1. 返回使用当前 Realm 记录此对象、"记录" 和 options 创建检索多个项目的请求的结果。 重新抛出任何异常。

count(query) 方法的步骤如下:

  1. transaction对象的事务

  2. index对象的索引

  3. 如果 indexindex对象存储已 被删除,则抛出一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. operation 为一个算法,用于使用 indexrange 运行计算范围内的记录数

  7. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是键范围(一个 IDBKeyRange), 用于标识要计数的索引记录。如果为 null 或未 给出,则使用无界键范围

如果在事务 不是活动的时调用,以下方法会抛出“TransactionInactiveErrorDOMException
request = index . openCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标, 按 direction 排序。如果 query 为 null,则匹配 index 中的所有记录

如果成功,requestresult 将是一个 IDBCursorWithValue, 如果没有匹配的记录,则为 null。

request = index . openKeyCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标, 并将仅键标志设置为 true, 按 direction 排序。如果 query 为 null,则匹配 index 中的所有记录

如果成功,requestresult 将是一个 IDBCursor, 如果没有匹配的记录,则为 null。

openCursor(query, direction) 方法的步骤如下:

  1. transaction对象的事务

  2. index对象的索引

  3. 如果 indexindex对象存储已被删除, 则抛出 一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. cursor 为一个新的游标, 其源句柄设置为此对象, 未定义的位置方向 设置为 directiongot value 标志设置为 false, 未定义的范围设置为 range,以及 仅键标志设置为 false。

  7. operation 为一个算法,用于使用当前 Realm 记录cursor 运行迭代游标

  8. request 为使用此对象operation 运行异步执行请求的结果。

  9. cursor请求设置为 request

  10. 返回 request

注意: query 参数可以是键范围(一个 IDBKeyRange), 用作游标范围。如果为 null 或未给出,则使用无界键范围

openKeyCursor(query, direction) 方法的步骤如下:

  1. transaction对象的事务

  2. index对象的索引

  3. 如果 indexindex对象存储已 被删除,则抛出一个“InvalidStateErrorDOMException

  4. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  5. range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. cursor 为一个新的游标, 其源句柄设置为此对象, 未定义的位置方向 设置为 directiongot value 标志设置为 false, 未定义的范围设置为 range,以及 仅键标志设置为 true。

  7. operation 为一个算法,用于使用当前 Realm 记录cursor 运行迭代游标

  8. request 为使用此对象operation 运行异步执行请求的结果。

  9. cursor请求设置为 request

  10. 返回 request

注意: query 参数可以是键范围(一个 IDBKeyRange), 用作游标范围。如果为 null 或未给出,则使用无界键范围

4.7. IDBKeyRange 接口

IDBKeyRange 接口表示一个键范围

[Exposed=(Window,Worker)]
interface IDBKeyRange {
  readonly attribute any lower;
  readonly attribute any upper;
  readonly attribute boolean lowerOpen;
  readonly attribute boolean upperOpen;

  // Static construction methods:
  [NewObject] static IDBKeyRange only(any value);
  [NewObject] static IDBKeyRange lowerBound(any lower, optional boolean open = false);
  [NewObject] static IDBKeyRange upperBound(any upper, optional boolean open = false);
  [NewObject] static IDBKeyRange bound(any lower,
                                       any upper,
                                       optional boolean lowerOpen = false,
                                       optional boolean upperOpen = false);

  boolean includes(any key);
};
range . lower

返回范围的下界,如果没有则返回 undefined

range . upper

返回范围的上界,如果没有则返回 undefined

range . lowerOpen

返回范围的下界开放标志

range . upperOpen

返回范围的上界开放标志

lower getter 的步骤是 返回使用对象的下界 将键转换为值的结果(如果它不为 null),否则返回 undefined。

upper getter 的步骤是 返回使用对象的上界将键转换为值的结果(如果它不为 null),否则返回 undefined。

lowerOpen getter 的步骤是 返回对象的下界 开放标志

upperOpen getter 的步骤是 返回对象的上界 开放标志

range = IDBKeyRange . only(key)

返回一个仅包含 key 的新 IDBKeyRange

range = IDBKeyRange . lowerBound(key [, open = false])

返回一个从 key 开始且没有上界的新 IDBKeyRange。如果 open 为 true,则 key 不包含在范围内。

range = IDBKeyRange . upperBound(key [, open = false])

返回一个没有下界且在 key 结束的新 IDBKeyRange。如果 open 为 true,则 key 不包含在范围内。

range = IDBKeyRange . bound(lower, upper [, lowerOpen = false [, upperOpen = false]])

返回一个从 lowerupper 的新 IDBKeyRange。 如果 lowerOpen 为 true,则 lower 不包含在范围内。 如果 upperOpen 为 true,则 upper 不包含在范围内。

only(value) 方法的步骤如下:

  1. key 为使用 value 将值转换为键的结果。重新抛出任何异常。

  2. 如果 key 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  3. 创建并返回一个仅包含 key 的新键范围

lowerBound(lower, open) 方法的步骤如下:

  1. lowerKey 为使用 lower 将值转换为键的结果。重新抛出任何异常。

  2. 如果 lowerKey 无效,则抛出一个“DataErrorDOMException

  3. 创建并返回一个新键范围,其下界 设置为 lowerKey下界开放标志设置为 open上界设置为 null,上界开放标志 设置为 true。

upperBound(upper, open) 方法的步骤如下:

  1. upperKey 为使用 upper 将值转换为键的结果。重新抛出任何异常。

  2. 如果 upperKey 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  3. 创建并返回一个新键范围,其下界 设置为 null,下界开放标志设置为 true,上界设置为 upperKey上界开放标志设置为 open

bound(lower, upper, lowerOpen, upperOpen) 方法的步骤如下:

  1. lowerKey 为使用 lower 将值转换为键的结果。重新抛出任何异常。

  2. 如果 lowerKey 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  3. upperKey 为使用 upper 将值转换为键的结果。重新抛出任何异常。

  4. 如果 upperKey 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  5. 如果 lowerKey 大于 upperKey,则抛出一个 “DataErrorDOMException

  6. 创建并返回一个新键范围,其下界 设置为 lowerKey下界开放标志设置为 lowerOpen上界设置为 upperKey上界开放标志设置为 upperOpen

range . includes(key)

如果 key 包含在范围内,则返回 true,否则返回 false。

includes(key) 方法的步骤如下:

  1. k 为使用 key 将值转换为键的结果。 重新抛出任何异常。

  2. 如果 k 是“无效值”或“无效类型”,则抛出一个“DataErrorDOMException

  3. 如果 k 此范围内,则返回 true,否则返回 false。

4.8. IDBRecord 接口

IDBRecord 接口表示一个记录快照

[Exposed=(Window,Worker)]
interface IDBRecord {
  readonly attribute any key;
  readonly attribute any primaryKey;
  readonly attribute any value;
};
record . key

返回记录的

record . primaryKey

如果记录是从索引中检索的,则返回该记录在索引的引用对象存储中的键。

如果记录是从对象存储中检索的,则返回记录的,该键与 record.key 相同。

record . value

返回记录的

key getter 的步骤是返回使用对象的将键转换为值的结果。

primaryKey getter 的步骤是返回使用对象的主键将键转换为值的结果。

value getter 的步骤是返回对象的

4.9. IDBCursor 接口

游标对象实现了 IDBCursor 接口。对于给定的游标,只有一个 IDBCursor 实例来表示它。 可以同时使用的游标数量没有限制。

[Exposed=(Window,Worker)]
interface IDBCursor {
  readonly attribute (IDBObjectStore or IDBIndex) source;
  readonly attribute IDBCursorDirection direction;
  readonly attribute any key;
  readonly attribute any primaryKey;
  [SameObject] readonly attribute IDBRequest request;

  undefined advance([EnforceRange] unsigned long count);
  undefined continue(optional any key);
  undefined continuePrimaryKey(any key, any primaryKey);

  [NewObject] IDBRequest update(any value);
  [NewObject] IDBRequest delete();
};

enum IDBCursorDirection {
  "next",
  "nextunique",
  "prev",
  "prevunique"
};
cursor . source

返回游标打开自的 IDBObjectStoreIDBIndex

cursor . direction

返回游标的方向 (“next”、 “nextunique”、 “prev” 或“prevunique”)。

cursor . key

返回游标的。 如果游标正在前进或已完成,则抛出“InvalidStateErrorDOMException

cursor . primaryKey

返回游标的有效键。 如果游标正在前进或已完成,则抛出“InvalidStateErrorDOMException

cursor . request

返回用于获取此游标的请求

source getter 的步骤是 返回对象的 源句柄

注意: source 属性永远不会返回 null 或抛出异常,即使 游标当前正在迭代、已迭代超过其末尾,或者其 事务 不是活动的

direction getter 的步骤是 返回对象的 方向

key getter 的步骤是 返回使用游标的当前将键转换为值的结果。

注意: 如果 key 返回一个对象(例如 DateArray), 则每次检查它时都会返回相同的对象实例,直到游标的 被更改。这意味着如果该对象被修改,任何检查游标值的人都会看到这些修改。 但是,修改这样的对象不会修改数据库的内容。

primaryKey getter 的步骤是 返回使用游标的当前有效键将键转换为值的结果。

注意: 如果 primaryKey 返回一个对象(例如 DateArray), 则每次检查它时都会返回相同的对象实例,直到游标的有效键被更改。 这意味着如果该对象被修改,任何检查游标值的人都会看到这些修改。 但是,修改这样的对象不会修改数据库的内容。

request getter 的步骤是 返回对象的 请求

🚧 request 属性是此版本中的新增功能。 Chrome 76、Edge 79、Firefox 77 和 Safari 15 支持它。 🚧
以下方法会前进一个游标。 一旦游标前进,将在打开游标时返回的同一个 IDBRequest 上触发一个 success 事件。如果范围内有记录,则 result 将是同一个游标,否则为 undefined

如果在游标已在前进时调用,将抛出“InvalidStateErrorDOMException

如果在事务 不是活动的时调用,以下方法会抛出“TransactionInactiveErrorDOMException

cursor . advance(count)

将游标在范围内前进接下来的 count记录

cursor . continue()

将游标前进到范围内的下一条记录

cursor . continue(key)

将游标前进到范围内匹配或在 key 之后的下一条记录

cursor . continuePrimaryKey(key, primaryKey)

将游标前进到范围内匹配或在 keyprimaryKey 之后的下一条记录。如果不是索引,则抛出“InvalidAccessErrorDOMException

advance(count) 方法的步骤如下:

  1. 如果 count 为 0(零),则抛出一个 TypeError

  2. transaction对象的事务

  3. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  4. 如果对象的有效对象存储已被删除,则抛出一个 “InvalidStateErrorDOMException

  5. 如果对象的已获取值标志 为 false,表示游标正在迭代或已迭代超过其末尾, 则抛出 一个“InvalidStateErrorDOMException

  6. 对象的已获取值标志 设置为 false。

  7. request对象的请求

  8. request已处理标志设置为 false。

  9. request已完成标志设置为 false。

  10. operation 为一个算法,用于使用当前 Realm 记录此对象count 运行迭代游标

  11. 使用对象的源句柄operationrequest 运行异步执行请求

注意: 在加载新游标数据之前多次调用此方法 - 例如,从同一个 onsuccess 处理程序中调用两次 advance() - 会导致在第二次调用时抛出“InvalidStateErrorDOMException, 因为游标的已获取值标志 已被设置为 false。

continue(key) 方法的步骤如下:

  1. transaction对象的事务

  2. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  3. 如果对象的有效对象存储已被删除,则抛出 一个 “InvalidStateErrorDOMException

  4. 如果对象的已获取值标志 为 false,表示游标正在迭代或已迭代超过其末尾, 则抛出 一个“InvalidStateErrorDOMException

  5. 如果给定了 key,则:

    1. r 为使用 key 将值转换为键的结果。重新抛出任何异常。

    2. 如果 r 是“无效值”或“无效类型”,则抛出 一个“DataErrorDOMException

    3. keyr

    4. 如果 key 小于等于对象的 位置对象的方向是 “next” 或“nextunique”, 则抛出一个“DataErrorDOMException

    5. 如果 key 大于等于对象的 位置对象的方向是 “prev” 或“prevunique”, 则抛出一个“DataErrorDOMException

  6. 对象的已获取值标志 设置为 false。

  7. request对象的请求

  8. request已处理标志设置为 false。

  9. request已完成标志设置为 false。

  10. operation 为一个算法,用于使用当前 Realm 记录此对象key(如果给定)运行迭代游标

  11. 使用对象的源句柄operationrequest 运行异步执行请求

注意: 在加载新游标数据之前多次调用此方法 - 例如,从同一个 onsuccess 处理程序中调用两次 continue() - 会导致在第二次调用时抛出“InvalidStateErrorDOMException, 因为游标的已获取值标志 已被设置为 false。

continuePrimaryKey(key, primaryKey) 方法的步骤如下:

  1. transaction对象的事务

  2. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  3. 如果对象的有效对象存储已被删除,则抛出一个 “InvalidStateErrorDOMException

  4. 如果对象的不是索引,则抛出一个“InvalidAccessErrorDOMException

  5. 如果对象的方向不是“next” 或“prev”, 则抛出 一个“InvalidAccessErrorDOMException

  6. 如果对象的已获取值标志 为 false,表示游标正在迭代或已迭代超过其末尾, 则抛出 一个“InvalidStateErrorDOMException

  7. r 为使用 key 将值转换为键的结果。 重新抛出任何异常。

  8. 如果 r 是“无效值”或“无效类型”,则抛出一个 “DataErrorDOMException

  9. keyr

  10. r 为使用 primaryKey 将值转换为键的结果。重新抛出任何异常。

  11. 如果 r 是“无效值”或“无效类型”,则抛出一个 “DataErrorDOMException

  12. primaryKeyr

  13. 如果 key 小于对象的 位置对象的方向是“next”, 则抛出 一个“DataErrorDOMException

  14. 如果 key 大于对象的 位置对象的方向是“prev”, 则抛出 一个“DataErrorDOMException

  15. 如果 key 等于 对象的位置primaryKey 小于等于对象的 对象存储位置对象的方向是 “next”, 则抛出 一个“DataErrorDOMException

  16. 如果 key 等于 对象的位置primaryKey 大于等于对象的 对象存储位置对象的 方向 是“prev”, 则抛出 一个 “DataErrorDOMException

  17. 对象的已获取值标志 设置为 false。

  18. request对象的请求

  19. request已处理标志设置为 false。

  20. request已完成标志设置为 false。

  21. operation 为一个算法,用于使用当前 Realm 记录此对象keyprimaryKey 运行迭代游标

  22. 使用对象的源句柄operationrequest 运行异步执行请求

注意: 在加载新游标数据之前多次调用此方法 - 例如,从同一个 onsuccess 处理程序中调用两次 continuePrimaryKey() - 会导致在第二次调用时抛出“InvalidStateErrorDOMException, 因为游标的已获取值标志已被设置为 false。

如果在只读事务中调用,以下方法会抛出“ReadOnlyErrorDOMException; 如果在事务不是活动的时调用,则会抛出“TransactionInactiveErrorDOMException
request = cursor . update(value)

用新值更新游标指向的记录

如果有效对象存储 使用内联键并且会发生改变,则抛出“DataErrorDOMException

如果成功,requestresult 将是记录

request = cursor . delete()

删除游标指向的记录

如果成功,requestresult 将是 undefined

update(value) 方法的步骤如下:

  1. transaction对象的事务

  2. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  3. 如果 transaction 是一个只读事务,则抛出一个 “ReadOnlyErrorDOMException

  4. 如果对象的有效对象存储已被删除,则抛出一个 “InvalidStateErrorDOMException

  5. 如果对象的已获取值标志 为 false,表示游标正在迭代或已迭代超过其末尾, 则抛出 一个“InvalidStateErrorDOMException

  6. 如果对象的仅键标志为 true,则抛出一个 “InvalidStateErrorDOMException

  7. targetRealm 为用户代理定义的Realm

  8. clone 为在 transaction 期间于 targetRealm 中对 value克隆。 重新抛出任何异常。

    为什么要创建值的副本? 值在存储时被序列化。此处将其视为副本 允许本规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察, 实现可以对此进行优化。
  9. 如果对象的有效对象存储使用内联键,则:

    1. kpk 为使用 clone对象的有效对象存储键路径 使用键路径从值中提取键的结果。 重新抛出任何异常。

    2. 如果 kpk 是失败、无效或不等于 对象的有效键,则抛出 一个 “DataErrorDOMException

  10. operation 为一个算法,用于使用对象的有效对象存储clone对象的有效键和 false 运行将记录存储到对象存储中

  11. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: 将记录存储到对象存储中的一个结果是,如果 记录自游标移动到它之后已被删除,则将创建一个新记录。

delete() 方法的步骤如下:

  1. transaction对象的事务

  2. 如果 transaction状态不是活动的, 则抛出一个“TransactionInactiveErrorDOMException

  3. 如果 transaction 是一个只读事务,则抛出一个 “ReadOnlyErrorDOMException

  4. 如果对象的有效对象存储已被删除,则抛出一个 “InvalidStateErrorDOMException

  5. 如果对象的已获取值标志 为 false,表示游标正在迭代或已迭代超过其末尾, 则抛出 一个“InvalidStateErrorDOMException

  6. 如果对象的仅键标志为 true,则抛出一个 “InvalidStateErrorDOMException

  7. operation 为一个算法,用于使用对象的有效对象存储对象的有效键运行从对象存储中删除记录

  8. 返回使用此对象operation 运行异步执行请求的结果(一个 IDBRequest)。

一个游标,其仅键标志 设置为 false,也实现了 IDBCursorWithValue 接口。

[Exposed=(Window,Worker)]
interface IDBCursorWithValue : IDBCursor {
  readonly attribute any value;
};
cursor . value

返回游标的当前

value getter 的步骤是 返回对象的 当前

注意: 如果 value 返回一个对象,则每次检查它时都会返回相同的对象实例,直到游标的 被更改。这意味着 如果该对象被修改,任何检查游标值的人都会看到这些修改。 但是,修改这样的对象不会修改数据库的内容。

4.10. IDBTransaction 接口

事务 对象实现以下接口:

[Exposed=(Window,Worker)]
    interface IDBTransaction : EventTarget {
      readonly attribute DOMStringList objectStoreNames;
      readonly attribute IDBTransactionMode mode;
      readonly attribute IDBTransactionDurability durability;
      [SameObject] readonly attribute IDBDatabase db;
      readonly attribute DOMException? error;
    
      IDBObjectStore objectStore(DOMString name);
      undefined commit();
      undefined abort();
    
      // 事件处理程序:
      attribute EventHandler onabort;
      attribute EventHandler oncomplete;
      attribute EventHandler onerror;
    };
    
    enum IDBTransactionMode {
      "readonly",
      "readwrite",
      "versionchange"
    };
    
transaction . objectStoreNames

返回事务作用域对象存储的名称列表。 对于升级事务, 这是数据库中的所有对象存储。

transaction . mode

返回创建事务时使用的模式 (“readonly” 或“readwrite”), 或对于升级事务 为“versionchange”。

transaction . durability

返回创建事务时使用的持久性提示 (“strict”、 “relaxed” 或“default”)。

transaction . db

返回事务的连接

transaction . error

如果事务被中止,则返回提供原因的 错误(一个 DOMException)。

objectStoreNames getter 的步骤是:

  1. names对象的作用域对象存储名称列表

  2. 返回使用 names 创建排序名称列表的结果(一个 DOMStringList)。

注意: 此属性返回的每个列表的内容不会改变, 但在升级事务期间对此属性的后续调用可以返回具有不同内容的列表, 因为对象存储被创建和删除。

mode getter 的步骤是 返回对象的 模式

durability getter 的步骤是返回对象的持久性提示

🚧 durability 属性是此版本中的新增功能。 Chrome 82、Edge 82、Firefox 126 和 Safari 15 支持它。 🚧

db getter 的步骤是 返回对象的 连接的 关联数据库

error getter 的步骤是 返回对象的 错误,如果没有则返回 null。

注意: 如果此事务 由于失败的请求而被中止, 这将与请求错误相同。如果此事务 由于事件处理程序中未捕获的异常而被中止,则错误将是 一个“AbortErrorDOMException。 如果事务 由于提交时出错而被中止,它将反映失败的原因 (例如“QuotaExceededError”、 “ConstraintError” 或 “UnknownErrorDOMException)。

transaction . objectStore(name)

返回事务作用域中的一个 IDBObjectStore

transaction . abort()

中止事务。所有待处理的请求都将失败,并带有 一个“AbortErrorDOMException, 并且对数据库所做的所有更改都将被还原。

transaction . commit()

尝试提交事务。所有待处理的请求都将被允许完成, 但不会接受新的请求。这可用于强制事务快速完成, 而无需等待待处理的请求在正常尝试提交之前触发 success 事件。

如果待处理的请求失败(例如由于约束错误),事务将中止。 成功请求的 success 事件仍将触发,但在事件处理程序中抛出异常不会中止事务。 同样,失败请求的 error 事件仍将触发,但调用 preventDefault() 不会阻止事务中止。

objectStore(name) 方法的步骤是:

  1. 如果对象的状态已完成, 则抛出一个“InvalidStateErrorDOMException

  2. store对象的 作用域名为 name对象存储, 如果不存在,则抛出 一个 “NotFoundErrorDOMException

  3. 返回与 store此对象关联的对象存储句柄

注意: 在同一个 IDBTransaction 实例上使用相同名称对此方法的每次调用都会返回相同的 IDBObjectStore 实例。

注意: 返回的 IDBObjectStore 实例特定于此 IDBTransaction。 如果在不同的 IDBTransaction 上调用此方法, 则会返回一个不同的 IDBObjectStore 实例。

abort() 方法的步骤是:

  1. 如果对象的状态正在提交已完成, 则抛出一个“InvalidStateErrorDOMException

  2. 对象的状态设置为非活动并使用此对象和 null 运行中止事务

commit() 方法的步骤是:

  1. 如果对象的状态不是活动的, 则抛出一个“InvalidStateErrorDOMException

  2. 使用此对象运行提交事务

🚧 commit() 方法是此版本中的新增功能。 Chrome 76、Edge 79、Firefox 74 和 Safari 15 支持它。 🚧

注意: 通常不需要在事务上调用 commit()。 当所有未完成的请求都已满足且没有发出新请求时, 事务将自动提交。此调用可用于在不等待未完成请求的事件 被分派的情况下启动提交过程。

onabort 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型abort

oncomplete 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型complete

onerror 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型error

注意: 要确定事务是否已成功完成, 请侦听事务complete 事件, 而不是特定请求success 事件,因为在 success 事件触发后,事务 仍可能失败。

5. 算法

5.1. 打开数据库连接

要使用请求打开数据库storageKey、数据库name、数据库versionrequest打开数据库连接,请运行以下步骤:

  1. queuestorageKeyname连接队列

  2. request 添加到 queue

  3. 等待 queue 中所有先前的请求都已处理完毕。

  4. dbstorageKey名为 name数据库,否则为 null。

  5. 如果 version 未定义,则如果 db 为 null,则令 version 为 1,否则为 db版本

  6. 如果 db 为 null,则令 db 为一个新数据库,其名称name版本为 0(零),并且没有对象存储。 如果此操作因任何原因失败,则返回一个适当的错误(例如,“QuotaExceededError” 或 “UnknownErrorDOMException)。

  7. 如果 db版本大于 version, 则返回一个新创建的“VersionErrorDOMException 并中止这些步骤。

  8. connection 为到 db 的一个新连接

  9. connection版本设置为 version

  10. 如果 db版本小于 version,则:

    1. openConnections 为与 db 关联的所有连接集合connection 除外。

    2. 对于 openConnections 中每个关闭待定标志未设置为 true 的entry将一个数据库任务排入队列以在 entry触发一个名为 versionchange 的版本更改事件,其参数为 db版本version

      注意: 触发此事件可能会导致 openConnections 中的一个或多个其他对象被关闭, 在这种情况下,即使尚未对这些对象执行此操作, versionchange 事件也不会在这些对象上触发。

    3. 等待所有事件都被触发。

    4. 如果 openConnections 中的任何连接仍未关闭, 将一个数据库任务排入队列以在 request触发一个名为 blocked 的版本更改事件,其参数为 db版本version

    5. 等待直到 openConnections 中的所有 连接关闭

    6. 使用 connectionversionrequest 运行升级数据库

    7. 如果 connection关闭, 则返回一个新创建的 “AbortErrorDOMException 并中止这些步骤。

    8. 如果升级事务被中止, 则使用 connection 运行关闭数据库连接的步骤, 返回一个新创建的 “AbortErrorDOMException 并中止这些步骤。

  11. 返回 connection

5.2. 关闭数据库连接

要使用 connection 对象和一个可选的 forced flag 关闭数据库连接,请运行以下步骤:

  1. connection关闭待定标志设置为 true。

  2. 如果 forced flag 为 true,则对于使用 connection 创建的每个 transaction, 使用 transaction 和新创建的 “AbortErrorDOMException 运行中止事务

  3. 等待使用 connection 创建的所有事务完成。 一旦它们完成,connection关闭了。

  4. 如果 forced flag 为 true,则在 connection触发一个名为 close 的事件。

    注意: close 事件仅在连接异常关闭时触发, 例如,如果存储密钥的存储被清除, 或者存在损坏或 I/O 错误。如果显式调用 close(), 则该事件不会触发。

注意: 一旦连接关闭待定标志被设置为 true, 就不能再使用该连接创建新的事务。 所有创建事务的方法 都会首先检查连接关闭待定标志, 如果为 true 则抛出异常。

注意: 一旦连接关闭, 这可以解除对升级数据库步骤的阻塞, 以及对删除数据库步骤的阻塞, 这两个步骤都 等待到给定数据库连接关闭后才继续。

5.3. 删除数据库

要使用请求删除数据库storageKey、 数据库namerequest删除数据库, 请运行以下步骤:

  1. queuestorageKeyname连接队列

  2. request 添加到 queue

  3. 等待 queue 中所有先前的请求都已处理完毕。

  4. dbstorageKey名为 name数据库,如果存在的话。否则,返回 0(零)。

  5. openConnections 为与 db 关联的所有 连接集合

  6. 对于 openConnections 中每个 关闭待定标志未设置为 true 的entry将一个数据库任务排入队列 以在 entry触发一个名为 versionchange 的版本更改事件,其参数为 db版本 和 null。

    注意: 触发此事件可能会导致 openConnections 中的一个或多个其他对象被关闭, 在这种情况下,即使尚未对这些对象执行此操作, versionchange 事件也不会在这些对象上触发。

  7. 等待所有事件都被触发。

  8. 如果 openConnections 中的任何连接仍未关闭, 将一个数据库任务排入队列 以在 request触发一个名为 blocked 的版本更改事件,其参数为 db版本和 null。

  9. 等待直到 openConnections 中的所有 连接关闭

  10. versiondb版本

  11. 删除 db。如果此操作因任何原因失败,则返回一个适当的错误 (例如“QuotaExceededError” 或“UnknownErrorDOMException)。

  12. 返回 version

5.4. 提交事务

要使用要提交的 transaction 提交事务,请运行以下步骤:

  1. transaction状态设置为正在提交

  2. 并行运行以下步骤:

    1. 等待 transaction请求列表中的每个项目都 处理完毕

    2. 如果 transaction状态不再是正在提交, 则终止这些步骤。

    3. 尝试将 transaction 所做的任何未完成的更改写入数据库, 同时考虑 transaction持久性提示

    4. 如果在将更改写入数据库时发生错误, 则使用 transaction 和一个适合该错误的类型(例如“QuotaExceededError” 或 “UnknownErrorDOMException)运行中止事务, 并终止这些步骤。

    5. 将一个数据库任务排入队列以运行以下步骤:

      1. 如果 transaction 是一个升级事务, 则将 transaction连接的关联数据库升级事务设置为 null。

      2. transaction状态设置为已完成

      3. transaction触发一个名为 complete 的事件。

        注意: 即使从此事件的某个事件处理程序中抛出异常, 事务仍然会被提交,因为写入数据库更改的操作发生在事件发生之前。 只有在事务成功写入后,才会触发 complete 事件。

      4. 如果 transaction 是一个升级事务, 则令 request 为与 transaction 关联的请求, 并将 request事务设置为 null。

5.5. 中止事务

要使用要中止的 transactionerror 中止事务,请运行以下步骤:

  1. 事务数据库所做的所有更改都将被还原。对于升级事务,这包括对对象存储索引集合的更改,以及对版本的更改。 在事务期间创建的任何对象存储索引现在都出于其他算法的目的而被视为已删除。

  2. 如果 transaction 是一个升级事务,则使用 transaction 运行中止升级事务的步骤。

    注意: 这将还原与 transaction 关联的所有连接对象存储句柄索引句柄实例的更改。

  3. transaction状态设置为已完成

  4. 如果 error 不为 null,则将 transaction错误设置为 error

  5. 对于 transaction请求列表中的每个 request, 中止为 request 异步执行请求的步骤, 将 request已处理标志设置为 true, 并将一个数据库任务排入队列以运行以下步骤:

    1. request完成标志设置为 true。

    2. request结果设置为 undefined。

    3. request错误设置为一个新创建的“AbortErrorDOMException

    4. request触发一个名为 error 的事件,其 bubblescancelable 属性初始化为 true。

    注意: 这并不总是导致任何 error 事件被触发。例如,如果由于在提交事务时出错而中止事务, 或者如果它是最后一个失败的请求。

  6. 将一个数据库任务排入队列以运行以下步骤:

    1. 如果 transaction 是一个升级事务,则将 transaction连接的关联数据库升级事务设置为 null。

    2. transaction触发一个名为 abort 的事件,其 bubbles 属性初始化为 true。

    3. 如果 transaction 是一个升级事务,则:

      1. request 为与 transaction 关联的打开请求

      2. request事务设置为 null。

      3. request结果设置为 undefined。

      4. request已处理标志设置为 false。

      5. request完成标志设置为 false。

5.6. 异步执行请求

要使用 source 对象和要在数据库上执行的 operation 以及一个可选的 request 异步执行请求,请运行以下步骤:

如果创建的请求所属的事务使用中止事务的步骤被中止,则可以在任何时候中止这些步骤。

  1. transaction 为与 source 关联的事务

  2. 断言transaction状态活动的

  3. 如果未提供 request,则令 request 为一个新请求,其source

  4. request 添加到 transaction请求列表的末尾。

  5. 并行运行以下步骤:

    1. 等待直到 requesttransaction请求列表中第一个未处理的项目。

    2. result 为执行 operation 的结果。

    3. 如果 result 是一个错误并且 transaction状态正在提交, 则使用 transactionresult 运行中止事务, 并终止这些步骤。

    4. 如果 result 是一个错误, 则还原 operation 所做的所有更改。

      注意: 这仅还原此请求所做的更改,而不还原事务 所做的任何其他更改。

    5. request已处理标志设置为 true。

    6. 将一个数据库任务排入队列以运行以下步骤:

      1. transaction请求列表中删除 request

      2. request完成标志设置为 true。

      3. 如果 result 是一个错误,则:

        1. request结果设置为 undefined。

        2. request错误设置为 result

        3. request触发一个错误事件

      4. 否则:

        1. request结果设置为 result

        2. request错误设置为 undefined。

        3. request触发一个成功事件

  6. 返回 request

5.7. 升级数据库

要使用 connection(一个连接)、一个新 version 和一个 request 升级数据库,请运行以下步骤:

  1. dbconnection数据库

  2. transaction 为一个新升级事务,其中 connection 用作连接

  3. transaction作用域设置为 connection对象存储集

  4. db升级事务设置为 transaction

  5. transaction状态设置为非活动

  6. 启动 transaction

    注意: 请注意,在此事务完成之前, 无法打开到同一数据库的 其他连接

  7. old versiondb版本

  8. db版本设置为 version。此更改被视为事务的一部分, 因此如果事务被中止,则此更改将被还原。

  9. request已处理标志设置为 true。

  10. 将一个数据库任务排入队列以运行以下步骤:

    1. request结果设置为 connection

    2. request事务设置为 transaction

    3. request完成标志设置为 true。

    4. transaction状态设置为活动的

    5. didThrow 为在 request触发一个名为 upgradeneeded 的版本更改事件的结果,其参数为 old versionversion

    6. 如果 transaction状态活动的,则:

      1. transaction状态设置为非活动

      2. 如果 didThrow 为 true,则使用 transaction 和一个新创建的“AbortErrorDOMException 运行中止事务

  11. 等待 transaction 完成

    注意:事务生命周期中调用的某些算法, 例如提交事务的步骤和中止事务的步骤, 包括特定于升级事务的步骤。

5.8. 中止升级事务

要使用 transaction 中止升级事务,请运行以下步骤:

注意: 这些步骤由中止事务的步骤根据需要运行,该步骤会还原对数据库的更改,包括关联的对象存储索引集,以及对版本的更改。

  1. connectiontransaction连接

  2. databaseconnection数据库

  3. 如果 database 先前存在,则将 connection版本设置为 database版本, 如果 database 是新创建的,则设置为 0(零)。

    注意: 这将还原由 IDBDatabase 对象返回的 version 的值。

  4. 如果 database 先前存在,则将 connection对象存储集设置为 database 中的对象存储集, 如果 database 是新创建的,则设置为空集。

    注意: 这将还原由 IDBDatabase 对象返回的 objectStoreNames 的值。

  5. 对于与 transaction 关联的每个对象存储句柄 handle, 包括在 transaction 期间创建或删除的对象存储的句柄:

    1. 如果 handle对象存储不是在 transaction 期间新创建的, 则将 handle名称设置为其对象存储名称

    2. handle索引集设置为引用其对象存储索引集。

    注意: 这将还原相关 IDBObjectStore 对象返回的 nameindexNames 的值。

    这是如何可观察的? 虽然在事务中止后,脚本无法通过在 IDBTransaction 实例上使用 objectStore() 方法访问对象存储,但它仍然可以拥有对 IDBObjectStore 实例的引用,在这些实例上可以查询 nameindexNames 属性。
  6. 对于与 transaction 关联的每个索引句柄 handle, 包括在 transaction 期间创建或删除的索引的句柄:

    1. 如果 handle索引不是在 transaction 期间新创建的, 则将 handle名称设置为其索引名称

    注意: 这将还原相关 IDBIndex 对象返回的 name 的值。

    这是如何可观察的? 虽然在事务中止后,脚本无法通过在 IDBObjectStore 实例上使用 index() 方法访问索引,但它仍然可以拥有对 IDBIndex 实例的引用,在这些实例上可以查询 name 属性。

注意: IDBDatabase 实例的 name 属性不会被修改,即使被中止的升级事务正在创建一个新的数据库

5.9. 触发成功事件

要在request触发成功事件,请运行以下步骤:

  1. event 为使用 Event 创建事件的结果。

  2. eventtype 属性设置为“success”。

  3. eventbubblescancelable 属性设置为 false。

  4. transactionrequest事务

  5. legacyOutputDidListenersThrowFlag 初始为 false。

  6. 如果 transaction状态非活动的, 则将 transaction状态设置为活动的

  7. 使用 legacyOutputDidListenersThrowFlagrequest分派 event

  8. 如果 transaction状态活动的, 则:

    1. transaction状态设置为非活动的

    2. 如果 legacyOutputDidListenersThrowFlag 为 true, 则使用 transaction 和一个新创建的 “AbortErrorDOMException 运行中止事务

    3. 如果 transaction请求列表为空, 则使用 transaction 运行提交事务

5.10. 触发错误事件

要在request触发错误事件,请运行以下步骤:

  1. event 为使用 Event 创建事件的结果。

  2. eventtype 属性设置为“error”。

  3. eventbubblescancelable 属性设置为 true。

  4. transactionrequest事务

  5. legacyOutputDidListenersThrowFlag 初始为 false。

  6. 如果 transaction状态非活动的, 则将 transaction状态设置为活动的

  7. 使用 legacyOutputDidListenersThrowFlag请求分派 event

  8. 如果 transaction状态活动的, 则:

    1. transaction状态设置为非活动的

    2. 如果 legacyOutputDidListenersThrowFlag 为 true, 则使用 transaction 和一个新创建的 “AbortErrorDOMException 运行中止事务并终止这些步骤。 即使 event已取消标志为 false,也要这样做。

      注意: 这意味着如果触发了一个错误事件并且任何事件处理程序抛出异常, transactionerror 属性将被设置为 AbortError 而不是 request错误,即使从未调用 preventDefault()

    3. 如果 event已取消标志为 false, 则使用 transaction请求错误运行中止事务, 并终止这些步骤。

    4. 如果 transaction请求列表为空, 则使用 transaction 运行提交事务

5.11. 克隆一个值

要在 transaction 期间在 targetRealm克隆一个 value, 请运行以下步骤:

  1. 断言transaction状态活动的

  2. transaction状态设置为非活动的

    注意: 事务被设置为非活动的,以便由克隆操作触发的 getter 或其他副作用无法对事务发出额外的请求。

  3. serialized? StructuredSerializeForStorage(value)。

  4. clone? StructuredDeserialize(serialized, targetRealm)。

  5. transaction状态设置为活动的

  6. 返回 clone

5.12. 创建检索多个项目的请求

要从对象存储索引创建检索多个项目的请求,使用 targetRealmsourceHandlekindqueryOrOptions 和可选的 count,请运行以下步骤:

  1. source 为来自 sourceHandle 的一个索引或一个对象存储。 如果 sourceHandle 是一个索引句柄,则 source该索引句柄关联的索引。 否则,source该对象存储句柄关联的对象存储

  2. 如果 source 已被删除,则抛出一个 “InvalidStateErrorDOMException

  3. 如果 source 是一个索引source对象存储已被删除,则抛出 一个“InvalidStateErrorDOMException

  4. transactionsourceHandle事务

  5. 如果 transaction状态不是活动的,则抛出一个“TransactionInactiveErrorDOMException

  6. range 为一个键范围

  7. direction 为一个游标方向

  8. 如果运行是一个潜在有效的键范围queryOrOptions 的结果为 true,则:

    1. range 设置为使用 queryOrOptions 将值转换为键范围的结果。重新抛出任何异常。

    2. direction 设置为“next”。

  9. 否则:

    1. range 设置为使用 queryOrOptions["query"] 将值转换为键范围的结果。 重新抛出任何异常。

    2. count 设置为 queryOrOptions["count"]。

    3. direction 设置为 queryOrOptions["direction"]。

  10. operation 为一个要运行的算法。

  11. 如果 source 是一个索引,则将 operation 设置为从索引中检索多个项目, 参数为 targetRealmsourcerangekinddirection,以及如果给定了 count

  12. 否则将 operation 设置为从对象存储中检索多个项目, 参数为 targetRealmsourcerangekinddirection,以及如果给定了 count

  13. 返回运行异步执行请求的结果(一个 IDBRequest), 参数为 sourceHandleoperation

注意: range 可以是一个键范围(一个 IDBKeyRange), 用于标识要检索的记录项。如果为 null 或未给出,则使用无界键范围。 如果指定了 count 并且范围内的记录超过 count 条,则只会检索前 count 条记录。

6. 数据库操作

本节描述了对数据库中的对象存储索引中的数据执行的各种操作。 这些操作由异步执行请求的步骤运行。

注意: 下面操作步骤中对 StructuredDeserialize() 的调用可以断言不会抛出(如 ! 前缀所示),因为它们仅对 StructuredSerializeForStorage() 的先前输出进行操作。

6.1. 对象存储的存储操作

要使用 storevalue、一个可选的 key 和一个 no-overwrite flag 将记录存储到对象存储中,请运行以下步骤:

  1. 如果 store 使用键生成器,则:

    1. 如果 key 未定义,则:

      1. key 为为 store 生成一个键的结果。

      2. 如果 key 是 failure,则此操作失败,并带有“ConstraintErrorDOMException。中止此算法,不执行任何进一步的步骤。

      3. 如果 store 也使用内联键,则使用 valuekeystore键路径运行使用键路径将键注入值中

    2. 否则,使用 keystore 运行可能更新键生成器

  2. 如果向这些步骤提供了 no-overwrite flag 并且为 true,并且在 store 中已存在一个记录,其键等于 key,则此操作失败,并带有“ConstraintErrorDOMException。中止此算法,不执行任何进一步的步骤。

  3. 如果在 store 中已存在一个记录,其键等于 key,则使用从对象存储中删除记录store 中删除该记录

  4. store 中存储一条记录,其键为 key,其值为 ! StructuredSerializeForStorage(value)。该记录存储在对象存储的记录列表中,以便列表根据记录的键按升序排序。

  5. 对于引用 store 的每个 index

    1. index key 为使用 valueindex键路径indexmultiEntry 标志从值中提取键的结果。

    2. 如果 index key 是一个异常、无效或失败,则不对 index 采取进一步操作,并继续对下一个索引执行这些步骤。

      注意: 此步骤中抛出的异常不会被重新抛出。

    3. 如果 indexmultiEntry 标志为 false,或者如果 index key 不是一个数组键,并且如果 index 中已存在一个记录,其等于 index key,并且 index唯一标志为 true,则此操作失败,并带有“ConstraintErrorDOMException。中止此算法,不执行任何进一步的步骤。

    4. 如果 indexmultiEntry 标志为 true 且 index key 是一个数组键,并且如果 index 中已存在一个记录,其等于 index key 的任何子键,并且 index唯一标志为 true,则此操作失败,并带有“ConstraintErrorDOMException。中止此算法,不执行任何进一步的步骤。

    5. 如果 indexmultiEntry 标志为 false,或者如果 index key 不是一个数组键,则在 index 中存储一条记录,其键为 index key,其值为 key。该记录存储在 index记录列表中,以便列表主要根据记录的键,其次根据记录的值,按升序排序。

    6. 如果 indexmultiEntry 标志为 true 且 index key 是一个数组键,则对于 index key子键中的每个 subkey,在 index 中存储一条记录,其键为 subkey,其值为 key。这些记录存储在 index记录列表中,以便列表主要根据记录的键,其次根据记录的值,按升序排序。

      注意: 没有子键是有效的。在这种情况下,不会向索引中添加任何记录。

      注意: 即使子键的任何成员本身是一个数组键,该成员也直接用作索引记录的键。 嵌套的数组键不会被展平或“解包”以产生多行;只有最外层的数组键会这样做。

  6. 返回 key

6.2. 对象存储的检索操作

要使用 targetRealmstorerange 从对象存储中检索值,请运行以下步骤。 它们返回 undefined、一个 ECMAScript 值或一个错误(一个 DOMException):

  1. recordstore记录列表中第一个记录,其 range 范围内(如果有)。

  2. 如果未找到 record,则返回 undefined。

  3. serializedrecord。如果在从底层存储中读取值时发生错误,则返回一个新创建的 “NotReadableErrorDOMException

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要使用 storerange 从对象存储中检索键,请运行以下步骤:

  1. recordstore记录列表中第一个记录,其 range 范围内(如果有)。

  2. 如果未找到 record,则返回 undefined。

  3. 返回使用 record 的键将键转换为值的结果。

要使用 targetRealmstorerangekinddirection 和可选的 count 从对象存储中检索多个项目,请运行以下步骤:

  1. 如果未提供 count 或为 0(零),则令 count 为无穷大。

  2. records 为一个空的列表,其中包含记录

  3. 如果 direction 是“next” 或“nextunique”, 则将 records 设置为 store记录列表中前 count range 范围内的记录。

  4. 如果 direction 是“prev” 或“prevunique”, 则将 records 设置为 store记录列表中后 count range 范围内的记录。

  5. list 为一个空的列表

  6. 对于 records 中的每个 record,根据 kind 进行切换:

    “key”
    1. key 为使用 record 的键将键转换为值的结果。

    2. 追加到 列表

    “value”
    1. serializedrecord

    2. value! StructuredDeserialize(serialized, targetRealm)。

    3. 追加到 列表

    “record”
    1. keyrecord 的键。

    2. serializedrecord

    3. value! StructuredDeserialize(serialized, targetRealm)。

    4. recordSnapshot 为一个新的记录快照,其设置为 key设置为 value,并且主键设置为 key

    5. recordSnapshot 附加list

  7. 返回 list

6.3. 索引检索操作

要使用 targetRealmindexrange 从索引中检索引用的值,请运行以下步骤:

  1. recordindex记录列表中第一个记录,其 range 范围内(如果有)。

  2. 如果未找到 record,则返回 undefined。

  3. serializedrecord引用值

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要使用 indexrange 从索引中检索值,请运行以下步骤:

  1. recordindex记录列表中第一个记录,其 range 范围内(如果有)。

  2. 如果未找到 record,则返回 undefined。

  3. 返回使用 record将键转换为值的结果。

要使用 targetRealmindexrangekinddirection 和可选的 count 从索引中检索多个项目,请运行以下步骤:

  1. 如果未提供 count 或为 0(零),则令 count 为无穷大。

  2. records 为一个空的列表,其中包含记录

  3. 根据 direction 进行切换:

    "next"
    1. records 设置为 index记录列表 range 范围内的前 count 个记录。

    "nextunique"
    1. rangeRecords 为一个列表,其中包含 index记录列表 range 范围内的记录。

    2. rangeRecordsLengthrangeRecords大小

    3. i 为 0。

    4. i 小于 rangeRecordsLength 时,则:

      1. i 增加 1。

      2. 如果 record大小等于 count,则跳出循环

      3. 如果使用 |rangeRecords[i]| 和 |rangeRecords[i-1]| 的键比较两个键的结果相等,则继续

      4. 否则将 |rangeRecords[i]| 附加records

    "prev"
    1. records 设置为 index记录列表 range 范围内的后 count 个记录。

    "prevunique"
    1. rangeRecords 为一个列表,其中包含 index记录列表 range 范围内的记录。

    2. rangeRecordsLengthrangeRecords大小

    3. i 为 0。

    4. i 小于 rangeRecordsLength 时,则:

      1. i 增加 1。

      2. 如果 record大小等于 count,则跳出循环

      3. 如果使用 |rangeRecords[i]| 和 |rangeRecords[i-1]| 的键比较两个键的结果相等,则继续

      4. 否则将 |rangeRecords[i]| 前置records

  4. list 为一个空的列表

  5. 对于 records 中的每个 record,根据 kind 进行切换:

    "key"
    1. key 为使用 record 的值将键转换为值的结果。

    2. key 附加list

    "value"
    1. serializedrecord引用值

    2. value! StructuredDeserialize(serialized, targetRealm)。

    3. value 附加list

    "record"
    1. index keyrecord 的键。

    2. keyrecord 的值。

    3. serializedrecord引用值

    4. value! StructuredDeserialize(serialized, targetRealm)。

    5. recordSnapshot 为一个新的记录快照,其设置为 index key设置为 value,并且主键设置为 key

    6. recordSnapshot 附加list

  6. 返回 list

6.4. 对象存储删除操作

要使用 storerange 从对象存储中删除记录,请运行以下步骤:

  1. store记录列表中删除所有键 range 范围内的记录(如果有)。

  2. 对于引用 store 的每个 index,从 index记录列表中删除所有值 range 范围内的记录(如果存在此类记录)。

  3. 返回 undefined。

6.5. 记录计数操作

要使用 sourcerange 对范围内的记录进行计数,请运行以下步骤:

  1. countsource 的记录列表中键 range 范围内的记录数(如果有)。

  2. 返回 count

6.6. 对象存储清除操作

要使用 store 清除对象存储,请运行以下步骤:

  1. store 中删除所有记录。

  2. 在所有引用 store索引中,删除所有记录

  3. 返回 undefined。

6.7. 游标迭代操作

要使用 targetRealmcursor、一个可选的要迭代到的 keyprimaryKey,以及一个可选的 count 迭代游标,请运行以下步骤:

  1. sourcecursor

  2. directioncursor方向

  3. 断言:如果给定了 primaryKey,则 source 是一个索引,并且 direction 是“next”或“prev”。

  4. recordssource 中的记录列表。

    注意: records 始终按升序顺序排序。 如果 source 是一个索引,则 records 其次按升序顺序排序(其中索引中的值是引用的对象存储记录)。

  5. rangecursor范围

  6. positioncursor位置

  7. object store positioncursor对象存储位置

  8. 如果未提供 count,则令 count 为 1。

  9. count 大于 0 时:

    1. 根据 direction 进行切换:

      "next"

      found recordrecords 中满足以下所有要求的第一个记录:

      • 如果定义了 key

      • 如果定义了 primaryKey

      • 如果定义了 positionsource 是一个对象存储

        • 记录的键大于 position

      • 如果定义了 positionsource 是一个索引

        • 记录的键等于 position 且记录的值大于 object store position

        • 记录的键大于 position

      • 记录的键 range 范围内。

      "nextunique"

      found recordrecords 中满足以下所有要求的第一个记录:

      • 如果定义了 key

      • 如果定义了 position

        • 记录的键大于 position

      • 记录的键 range 范围内。

      "prev"

      found recordrecords 中满足以下所有要求的最后一个记录:

      • 如果定义了 key

      • 如果定义了 primaryKey

      • 如果定义了 positionsource 是一个对象存储

        • 记录的键小于 position

      • 如果定义了 positionsource 是一个索引

        • 记录的键等于 position 且记录的值小于 object store position

        • 记录的键小于 position

      • 记录的键 range 范围内。

      "prevunique"

      temp recordrecords 中满足以下所有要求的最后一个记录:

      • 如果定义了 key

      • 如果定义了 position

        • 记录的键小于 position

      • 记录的键 range 范围内。

      如果定义了 temp record,则令 found recordrecords等于 temp record的第一个记录。

      注意: 使用“prevunique”进行迭代会访问与“nextunique”访问的相同记录,但顺序相反。

    2. 如果未定义 found record,则:

      1. cursor设置为 undefined。

      2. 如果 source 是一个索引,则将 cursor对象存储位置设置为 undefined。

      3. 如果 cursor仅键标志为 false,则将 cursor设置为 undefined。

      4. 返回 null。

    3. positionfound record 的键。

    4. 如果 source 是一个索引,则令 object store positionfound record 的值。

    5. count 减 1。

  10. cursor位置设置为 position

  11. 如果 source 是一个索引,则将 cursor对象存储位置设置为 object store position

  12. cursor设置为 found record 的键。

  13. 如果 cursor仅键标志为 false,则:

    1. serializedfound record(如果 source 是一个对象存储),否则为 found record引用值

    2. cursor设置为 ! StructuredDeserialize(serialized, targetRealm)

  14. cursor已获取值标志设置为 true。

  15. 返回 cursor

7. ECMAScript 绑定

本节定义了本规范中定义的值如何与 ECMAScript 值相互转换,以及如何使用键路径从 ECMAScript 值中提取和注入它们。本节引用了 ECMAScript 语言规范中的类型和算法,并使用了一些算法约定。 [ECMA-262] 此处未详细说明的转换在 [WEBIDL] 中定义。

7.1. 从值中提取键

要使用 valuekeyPath 和一个可选的 multiEntry flag 使用键路径从值中提取键,请运行以下步骤。这些步骤的结果是一个、无效或失败,或者这些步骤可能会抛出异常。

  1. r 为使用 valuekeyPath 在值上评估键路径的结果。重新抛出任何异常。

  2. 如果 r 是 failure,则返回 failure。

  3. 如果 multiEntry flag 为 false,则令 key 为使用 r 将值转换为键的结果,否则为使用 r 将值转换为 multiEntry 键的结果。重新抛出任何异常。

  4. 如果 key 是“invalid value”或“invalid type”,则返回 invalid。

  5. 返回 key

要使用 valuekeyPath 在值上评估键路径,请运行以下步骤。这些步骤的结果是一个 ECMAScript 值或 failure,或者这些步骤可能会抛出异常。

  1. 如果 keyPath 是一个字符串列表,则:

    1. result 为一个通过表达式 [] 创建的新 Array 对象。

    2. i 为 0。

    3. 对于 keyPath 中的每个 item

      1. key 为使用 itemvalue 递归在值上评估键路径的结果。

      2. 断言key 不是一个突然完成

      3. 如果 key 是 failure,则中止整个算法并返回 failure。

      4. p! ToString(i)。

      5. statusCreateDataProperty(result, p, key)。

      6. 断言status 为 true。

      7. i 增加 1。

    4. 返回 result

      注意: 这只会“递归”一层,因为键路径序列永远不能嵌套。

  2. 如果 keyPath 是空字符串,则返回 value 并跳过其余步骤。

  3. identifiers 为在 U+002E FULL STOP 字符 (.) 上严格分割 keyPath 的结果。

  4. 对于 identifiers 中的每个 identifier,跳转到下面的相应步骤:

    如果 Type(value) 是 String,且 identifier 是 "length"

    value 为一个等于 value 中元素数量的 Number。

    如果 value 是一个 Arrayidentifier 是 "length"

    value! ToLength(! Get(value, "length"))。

    如果 value 是一个 Blobidentifier 是 "size"

    value 为一个等于 valuesize 的 Number。

    如果 value 是一个 Blobidentifier 是 "type"

    value 为一个等于 valuetype 的 String。

    如果 value 是一个 Fileidentifier 是 "name"

    value 为一个等于 valuename 的 String。

    如果 value 是一个 Fileidentifier 是 "lastModified"

    value 为一个等于 valuelastModified 的 Number。

    否则
    1. 如果 Type(value) 不是 Object,则返回 failure。

    2. hop! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则返回 failure。

    4. value! Get(value, identifier)。

    5. 如果 value 是 undefined,则返回 failure。

  5. 断言value 不是一个突然完成

  6. 返回 value

注意: 可以在上述步骤中进行断言,因为此算法仅适用于作为 StructuredDeserialize 输出的值,并且仅访问“自有”属性。

7.2. 将键注入值

注意: 本节中使用的键路径始终是字符串,绝不是序列,因为不可能创建一个既有键生成器又有作为序列的键路径对象存储

要使用 valuekeyPath 检查键是否可以注入值,请运行以下步骤。这些步骤的结果为 true 或 false。

  1. identifiers 为在 U+002E FULL STOP 字符 (.) 上严格分割 keyPath 的结果。

  2. 断言identifiers 不为空。

  3. 移除 identifiers 的最后一个项目

  4. 对于 identifiers 中剩余的每个 identifier(如果有):

    1. 如果 value 不是 ObjectArray, 则返回 false。

    2. hop! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则返回 true。

    4. value! Get(value, identifier)。

  5. 如果 valueObjectArray,则返回 true,否则返回 false。

注意: 可以在上述步骤中进行断言,因为此算法仅应用于作为 StructuredDeserialize 输出的值。

要使用 value、一个 key 和一个 keyPath 使用键路径将键注入值,请运行以下步骤:

  1. identifiers 为在 U+002E FULL STOP 字符 (.) 上严格分割 keyPath 的结果。

  2. 断言identifiers 不为空。

  3. lastidentifiers 的最后一个项目,并将其从列表中移除。

  4. 对于 identifiers 中剩余的每个 identifier

    1. 断言valueObjectArray

    2. hop! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则:

      1. o 为一个通过表达式 ({}) 创建的新 Object

      2. statusCreateDataProperty(value, identifier, o)。

      3. 断言status 为 true。

    4. value! Get(value, identifier)。

  5. 断言valueObjectArray

  6. keyValue 为使用 key 将键转换为值的结果。

  7. statusCreateDataProperty(value, last, keyValue)。

  8. 断言status 为 true。

注意: 可以在上述步骤中进行断言,因为此算法仅应用于作为 StructuredDeserialize 输出的值,并且已经运行了检查键是否可以注入值的步骤。

7.3. 将键转换为值

要使用 key 将键转换为值,请运行以下步骤。这些步骤返回一个 ECMAScript 值。

  1. typekey类型

  2. valuekey

  3. 根据 type 进行切换:

    number

    返回一个等于 value 的 ECMAScript Number 值

    string

    返回一个等于 value 的 ECMAScript String 值

    date
    1. date 为使用单个参数 value 执行 ECMAScript Date 构造函数的结果。

    2. 断言date 不是一个突然完成

    3. 返回 date

    binary
    1. lenvalue长度

    2. buffer 为使用 len 执行 ECMAScript ArrayBuffer 构造函数的结果。

    3. 断言buffer 不是一个突然完成

    4. buffer 的 [[ArrayBufferData]] 内部插槽中的条目设置为 value 中的条目。

    5. 返回 buffer

    array
    1. array 为不带参数执行 ECMAScript Array 构造函数的结果。

    2. 断言array 不是一个突然完成

    3. lenvalue大小

    4. index 为 0。

    5. index 小于 len 时:

      1. entry 为使用 value[index] 将键转换为值的结果。

      2. statusCreateDataProperty(array, index, entry)。

      3. 断言status 为 true。

      4. index 增加 1。

    6. 返回 array

7.4. 将值转换为键

要使用 ECMAScript 值 input 和一个可选的集合 seen 将值转换为键,请运行以下步骤。这些步骤的结果是一个、"invalid value" 或 "invalid type",或者这些步骤可能会抛出异常。

  1. 如果未提供 seen,则令 seen 为一个新的空集合

  2. 如果 seen 包含 input,则返回 "invalid value"。

  3. 跳转到下面的相应步骤:

    如果 Type(input) 是 Number
    1. 如果 input 是 NaN,则返回 "invalid value"。

    2. 否则,返回一个新,其类型numberinput

    如果 input 是一个 Date (有一个 [[DateValue]] 内部插槽)
    1. msinput 的 [[DateValue]] 内部插槽的值。

    2. 如果 ms 是 NaN,则返回 "invalid value"。

    3. 否则,返回一个新,其类型datems

    如果 Type(input) 是 String
    1. 返回一个新,其类型stringinput

    如果 input 是一个缓冲区源类型
    1. 如果 input分离,则返回 "invalid value"。

    2. bytes获取缓冲区源所持有的字节副本的结果,参数为input

    3. 返回一个新,其类型binarybytes

    如果 input 是一个数组外来对象
    1. len? ToLength( ? Get(input, "length"))。

    2. input 附加seen

    3. keys 为一个新的空列表。

    4. index 为 0。

    5. index 小于 len 时:

      1. hop? HasOwnProperty(input, index)。

      2. 如果 hop 为 false,则返回 "invalid value"。

      3. entry? Get(input, index)。

      4. key 为使用参数 entryseen 将值转换为键的结果。

      5. ReturnIfAbrupt(key)。

      6. 如果 key 是 "invalid value" 或 "invalid type",则中止这些步骤并返回 "invalid value"。

      7. key 附加keys

      8. index 增加 1。

    6. 返回一个新数组键,其keys

    否则

    返回 "invalid type"。

要使用 ECMAScript 值 input 将值转换为 multiEntry 键,请运行以下步骤。这些步骤的结果是一个、"invalid value" 或 "invalid type",或者这些步骤可能会抛出异常。

  1. 如果 input 是一个数组外来对象,则:

    1. len? ToLength( ? Get(input, "length"))。

    2. seen 为一个仅包含 input 的新集合

    3. keys 为一个新的空列表

    4. index 为 0。

    5. index 小于 len 时:

      1. entryGet(input, index)。

      2. 如果 entry 不是一个突然完成,则:

        1. key 为使用参数 entryseen 将值转换为键的结果。

        2. 如果 key 不是 "invalid value" 或 "invalid type" 或突然完成,并且 keys 中没有项目等于 key,则将 key 附加keys

      3. index 增加 1。

    6. 返回一个新数组键,其设置为keys

  2. 否则,返回使用参数 input 将值转换为键的结果。重新抛出任何异常。

注意: 这些步骤与将值转换为键的步骤类似,但如果顶层值是 Array,则无法转换为键的成员将被忽略,并且重复项将被删除。

例如,值 [10, 20, null, 30, 20] 被转换为一个数组键,其子键为 10、20、30。

8. 隐私注意事项

本节是非规范性的。

8.1. 用户跟踪

第三方主机(或任何能够将内容分发到多个站点的对象)可以使用存储在其客户端数据库中的唯一标识符来跨多个会话跟踪用户,从而建立用户活动的用户画像。与知道用户真实身份对象(例如需要经过身份验证的凭据的电子商务网站)的网站相结合,这可能使压迫性团体能够比在纯粹匿名的网络使用世界中更准确地针对个人。

有许多技术可用于降低用户跟踪的风险:

阻止第三方存储

用户代理可以将对数据库对象的访问限制为源自浏览上下文的顶级文档域的脚本,例如,拒绝在 iframe 中运行的其他域的页面访问 API。

使存储的数据过期

用户代理可以在一段时间后自动删除存储的数据。

这可以限制网站跟踪用户的能力,因为网站只能在用户通过网站本身进行身份验证时(例如,通过购买或登录服务)跨多个会话跟踪用户。

然而,这也将用户的数据置于风险之中。

将持久性存储视为 cookie

用户代理应以一种将数据库功能与 HTTP 会话 cookie 紧密关联的方式向用户呈现该功能。[COOKIES]

这可能会鼓励用户以健康的怀疑态度看待此类存储。

特定站点的数据库访问安全列表

用户代理可能会要求用户在网站使用该功能之前授权访问数据库。

第三方存储的归属

用户代理可以记录包含来自导致数据被存储的第三方的内容的网站的

如果此信息随后用于呈现当前持久性存储中的数据视图,它将允许用户就修剪持久性存储的哪些部分做出明智的决定。结合黑名单(“删除此数据并阻止此域再次存储数据”),用户可以将持久性存储的使用限制在她信任的网站上。

共享黑名单

用户代理可以允许用户共享其持久性存储域黑名单。

这将允许社区共同行动以保护其隐私。

虽然这些建议可以防止滥用此 API 进行用户跟踪,但它们并不能完全阻止它。在单个域内,网站可以在会话期间继续跟踪用户,然后可以将所有这些信息连同网站获得的任何身份识别信息(姓名、信用卡号、地址)传递给第三方。如果第三方与多个网站合作以获取此类信息,仍然可以创建用户画像。

然而,即使用户代理完全不合作,在某种程度上也可以进行用户跟踪,例如通过在 URL 中使用会话标识符,这种技术已经普遍用于无害的目的,但很容易被重新用于用户跟踪(甚至可以追溯)。然后,这些信息可以与其他网站共享,使用访问者的 IP 地址和其他用户特定数据(例如,用户代理标头和配置设置)将单独的会话组合成连贯的用户画像。

如果持久性存储的用户界面将本规范中描述的持久性存储功能中的数据与 HTTP 会话 cookie 中的数据分开呈现,那么用户很可能会删除其中一个中的数据而不是另一个。这将允许网站将这两个功能用作彼此的冗余备份,从而挫败用户保护其隐私的尝试。

8.3. 数据敏感性

用户代理应将持久存储的数据视为潜在的敏感数据;电子邮件、日历约会、健康记录或其他机密文件很可能存储在此机制中。

为此,用户代理应确保在删除数据时,会立即从底层存储中删除它。

9. 安全注意事项

9.1. DNS 欺骗攻击

由于存在 DNS 欺骗攻击的可能性,因此无法保证声称位于某个域中的主机确实来自该域。为了缓解这种情况,页面可以使用 TLS。使用 TLS 的页面可以确保只有使用 TLS 且具有将其标识为来自同一域的证书的页面才能访问其数据库。

9.2. 跨目录攻击

共享一个主机名的不同作者,例如在 geocities.com 上托管内容的用户,都共享同一组数据库。

没有功能可以按路径名限制访问。因此,建议共享主机上的作者避免使用这些功能,因为其他作者读取数据并覆盖它将是微不足道的。

注意: 即使提供了路径限制功能,通常的 DOM 脚本安全模型也会使其很容易绕过此保护并从任何路径访问数据。

9.3. 实现风险

实现这些持久性存储功能时的两个主要风险是让恶意网站读取来自其他域的信息,以及让恶意网站写入随后从其他域读取的信息。

让第三方网站读取不应从其域读取的数据会导致信息泄露。例如,一个域上的用户购物愿望清单可能被另一个域用于定向广告;或者,文字处理网站存储的用户正在进行的机密文件可能会被竞争公司的网站检查。

让第三方网站向其他域的持久性存储中写入数据可能导致信息欺骗,这同样危险。例如,恶意网站可以向用户的愿望清单中添加记录;或者恶意网站可以将用户的会话标识符设置为一个已知的 ID,然后恶意网站可以使用该 ID 来跟踪用户在受害者网站上的行为。

因此,严格遵循本规范中描述的存储键分区模型对于用户安全至关重要。

如果使用主机名或数据库名来构造持久化到文件系统的路径,则必须对它们进行适当的转义,以防止对手使用相对路径(如“../”)访问来自其他存储键的信息。

9.4. 持久性风险

实际的实现会将数据持久化到非易失性存储介质。数据在存储时将被序列化,在检索时将被反序列化,尽管序列化格式的细节将是用户代理特定的。用户代理可能会随着时间的推移改变其序列化格式。例如,可能会更新格式以处理新的数据类型,或提高性能。为了满足本规范的操作要求,实现因此必须以某种方式处理旧的序列化格式。对旧数据处理不当可能导致安全问题。除了基本的序列化问题外,序列化的数据可能编码了在较新版本的用户代理中无效的假设。

一个实际的例子是 RegExp 类型。 StructuredSerializeForStorage 操作允许序列化 RegExp 对象。典型的用户代理会将正则表达式编译为本机机器指令,并对如何传递输入数据和返回结果做出假设。如果此内部状态作为存储到数据库的数据的一部分被序列化,则在稍后反序列化内部表示时可能会出现各种问题。例如,将数据传递到代码中的方式可能已经改变。编译器输出中的安全漏洞可能已在用户代理的更新中被识别和修复,但仍保留在序列化的内部状态中。

用户代理必须适当地识别和处理旧数据。一种方法是在序列化格式中包含版本标识符,并在遇到旧数据时从脚本可见的状态重建任何内部状态。

10. 可访问性注意事项

本节是非规范性的。

本规范描述的 API 具有有限的可访问性注意事项:

该 API 确实允许存储结构化内容。文本内容可以作为字符串存储。API 中存在支持,供开发人员将替代的非文本内容(如图像或音频)存储为 BlobFileImageData 对象。使用该 API 开发动态内容应用程序的开发人员应确保内容对于具有各种技术和需求的用户是可访问的。

虽然 API 本身没有为其定义特定的机制,但结构化内容的存储也允许开发人员存储国际化内容,使用不同的记录或记录内的结构来保存语言替代方案。

该 API 不定义或要求任何用户代理生成用户界面以实现与 API 的交互。用户代理可以选择性地提供用户界面元素来支持该 API。示例包括在需要额外存储配额时向用户发出提示、观察特定网站使用的存储的功能,或特定于 API 存储的工具,例如检查、修改或删除记录。任何此类用户界面元素的设计都必须考虑到可访问性工具。例如,以图形形式呈现已用存储配额比例的用户界面也必须向屏幕阅读器等工具提供相同的数据。

11. 修订历史

本节是非规范性的。

以下是自本规范上次发布以来的变更的非正式摘要。完整的修订历史可以在这里找到。 有关第一版的修订历史,请参阅该文档的修订历史。 有关第二版的修订历史,请参阅该文档的修订历史

12. 致谢

本节是非规范性的。

特别感谢第一版的原作者 Nikunj Mehta,以及第一版的其他编辑 Jonas Sicking、Eliot Graff、Andrei Popescu 和 Jeremy Orlow。

Garret Swart 对本规范的设计产生了极其重要的影响。

感谢 Tab Atkins, Jr. 创建和维护用于创建本文档的规范编写工具 Bikeshed,以及他的一般性编写建议。

特别感谢 Abhishek Shanthkumar、 Adam Klein、 Addison Phillips、 Adrienne Walker、 Alec Flett、 Andrea Marchesini、 Andreas Butler、 Andrew Sutherland、 Anne van Kesteren、 Anthony Ramine、 Ari Chivukula、 Arun Ranganathan、 Ben Dilts、 Ben Turner、 Bevis Tseng、 Boris Zbarsky、 Brett Zamir、 Chris Anderson、 Dana Florescu、 Danillo Paiva、 David Grogan、 Domenic Denicola、 Dominique Hazael-Massieux、 Evan Stade、 Glenn Maynard、 Hans Wennborg、 Isiah Meadows、 Israel Hilerio、 Jake Archibald、 Jake Drew、 Jerome Hode、 Josh Matthews、 João Eiras、 Kagami Sascha Rosylight、 Kang-Hao Lu、 Kris Zyp、 Kristof Degrave、 Kyaw Tun、 Kyle Huey、 Laxminarayan G Kamath A、 Maciej Stachowiak、 Marcos Cáceres、 Margo Seltzer、 Marijn Kruisselbrink、 Ms2ger、 Odin Omdal、 Olli Pettay、 Pablo Castro、 Philip Jägenstedt、 Shawn Wilsher、 Simon Pieters、 Steffen Larssen、 Steve Becker、 Tobie Langel、 Victor Costan、 Xiaoqian Wu、 Yannic Bonenberger、 Yaron Tausky、 Yonathan Randolph 和 Zhiqiang Zhang, 他们所有人的反馈和建议都对本规范的改进做出了贡献。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合来表达。 本文档规范部分的关键词“必须 (MUST)”、“不得 (MUST NOT)”、“必需 (REQUIRED)”、“应 (SHALL)”、“不应 (SHALL NOT)”、“应该 (SHOULD)”、“不应该 (SHOULD NOT)”、“推荐 (RECOMMENDED)”、“可以 (MAY)”和“可选 (OPTIONAL)” 应按照 RFC 2119 中的描述进行解释。 但是,为了便于阅读,这些词在本规范中不以全大写字母出现。

本规范的所有文本都是规范性的,除非明确标记为非规范性的章节、示例和注释。[RFC2119]

本规范中的示例以“例如”一词引入,或通过 class="example" 与规范性文本分开, 如下所示:

这是一个信息性示例的例子。

信息性注释以“注意”一词开头,并通过 class="note" 与规范性文本分开, 如下所示:

注意,这是一个信息性注释。

一致性算法

作为算法一部分以祈使语气表述的要求 (例如“去除任何前导空格字符” 或“返回 false 并中止这些步骤”) 应根据引入该算法时使用的关键词 (“必须”、“应该”、“可以”等)的含义进行解释。

以算法或特定步骤形式表述的一致性要求可以以任何方式实现, 只要最终结果等效即可。 特别是,本规范中定义的算法 旨在易于理解 而不是为了高性能。 鼓励实现者进行优化。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 实时标准. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[FileAPI]
Marijn Kruisselbrink. 文件 API. 2024年12月4日. WD. URL: https://www.w3.org/TR/FileAPI/
[HTML]
Anne van Kesteren; 等. HTML 标准. 实时标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 实时标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于在 RFC 中指示需求级别的关键词. 1997年3月. 最佳实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[STORAGE]
Anne van Kesteren. 存储标准. 实时 标准. URL: https://storage.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 实时 标准. URL: https://webidl.spec.whatwg.org/

信息性引用

[Charmod-Norm]
Addison Phillips; 等. 万维网字符模型:字符串匹配. 2021年8月11日. NOTE. URL: https://www.w3.org/TR/charmod-norm/
[COOKIES]
A. Barth. HTTP 状态管理机制. 2011年4月. 建议标准. URL: https://httpwg.org/specs/rfc6265.html
[WEBSTORAGE]
Ian Hickson. Web 存储 (第二版). 2021年1月28日. REC. URL: https://www.w3.org/TR/webstorage/

IDL 索引

[Exposed=(Window,Worker)]
interface IDBRequest : EventTarget {
  readonly attribute any result;
  readonly attribute DOMException? error;
  readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
  readonly attribute IDBTransaction? transaction;
  readonly attribute IDBRequestReadyState readyState;

  // Event handlers:
  attribute EventHandler onsuccess;
  attribute EventHandler onerror;
};

enum IDBRequestReadyState {
  "pending",
  "done"
};

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // Event handlers:
  attribute EventHandler onblocked;
  attribute EventHandler onupgradeneeded;
};

[Exposed=(Window,Worker)]
interface IDBVersionChangeEvent : Event {
  constructor(DOMString type, optional IDBVersionChangeEventInit eventInitDict = {});
  readonly attribute unsigned long long oldVersion;
  readonly attribute unsigned long long? newVersion;
};

dictionary IDBVersionChangeEventInit : EventInit {
  unsigned long long oldVersion = 0;
  unsigned long long? newVersion = null;
};

partial interface mixin WindowOrWorkerGlobalScope {
  [SameObject] readonly attribute IDBFactory indexedDB;
};

[Exposed=(Window,Worker)]
interface IDBFactory {
  [NewObject] IDBOpenDBRequest open(DOMString name,
                                    optional [EnforceRange] unsigned long long version);
  [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name);

  Promise<sequence<IDBDatabaseInfo>> databases();

  short cmp(any first, any second);
};

dictionary IDBDatabaseInfo {
  DOMString name;
  unsigned long long version;
};

[Exposed=(Window,Worker)]
interface IDBDatabase : EventTarget {
  readonly attribute DOMString name;
  readonly attribute unsigned long long version;
  readonly attribute DOMStringList objectStoreNames;

  [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
                                         optional IDBTransactionMode mode = "readonly",
                                         optional IDBTransactionOptions options = {});
  undefined close();

  [NewObject] IDBObjectStore createObjectStore(
    DOMString name,
    optional IDBObjectStoreParameters options = {});
  undefined deleteObjectStore(DOMString name);

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

enum IDBTransactionDurability { "default", "strict", "relaxed" };

dictionary IDBTransactionOptions {
  IDBTransactionDurability durability = "default";
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};

[Exposed=(Window,Worker)]
interface IDBObjectStore {
  attribute DOMString name;
  readonly attribute any keyPath;
  readonly attribute DOMStringList indexNames;
  [SameObject] readonly attribute IDBTransaction transaction;
  readonly attribute boolean autoIncrement;

  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);
  [NewObject] IDBRequest clear();
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any queryOrOptions,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any queryOrOptions,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllRecords(optional IDBGetAllOptions options = {});
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  IDBIndex index(DOMString name);

  [NewObject] IDBIndex createIndex(DOMString name,
                                   (DOMString or sequence<DOMString>) keyPath,
                                   optional IDBIndexParameters options = {});
  undefined deleteIndex(DOMString name);
};

dictionary IDBIndexParameters {
  boolean unique = false;
  boolean multiEntry = false;
};

dictionary IDBGetAllOptions {
  any query = null;
  [EnforceRange] unsigned long count;
  IDBCursorDirection direction = "next";
};

[Exposed=(Window,Worker)]
interface IDBIndex {
  attribute DOMString name;
  [SameObject] readonly attribute IDBObjectStore objectStore;
  readonly attribute any keyPath;
  readonly attribute boolean multiEntry;
  readonly attribute boolean unique;

  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any queryOrOptions,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any queryOrOptions,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllRecords(optional IDBGetAllOptions options = {});
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");
};

[Exposed=(Window,Worker)]
interface IDBKeyRange {
  readonly attribute any lower;
  readonly attribute any upper;
  readonly attribute boolean lowerOpen;
  readonly attribute boolean upperOpen;

  // Static construction methods:
  [NewObject] static IDBKeyRange only(any value);
  [NewObject] static IDBKeyRange lowerBound(any lower, optional boolean open = false);
  [NewObject] static IDBKeyRange upperBound(any upper, optional boolean open = false);
  [NewObject] static IDBKeyRange bound(any lower,
                                       any upper,
                                       optional boolean lowerOpen = false,
                                       optional boolean upperOpen = false);

  boolean includes(any key);
};

[Exposed=(Window,Worker)]
interface IDBRecord {
  readonly attribute any key;
  readonly attribute any primaryKey;
  readonly attribute any value;
};

[Exposed=(Window,Worker)]
interface IDBCursor {
  readonly attribute (IDBObjectStore or IDBIndex) source;
  readonly attribute IDBCursorDirection direction;
  readonly attribute any key;
  readonly attribute any primaryKey;
  [SameObject] readonly attribute IDBRequest request;

  undefined advance([EnforceRange] unsigned long count);
  undefined continue(optional any key);
  undefined continuePrimaryKey(any key, any primaryKey);

  [NewObject] IDBRequest update(any value);
  [NewObject] IDBRequest delete();
};

enum IDBCursorDirection {
  "next",
  "nextunique",
  "prev",
  "prevunique"
};

[Exposed=(Window,Worker)]
interface IDBCursorWithValue : IDBCursor {
  readonly attribute any value;
};

[Exposed=(Window,Worker)]
interface IDBTransaction : EventTarget {
  readonly attribute DOMStringList objectStoreNames;
  readonly attribute IDBTransactionMode mode;
  readonly attribute IDBTransactionDurability durability;
  [SameObject] readonly attribute IDBDatabase db;
  readonly attribute DOMException? error;

  IDBObjectStore objectStore(DOMString name);
  undefined commit();
  undefined abort();

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler oncomplete;
  attribute EventHandler onerror;
};

enum IDBTransactionMode {
  "readonly",
  "readwrite",
  "versionchange"
};

MDN

IDBCursor/advance

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/continue

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/continuePrimaryKey

In all current engines.

Firefox10+Safari10.1+Chrome58+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android51+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/delete

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/direction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/key

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/primaryKey

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/request

In all current engines.

Firefox77+Safari15+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/source

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/update

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor

In all current engines.

Firefox16+Safari8+Chrome24+
Opera44+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile43+
MDN

IDBCursorWithValue/value

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursorWithValue

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBDatabase/close

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/close_event

In all current engines.

Firefox50+Safari10.1+Chrome30+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile?
MDN

IDBDatabase/close_event

In all current engines.

Firefox50+Safari10.1+Chrome30+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile?
MDN

IDBDatabase/createObjectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/deleteObjectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/objectStoreNames

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/version

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/versionchange_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/versionchange_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBFactory/cmp

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory/databases

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

IDBFactory/deleteDatabase

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory/open

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBIndex/count

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/get

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getAll

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getAllKeys

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getKey

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/keyPath

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/multiEntry

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/objectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/openCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/openKeyCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/unique

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBKeyRange/bound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/includes

In all current engines.

Firefox47+Safari10.1+Chrome52+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lower

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lowerBound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lowerOpen

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/only_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upper

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upperBound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upperOpen

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBObjectStore/add

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/autoIncrement

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)?IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/clear

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/count

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/createIndex

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/delete

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/deleteIndex

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/get

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getAll

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android48+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getAllKeys

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android48+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getKey

In all current engines.

Firefox51+Safari10.1+Chrome48+
Opera45+Edge79+
Edge (Legacy)?IENone
Firefox for Android58+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile43+
MDN

IDBObjectStore/index

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/indexNames

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/keyPath

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/openCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/openKeyCursor

In all current engines.

Firefox44+Safari10.1+Chrome23+
Opera?Edge79+
Edge (Legacy)?IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/put

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBOpenDBRequest/blocked_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBOpenDBRequest/upgradeneeded_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBOpenDBRequest

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBRequest/error

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/error_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

IDBTransaction/error_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/readyState

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/result

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/source

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/success_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android25+Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBTransaction/abort

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/abort_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/commit

In all current engines.

Firefox74+Safari15+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/complete_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/db

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/durability

FirefoxNoneSafari15+Chrome83+
Opera70+Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/error

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/mode

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/objectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/objectStoreNames

In all current engines.

Firefox10+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBVersionChangeEvent/IDBVersionChangeEvent

In all current engines.

Firefox25+Safari10+Chrome41+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent/newVersion

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent/oldVersion

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile?
MDN

indexedDB

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?