This document defines APIs for a database of records holding simple values and hierarchical objects. Each record consists of a key and some value. Moreover, the database maintains indexes over records it stores. An application developer directly uses an API to locate records either by their key or by using an index. A query language can be layered on this API. An indexed database can be implemented using a persistent B-tree data structure.

This is the Second Edition of Indexed Database API. The First Edition became a W3C Recommendation on 8 January 2015.

This specification's bugs and issues are managed in GitHub (new bug, open bugs). Feature requests are tracked in the Indexed Database Features document.

Introduction

User agents need to store large numbers of objects locally in order to satisfy off-line data requirements of Web applications. [[WEBSTORAGE]] is useful for storing pairs of keys and their corresponding values. However, it does not provide in-order retrieval of keys, efficient searching over values, or storage of duplicate values for a key.

This specification provides a concrete API to perform advanced key-value data management that is at the heart of most sophisticated query processors. It does so by using transactional databases to store keys and their corresponding values (one or more per key), and providing a means of traversing keys in a deterministic order. This is often implemented through the use of persistent B-tree data structures that are considered efficient for insertion and deletion as well as in-order traversal of very large numbers of data records.

This specification defines one class of products:

Conforming user agent

A user agent MUST behave as described in this specification in order to be considered conformant.

User agents MAY implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.

A conforming Indexed Database API user agent MUST also be a conforming implementation of the IDL fragments of this specification, as described in the “Web IDL” specification. [[!WEBIDL]]

This specification uses both the terms "conforming user agent(s)" and "user agent(s)" to refer to this product class.

Dependencies

This specification relies on several other underlying specifications.

DOM-LEVEL-3-EVENTS
The terms default action and propagation path are defined by the Document Object Model (DOM) Level 3 Events Specification [[!DOM-LEVEL-3-EVENTS]].
DOM4
The types Event, EventTarget and DOMStringList are defined by the W3C DOM4 Specification [[!DOM4]].
HTML5
The terms and algorithms document, document base URL, event handler attributes, event handler event type, Function, strictly split a string, origin, same origin, structured clone, structured clone algorithm, task, task source, and queue a task are defined by the HTML 5 specification [[!HTML5]].
WebIDL
The terms and types throw, unrestricted double, DOMString, sequence<DOMString>, DOMException and and the interface definition language used in this spec are defined by [[!WEBIDL]].
WebWorkers
The terms Worker and WorkerUtils are defined by the WebWorkers specification [[!WEBWORKERS]].

Indexed Database API

A sorted list is a DOMStringList containing strings sorted in ascending order using the algorithm defined by step 4 of section 11.8.5, The Abstract Relational Comparison Algorithm of the ECMAScript Language Specification [[!ECMA-262]].

Constructs

Database

A database's origin is the same as the origin of the document or worker. Each origin has an associated set of databases.

The database origin is not affected by changes to document.domain.

Each origin has an associated set of databases. A database comprises one or more object stores which hold the data stored in the database.

A database has a name which identifies it within a specific origin. The name can be any string value, including the empty string, and stays constant for the lifetime of the database. Database names are always compared in a case-sensitive manner, as opaque sequences of 16-bit code units. Implementations MUST support all names.

If an implementation uses a storage mechanism which can't handle arbitrary database names, the implementation must use an escaping mechanism or something similar to map the provided name to a name that it can handle.

A database has a version. When a database is first created, its version is 0 (zero).

Each database has one version at a time; a database can't exist in multiple versions at once. The only way to change the version is using an upgrade transaction.

A database has a delete pending flag which is used during deletion. When a database is requested to be deleted the flag is set and all attempts at opening the database are stalled until the database can be deleted.

The act of opening a database creates a connection. There MAY be multiple connections to a given database at any given time. Each connection has a close pending flag which is initially unset.

A connection object can be used to manipulate the objects of that database. It is also the only way to obtain a transaction for that database.

When a connection is initially created it is in opened state. The connection can be closed through several means. If the execution context where the connection was created is destroyed (for example due to the user navigating away from that page), the connection is closed. The connection can also be closed explicitly using the steps for closing a database connection. When the connection is closed the close pending flag is always set if it hasn't already been.

A connection may be closed by a user agent in exceptional circumstances, for example due to loss of access to the file system or a permission change. If this occurs the user agent MUST run the steps for closing a database connection with the connection and with the forced flag set.

Object Store

An object store is the primary storage mechanism for storing data in a database.

Each database has a set of object stores. The set of object stores can be changed, but only using an upgrade transaction, i.e. in response to an upgradeneeded event. When a new database is created it doesn't contain any object stores.

An object store has a list of records which hold the data stored in the object store. Each record consists of a key and a value. The list is sorted according to key in ascending order. There can never be multiple records in a given object store with the same key.

An object store has a name. The name can be any string value, including the empty string, and stays constant for the lifetime of the object store. At any one time, the name is unique within the database to which it belongs. Object store names are always compared in a case-sensitive manner, as opaque sequences of 16-bit code units.

An object store optionally has a key path. If the object store has a key path it is said to use in-line keys. Otherwise it is said to use out-of-line keys.

An object store optionally has a key generator.

An object store can derive a key for a record from one of three sources:

  1. A key generator. A key generator generates a monotonically increasing numbers every time a key is needed.
  2. Keys can be derived via a key path.
  3. Keys can also be explicitly specified when a value is stored in the object store.

Values

Each record is associated with a value. Conforming user agents MUST support any ECMAScript [[!ECMA-262]] value supported by the structured clone algorithm [[!HTML5]]. This includes simple types such as String primitive values and Date objects as well as Object and Array instances, File objects, Blob objects, ImageData objects, and so on. Record values are stored and retrieved by value rather than by reference; later changes to a value have no effect on the record stored in the database.

Keys

In order to efficiently retrieve records stored in an indexed database, each record is organized according to its key.

A key has an associated type which is one of: number, date, string, or array.

A key also has an associated value, which will be either: an unrestricted double if type is number or date, a DOMString if type is string, or a list of other keys if type is array.

An ECMAScript [[!ECMA-262]] value can be converted to a key by following the steps to convert a value to a key.

An array key is a key with type array. The subkeys of an array key are the members of the array key's value list.

To compare two keys a and b, run these steps:

  1. Let ta be the type of a.
  2. Let tb be the type of b.
  3. If ta is array and tb is string, date or number, return 1.
  4. If tb is array and ta is string, date or number, return -1.
  5. If ta is string and tb is date or number, return 1.
  6. If tb is string and ta is date or number, return -1.
  7. If ta is date and tb is number, return 1.
  8. If tb is date and ta is number, return -1.
  9. Assert: ta and tb are equal.
  10. Let va be the value of a.
  11. Let vb be the value of b.
  12. Switch on ta:
    number
    date
    1. If the va is greater than vb, then return 1.
    2. If the va is less than vb, then return -1.
    3. Return 0
    string
    1. Let length be the lesser of va's length and vb's length.
    2. Let i be 0.
    3. While i is less than length:
      1. Let u be the code unit of va at index i
      2. Let v be the code unit of vb at index i
      3. If u is greater than v then return 1
      4. If u is less than v then return -1
      5. Increase i by 1
    4. If va's length is greater than vb's length, then return 1
    5. If va's length is less than vb's length, then return -1
    6. Return 0
    array
    1. Let length be the lesser of va's length and vb's length.
    2. Let i be 0.
    3. While i is less than length:
      1. Let u be the key in va at index i
      2. Let v be the key in vb at index i
      3. Let c be the result of recursively running the steps to compare two keys with u and v
      4. If c is not 0, return c
      5. Increase i by 1
    4. If va's length is greater than vb's length, then return 1
    5. If va's length is less than vb's length, then return -1
    6. Return 0

The key a is greater than the key b if the result of running the steps to compare two keys with a and b is 1.

The key a is less than the key b if the result of running the steps to compare two keys with a and b is -1.

The key a is equal to the key b if the result of running the steps to compare two keys with a and b is 0.

As a result of the above rules, negative infinity is the lowest possible value for a key. Numbers are less than dates. Dates are less than strings. Strings are less than arrays. There is no highest possible key value. This is because an array of any candidate highest key followed by another key is even higher.

Key Path

A key path is a DOMString or sequence<DOMString> that defines how to extract a key from a value. A valid key path is one of:

  • An empty DOMString.
  • An identifier, which is a DOMString matching the IdentifierName production from the ECMAScript Language Specification [[!ECMA-262]].
  • A DOMString consisting of two or more identifiers separated by periods (ASCII character code 46, U+002E FULL STOP).
  • A non-empty sequence<DOMString> containing only strings conforming to the above requirements.

Spaces are not allowed within a key path.

Key path values can only be accessed from properties explicitly copied by the structured clone algorithm, as well as the following type-specific properties:

TypeProperties
Blobsize, type
Filename, lastModified, lastModifiedDate
Arraylength
Stringlength

Index

It is sometimes useful to retrieve records in an object store through other means than their key. An index allows looking up records in an object store using properties of the values in the object stores records.

An index is a specialized persistent key-value storage and has a referenced object store. The index has a list of records which hold the data stored in the index. The records in an index are automatically populated whenever records in the referenced object store are inserted, updated or deleted. There can be several indexes referencing the same object store, in which changes to the object store cause all such indexes to get updated.

The values in the index's records are always values of keys in the index's referenced object store. The keys are derived from the referenced object store's values using a key path. If a given record with key X in the object store referenced by the index has the value A, and evaluating the index's key path on A yields the result Y, then the index will contain a record with key Y and value X.

Records in an index are said to have a referenced value. This is the value of the record in the index's referenced object store which has a key equal to the index's record's value. So in the example above, the record in the index whose key is Y and value is X has a referenced value of A.

Each record in an index references one and only one record in the index's referenced object store. However there can be multiple records in an index which reference the same record in the object store. And there can also be no records in an index which reference a given record in an object store.

The records in an index are always sorted according to the record's key. However unlike object stores, a given index can contain multiple records with the same key. Such records are additionally sorted according to the index's record's value (meaning the key of the record in the referenced object store).

An index has a name. The name can be any string value, including the empty string, and stays constant for the lifetime of the index. At any one time, the name is unique within index's referenced object store. Index names are always compared in a case-sensitive manner, as opaque sequences of 16-bit code units.

An index has a unique flag. When this flag is set, the index enforces that no two records in the index has the same key. If a record in the index's referenced object store is attempted to be inserted or modified such that evaluating the index's key path on the records new value yields a result which already exists in the index, then the attempted modification to the object store fails.

An index has a multiEntry flag. This flag affects how the index behaves when the result of evaluating the index's key path yields an array key. If the multiEntry flag is unset, then a single record whose key is an array key is added to the index. If the multiEntry flag is true, then the one record is added to the index for each of the subkeys.

Transactions

A transaction is used to interact with the data in a database. Whenever data is read or written to the database it is done by using a transaction.

Transactions offer some protection from application and system failures. A transaction may be used to store multiple data records or to conditionally modify certain data records. A transaction represents an atomic and durable set of data access and data mutation operations.

All transactions are created through a connection, which is the transaction's connection.

A transaction has a scope that determines the object stores with which the transaction may interact. A transaction's scope remains fixed for the lifetime of that transaction.

A transaction has a mode that determines which types of interactions can be performed upon that transaction. The mode is set when the transaction is created and remains fixed for the life of the transaction. A transaction's mode is one of the following:

"readonly"
The transaction is only allowed to read data. No modifications can be done by this type of transaction. This has the advantage that several read-only transactions can run at the same time even if their scopes are overlapping, i.e. if they are using the same object stores. This type of transaction can be created any time once a database has been opened.
"readwrite"
The transaction is allowed to read, modify and delete data from existing object stores. However object stores and indexes can't be added or removed. Multiple "readwrite" transactions can't run at the same time if their scopes are overlapping since that would mean that they can modify each other's data in the middle of the transaction. This type of transaction can be created any time once a database has been opened.
"versionchange"
The transaction is allowed to read, modify and delete data from existing object stores, and can also create and remove object stores and indexes. It is the only type of transaction that can do so. This type of transaction can't be manually created, but instead is created automatically when an upgradeneeded event is fired.

A transaction has an active flag, which determines if new requests can be made against the transaction. A transaction is said to be active if its active flag is set.

A transaction has a request list of requests which have been made against the transaction.

Transactions are expected to be short lived. This is encouraged by the automatic committing functionality described below.

Authors can still cause transactions to run for a long time; however, this usage pattern is not generally recommended as it can lead to a bad user experience.

The lifetime of a transaction is as follows:

  1. A transaction is created with a scope and a mode. When a transaction is created its active flag is initially set.
  2. The implementation MUST allow requests to be placed against the transaction whenever the active flag is set. This is the case even if the transaction has not yet been started. Until the transaction is started the implementation MUST NOT execute these requests; however, the implementation MUST keep track of the requests and their order. Requests may be placed against a transaction only while that transaction is active. If an attempt is made to place a request against a transaction when that transaction is not active, the implementation MUST reject the attempt by throwing a TransactionInactiveError exception.
  3. Once an implementation is able to enforce the constraints defined for the transaction scope and mode, defined below, the implementation MUST queue a task to start the transaction asynchronously.
  4. Once the transaction has been started the implementation can start executing the requests placed against the transaction. Unless otherwise defined, requests MUST be executed in the order in which they were made against the transaction. Likewise, their results MUST be returned in the order the requests were placed against a specific transaction. There is no guarantee about the order that results from requests in different transactions are returned. Similarly, the transaction modes ensure that two requests placed against different transactions can execute in any order without affecting what resulting data is stored in the database.
  5. A transaction can be aborted at any time before it is finished, even if the transaction isn't currently active or hasn't yet started. When a transaction is aborted the implementation MUST undo (roll back) any changes that were made to the database during that transaction. This includes both changes to the contents of object stores as well as additions and removals of object stores and indexes.
  6. A transaction can fail for reasons not tied to a particular request. For example due to IO errors when committing the transaction, or due to running into a quota limit where the implementation can't tie exceeding the quota to a partcular request. In this case the implementation MUST run the steps for aborting a transaction using the transaction as transaction and the appropriate error type as error. For example if quota was exceeded then QuotaExceededError should be used as error, and if an IO error happened, UnknownError should be used as error.
  7. When a transaction can no longer become active, the implementation MUST attempt to commit it, as long as the transaction has not been aborted. This usually happens after all requests placed against the transaction have been executed and their returned results handled, and no new requests have been placed against the transaction. When a transaction is committed, the implementation MUST atomically write any changes to the database made by requests placed against the transaction. That is, either all of the changes MUST be written, or if an error occurs, such as a disk write error, the implementation MUST NOT write any of the changes to the database. If such an error occurs, the implementation MUST abort the transaction by following the steps for aborting a transaction, otherwise it MUST commit the transaction by following the steps for committing a transaction.
  8. When a transaction is committed or aborted, it is said to be finished. If a transaction can't be finished, for example due to the implementation crashing or the user taking some explicit action to cancel it, the implementation MUST abort the transaction.

A read-only transaction is a transaction with mode "readonly".

A read/write transaction is a transaction with mode "readwrite".

The following constraints define when a transaction can be started:

  • Any number of read-only transactions are allowed to run concurrently, even if the transaction's scope overlap and include the same object stores. As long as a read-only transaction is running, the data that the implementation returns through requests created with that transaction MUST remain constant. That is, two requests to read the same piece of data MUST yield the same result both for the case when data is found and the result is that data, and for the case when data is not found and a lack of data is indicated.

    There are a number of ways that an implementation can ensure this. The implementation could prevent any read/write transaction, whose scope overlaps the scope of the read-only transaction, from starting until the read-only transaction finishes. Or the implementation could allow the read-only transaction to see a snapshot of the contents of the object stores which is taken when the read-only transaction started.

  • Similarly, implementations MUST ensure that a read/write transaction is only affected by changes to object stores that are made using the transaction itself. For example, the implementation MUST ensure that another transaction does not modify the contents of object stores in the read/write transaction's scope. The implementation MUST also ensure that if the read/write transaction completes successfully, the changes written to object stores using the transaction can be committed to the database without merge conflicts. An implementation MUST NOT abort a transaction due to merge conflicts.
  • If multiple read/write transactions are attempting to access the same object store (i.e. if they have overlapping scope), the transaction that was created first MUST be the transaction which gets access to the object store first. Due to the requirements in the previous paragraph, this also means that it is the only transaction which has access to the object store until the transaction is finished.
  • Any transaction created after a read/write transaction MUST see the changes written by the read/write transaction. So if a read/write transaction, A, is created, and later another transaction B, is created, and the two transactions have overlapping scopes, then B MUST see any changes made to any object stores that are part of that overlapping scope. Due to the requirements in the previous paragraph, this also means that the B transaction does not have access to any object stores in that overlapping scope until the A transaction is finished.

    Generally speaking, the above requirements mean that any transaction which has an overlapping scope with a read/write transaction and which was created after that read/write transaction, can't run in parallel with that read/write transaction.

  • User agents MUST ensure a reasonable level of fairness across transactions to prevent starvation. For example, if multiple read-only transactions are started one after another the implementation MUST NOT indefinitely prevent a pending read/write transaction from starting.
Upgrade Transactions

An upgrade transaction is a transaction with mode "versionchange".

An upgrade transaction is automatically created when running steps for running an upgrade transaction after a connection is opened to a database giving a greater version than the current version. This transaction will be active inside the upgradeneeded event handler, allowing the creation of new object stores and indexes.

An upgrade transaction is never run concurrently with other transactions. When a database is opened with a version number higher than the current version, a new upgrade transaction is automatically created and made available through the open request when the upgradeneeded event is fired. The upgradeneeded event isn't fired, and thus the upgrade transaction isn't started, until all other connections to the same database are closed. This ensures that all other transactions are finished.

As long as an upgrade transaction is running, attempts to open more connections to the same database are delayed, and any attempts to use the same connection to start additional transactions will result in an exception being thrown. Thus upgrade transactions not only ensure that no other transactions are running concurrently, but also ensure that no other transactions are queued against the same database as long as the transaction is running.

Requests

Each asynchronous operation on a database is done using a request. Every request represents one operation.

A request has a done flag which is initially unset.

A request has a source object.

A request has a result and an error, neither of which are accessible until the done flag is set.

A request has a transaction which is initially null. This will be set when a request is placed against a transaction using the steps for asynchronously executing a request.

When a request is made, a new request is returned with its done flag unset. If a request completes successfully, the done flag is set, the result is set to the result of the request, and an event with type success is fired at the request.

If an error occurs while performing the operation, the done flag is set, the error is set to the error, and an event with type error is fired at the request.

An open request is a special type of request used when opening a connection or deleting a database. In addition to success and error events, blocked and upgradeneeded may be fired at an open request to indicate progress.

The source of an open request is always null.

The transaction of an open request is null unless an upgradeneeded event has been fired.

Key Range

Records can be retrieved from object stores and indexes using either keys or key ranges. A key range is a continuous interval over some data type used for keys.

A key range has an associated lower bound (null or a key).

A key range has an associated upper bound (null or a key).

A key range has an associated lower open flag. Unless otherwise stated it is unset.

A key range has an associated upper open flag. Unless otherwise stated it is unset.

A key range MAY have a lower bound equal to its upper bound. A key range MUST NOT have a lower bound greater than its upper bound.

A key range containing only key has both lower bound and upper bound equal to key.

A key is in a key range if both of the following conditions are fulfilled:

An unbounded key range is a key range that has both lower bound and upper bound equal to null. All keys are in an unbounded key range.

The steps to convert a value to a key range with value and optional null allowed flag are as follows:

  1. If value is a key range, return value.
  2. If value is undefined or is null, then throw a DataError exception if null disallowed flag is set, or return an unbounded key range otherwise.
  3. Let key be the result of running the steps to convert a value to a key with value. If this throws an exception, rethrow it.
  4. If key is invalid, throw a DataError exception.
  5. Return a key range containing only key.

Cursor

A cursor is used to iterate over a range of records in an index or an object store in a specific direction.

A cursor has a range of records in either an index or an object store.

A cursor has a source that indicates which index or an object store is associated with the records over which the cursor is iterating.

A cursor has a direction that determines whether it moves in monotonically increasing or decreasing order of the record keys when iterated, and if it skips duplicated values when iterating indexes. The direction of a cursor also determines if the cursor initial position is at the start of its source or at its end. A cursor's direction is one of the following:

"next"
This direction causes the cursor to be opened at the start of the source. When iterated, the cursor should yield all records, including duplicates, in monotonically increasing order of keys.
"nextunique"
This direction causes the cursor to be opened at the start of the source. When iterated, the cursor should not yield records with the same key, but otherwise yield all records, in monotonically increasing order of keys. For every key with duplicate values, only the first record is yielded. When the source is an object store or an index with the unique flag set, this direction has exactly the same behavior as "next".
"prev"
This direction causes the cursor to be opened at the end of the source. When iterated, the cursor should yield all records, including duplicates, in monotonically decreasing order of keys.
"prevunique"
This direction causes the cursor to be opened at the end of the source. When iterated, the cursor should not yield records with the same key, but otherwise yield all records, in monotonically decreasing order of keys. For every key with duplicate values, only the first record is yielded. When the source is an object store or an index with the unique flag set, this direction has exactly the same behavior as "prev".

A cursor has a position within its range. It is possible for the list of records which the cursor is iterating over to change before the full range of the cursor has been iterated. In order to handle this, cursors maintain their position not as an index, but rather as a key of the previously returned record. For a forward iterating cursor, the next time the cursor is asked to iterate to the next record it returns the record with the lowest key greater than the one previously returned. For a backwards iterating cursor, the situation is opposite and it returns the record with the highest key less than the one previously returned.

For cursors iterating indexes the situation is a little bit more complicated since multiple records can have the same key and are therefore also sorted by value. When iterating indexes the cursor also has an object store position, which indicates the value of the previously found record in the index. Both position and the object store position are used when finding the next appropriate record.

A cursor has a key and a value which represent the key and the value of the last iterated record.

A cursor has a got value flag. When this flag unset, the cursor is either in the process of loading the next value or it has reached the end of its range. When it is set, it indicates that the cursor is currently holding a value and that it is ready to iterate to the next one.

If the source of a cursor is an object store, the effective object store of the cursor is that object store and the effective key of the cursor is the cursor's position. If the source of a cursor is an index, the effective object store of the cursor is that index's referenced object store and the effective key is the cursor's object store position.

A cursor also has a key only flag, that indicates whether the cursor's value is exposed via the API. Unless stated otherwise it is unset.

Key Generators

When a object store is created it can be specified to use a key generator. A key generator keeps an internal current number. The current number is always a positive integer. Whenever the key generator is used to generate a new key, the generator's current number is returned and then incremented to prepare for the next time a new key is needed. Implementations MUST use the following rules for generating numbers when a key generator is used.

  • Every object store that uses key generators use a separate generator. I.e. interacting with one object store never affects the key generator of any other object store.
  • The current number of a key generator is always set to 1 when the object store for that key generator is first created.
  • When a key generator is used to generate a new key for a object store, the key generator's current number is used as the new key value and then the key generator's current number is increased by 1.
  • When a record is stored and a key is specified in the call to store the record, if type of the key is number and the value is greater than or equal to the key generator's current number, then the key generator's current number is set to the smallest integer number greater than the explicit key. A key can be specified both for object stores which use in-line keys, by setting the property on the stored value which the object store's key path points to, and for object stores which use out-of-line keys, by passing a key argument to the call to store the record.

    Only specified keys of type number may affect the current number of the key generator. Keys of type date, array (regardless of the other keys they contain), or string (regardless of whether they could be parsed as numbers) have no effect on the current number of the key generator. Keys of type number with value less than 1 do not affect the current number since they are always lower than the current number.

  • Modifying a key generator's current number is considered part of a database operation. This means that if the operation fails and the operation is reverted, the current number is reverted to the value it had before the operation started. This applies both to modifications that happen due to the current number getting increased by 1 when the key generator is used, and to modifications that happen due to a record being stored with a key value specified in the call to store the record.
  • Likewise, if a transaction is aborted, the current number of the key generator for each object store in the transaction's scope is reverted to the value it had before the transaction was started.
  • When the current number of a key generator reaches above the value 253 (9007199254740992) any attempts to use the key generator to generate a new key will result in a ConstraintError. It is still possible to insert records into the object store by specifying an explicit key, however the only way to use a key generator again for the object store is to delete the object store and create a new one.

    As long as key generators are used in a normal fashion this will not be a problem. If you generate a new key 1000 times per second day and night, you won't run into this limit for over 285000 years.

  • The current number for a key generator never decreases, other than as a result of database operations being reverted. Deleting a record from an object store never affects the object store's key generator. Even clearing all records from an object store, for example using the clear() function, does not affect the current number of the object store's key generator.

A practical result of this is that the first key generated for an object store is always 1 (unless a higher numeric key is inserted first) and the key generated for an object store is always a positive integer higher than the highest numeric key in the store. The same key is never generated twice for the same object store unless a transaction is rolled back.

Exceptions

Each of the exceptions defined in this document is a DOMException with a specific type. The exception types and properties such as code value are defined in [[!WEBIDL]].

Type Description
AbortError A request was aborted.
ConstraintError A mutation operation in the transaction failed because a constraint was not satisfied.
DataCloneError The data being stored could not be cloned by the internal structured cloning algorithm.
DataError Data provided to an operation does not meet requirements.
InvalidAccessError An invalid operation was performed on an object.
InvalidStateError An operation was called on an object on which it is not allowed or at a time when it is not allowed, or if a request is made on a source object that has been deleted or removed.
NotFoundError The operation failed because the requested database object could not be found.
QuotaExceededError The operation failed because there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database.
SyntaxError The keyPath argument contains an invalid key path.
ReadOnlyError The mutating operation was attempted in a read-only transaction.
TransactionInactiveError A request was placed against a transaction which is currently not active, or which is finished.
UnknownError The operation failed for reasons unrelated to the database itself and not covered by any other errors.
VersionError An attempt was made to open a database using a lower version than the existing version.

API

The API methods return without blocking the calling thread. All asynchronous operations immediately return an IDBRequest instance. This object does not initially contain any information about the result of the operation. Once information becomes available, an event is fired on the request and the information becomes available through the properties of the IDBRequest instance.

The task source for these tasks is the database access task source.

The IDBRequest interface

The IDBRequest interface provides the means to access results of asynchronous requests to databases and database objects using event handler attributes [[!HTML5]].

Every method for making asynchronous requests returns an IDBRequest object that communicates back to the requesting application through events. This design means that any number of requests can be active on any database at a time.

readonly attribute any result
If the done flag is unset, the result attribute's getter throws an InvalidStateError exception. Otherwise, the result attribute's getter must return the result of the request, or undefined if the request resulted in an error.
readonly attribute DOMException error
If the done flag is unset, the error attribute's getter throws an InvalidStateError exception. Otherwise, the error attribute's getter must return the error of the request, or null if no error occurred.
readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source
The source attribute's getter must return the source of the request, or null if no source is set.
readonly attribute IDBTransaction transaction
The transaction attribute's getter must return the transaction of the request. This property can be null for certain requests, such as for request returned from IDBFactory.open.
readonly attribute IDBRequestReadyState readyState
The readyState attribute's getter must return "pending" if the done flag is unset, and "done" otherwise.
attribute EventHandler onsuccess
The event handler for the success event.
attribute EventHandler onerror
The event handler for the error event.

Methods on IDBFactory that return a open request use an extended interface to allow listening to the blocked event and upgradeneeded event.

attribute EventHandler onblocked
The event handler for the blocked event.
attribute EventHandler onupgradeneeded
The event handler for the upgradeneeded event.
pending
The done flag of the request is unset.
done
The done flag of the request is set.

Event interfaces

This specification fires events with the following custom interfaces:

readonly attribute unsigned long long oldVersion
The previous version of the database.
readonly attribute unsigned long long? newVersion
The new version of the database, or null if the database is being deleted. See the steps for running an upgrade transaction.

The oldVersion and newVersion attributes must return the value they were initialized to.

Events are constructed as defined in Constructing events, in [[!DOM4]].

unsigned long long oldVersion = 0
unsigned long long? newVersion = null

To fire a version change event named e at target given oldVersion and newVersion, dispatch an event at target. The event MUST use the IDBVersionChangeEvent interface with its type set to e, its oldVersion attribute set to oldVersion, and its newVersion attribute set to newVersion. This event MUST NOT bubble or be cancelable. The propagation path for the event is just target.

The IDBFactory interface

Database objects are accessed through methods on the IDBFactory interface. A single object implementing this interface is present in the global scope of environments that support Indexed DB operations.

readonly attribute IDBFactory indexedDB
This attribute provides applications a mechanism for accessing capabilities of indexed databases.

IDBOpenDBRequest open()
The open(name, version) method, when invoked, must run these steps:
  1. If version is 0 (zero), throw a TypeError.
  2. Let request be a new open request.
  3. Queue a task to run these substeps:
    1. Let result be the result of running the steps for opening a database, with the origin of the IDBEnvironment used to access this IDBFactory, name, version if given and undefined otherwise, and request.

      If version is not given and a database with that name already exists, a connection will be opened without changing the version. If version is not given and no database with that name exists, a new database will be created with version equal to 1.

    2. If result is an error, set the error of request to result and dispatch an event at request. The event must use the Event interface and set the type attribute to "error". The event does bubble but is not cancelable. The propagation path of the event is just request.
    3. Otherwise, set the result of request to result and dispatch an event at request. The event must use the Event interface and set the type attribute to "success". The event does not bubble and is not cancelable. The propagation path of the event is just request. If the steps above resulted in an upgrade transaction being run, then firing the "success" event MUST be done after the upgrade transaction completes.

      The last requirement is to ensure that in case another version upgrade is about to happen, the success event is fired on the connection first so that the script gets a chance to register a listener for the versionchange event.

      The firing of "success" or "error" events do not follow the normal steps to fire a success event or fire an error event as there is no active transaction at the time when they fire.

  4. Return a new IDBOpenDBRequest object for request.
DOMString name
The name for the database
[EnforceRange] optional unsigned long long version
The version for the database
IDBOpenDBRequest deleteDatabase()
The deleteDatabase(name) method, when invoked, must run these steps:
  1. Let request be a new open request.
  2. Queue a task to run these substeps:
    1. Let result be the result of running the steps for deleting a database, with the origin of the IDBEnvironment used to access this IDBFactory, name, and request.
    2. If result is an error set the error of request to result and dispatch an event at request. The event must use the Event interface and set the type attribute to "error". The event does bubble but is not cancelable. The propagation path of the event is just request.
    3. Otherwise, set the result of request to undefined and fire a version change event named success at request with database version and null.

      The firing of "success" or "error" events do not follow the normal steps to fire a success event or fire an error event as there is no active transaction at the time when they fire.

  3. Return a new IDBOpenDBRequest object for request.
DOMString name
The name of the database to delete
short cmp()
The cmp(first, second) method, when invoked, must run these steps:
  1. Let a be the result of running the steps to convert a value to a key with first. If this throws an exception, rethrow it.
  2. If a is invalid, throw a DataError exception.
  3. Let b be the result of running the steps to convert a value to a key with second. If this throws an exception, rethrow it.
  4. If b is invalid, throw a DataError exception.
  5. Return the results of running the steps to compare two keys with a and b.
any first
The first key to compare.
any second
The second key to compare.

The IDBDatabase interface

The IDBDatabase interface represents a connection to a database.

An IDBDatabase object must not be garbage collected if its associated connection's close pending flag is unset and it has one or more event listeners registers whose type is one of abort, error, or versionchange. If an IDBDatabase object is garbage collected, the associated connection must be closed.

readonly attribute DOMString name
The name attribute's getter must return the name of the connected database. The attribute MUST return this name even if the close pending flag is set on the connection. In other words, the value of this attribute stays constant for the lifetime of the IDBDatabase instance.
readonly attribute unsigned long long version
The version attribute's getter must return the version of the database when the connection was created. When an IDBDatabase instance is created, this is always the number given as the version argument given to the open call used to create the IDBDatabase instance. This value remains constant for the lifetime of the IDBDatabase object. If the connection is closed, this attribute represents a snapshot of the version that the database had when the connection was closed. Even if another connection is later used to modify the version, that attribute on closed instances are not changed.
readonly attribute DOMStringList objectStoreNames

The objectStoreNames attribute's getter must return a sorted list of the names of the object stores currently in the connected database.

Once the close pending flag is set on the connection, this attribute MUST return a snapshot of the list of names of the object stores taken at the time when the close method was called, even if other connections are later used to change the set of object stores that exist in the database. In other words, the value of this attribute stays constant for the lifetime of the IDBDatabase instance, except during an upgrade transaction if createObjectStore or deleteObjectStore is called on this IDBDatabase instance itself.

IDBObjectStore createObjectStore()

This method creates and returns a new object store with the given name in the connected database. Note that this method must only be called from within an upgrade transaction.

The createObjectStore(name, options) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a not an upgrade transaction, throw an InvalidStateError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If an object store named name already exists in this database, throw a ConstraintError exception.
  4. Let keyPath be options's keyPath member if it is not undefined or null, or null otherwise.
  5. If keyPath is not null and is not a valid key path, throw a SyntaxError exception.
  6. Let autoIncrement be set if options's autoIncrement member is true, or unset otherwise.
  7. If autoIncrement is set and keyPath is an empty string or any sequence (empty or otherwise), throw an InvalidAccessError exception.
  8. Create a new object store in the connected database and return an IDBObjectStore object representing it. Set the created object store's name to name. If autoIncrement is set, then the created object store uses a key generator. If keyPath is not null, set the created object store's key path to keyPath.

This method synchronously modifies the IDBDatabase.objectStoreNames property on the IDBDatabase instance on which it was called.

In some implementations it is possible for the implementation to run into problems after queuing a task to create the object store after the createObjectStore function has returned. For example in implementations where metadata about the newly created object store is inserted into the database asynchronously, or where the implementation might need to ask the user for permission for quota reasons. Such implementations MUST still create and return an IDBObjectStore object, and once the implementation determines that creating the object store has failed, it MUST abort the transaction using the steps for aborting a transaction using the appropriate error. For example if creating the object store failed due to quota reasons, QuotaExceededError MUST be used as error.

DOMString name
The name of a new object store
optional IDBObjectStoreParameters options
A dictionary of optional parameters to this function. keyPath specifies the key path of the new object store. If the attribute is null, no key path is specified and thus keys are out-of-line. autoIncrement specifies whether the object store created should have a key generator.
void deleteObjectStore()

This method destroys the object store with the given name in the connected database. Note that this method must only be called from within an upgrade transaction.

The deleteObjectStore(name) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a not an upgrade transaction, throw an InvalidStateError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If there is no object store named name in the connected database, throw a NotFoundError exception.
  4. Destroy the object store named name in the connected database.

This method synchronously modifies the IDBDatabase.objectStoreNames property on the IDBDatabase instance on which it was called.

DOMString name
The name of an existing object store
IDBTransaction transaction()
The transaction(storeNames, mode) method, when invoked, must run these steps:
  1. If mode parameter is not "readonly" or "readwrite", throw a TypeError.
  2. If this method is called on IDBDatabase object for which an upgrade transaction is still running, throw an InvalidStateError exception.
  3. If this method is called on an IDBDatabase instance where the close pending flag is set, throw an InvalidStateError exception.
  4. Let scope be the set of unique strings in storeNames if it is a sequence, or a set containing one string equal to storeNames otherwise.
  5. If any string in scope is not the name of an object store in the connected database, throw a NotFoundError exception.
  6. If scope is empty, throw an InvalidAccessError exception.
  7. Let transaction be a new transaction with connection, mode and the set of object stores named in scope.
  8. When control is returned to the event loop, the implementation MUST unset the active flag.

    Specify this more precisely in terms of tasks and microtasks.

  9. Return an IDBTransaction object representing transaction.

The created transaction will follow the transaction lifetime rules.

(DOMString or sequence<DOMString>) storeNames
The names of object stores in the scope of the new transaction
optional IDBTransactionMode mode = "readonly"
The mode for isolating access to data inside the given object stores. If this parameter is not given, the default access mode is "readonly".
void close()
The close() method, when invoked, must run these steps:
  1. Run the steps for closing a database connection with this connection.
attribute EventHandler onabort
The event handler for the abort event.
attribute EventHandler onclose
The event handler for the close event.
attribute EventHandler onerror
The event handler for the error event.
attribute EventHandler onversionchange
The event handler for the versionchange event.

The IDBObjectStore interface

The IDBObjectStore interface represents an object store. Note however that multiple instances of those interfaces representing the same object store can exist.

readonly attribute DOMString name
The name attribute's getter must return the name of this object store.
readonly attribute any keyPath

The keyPath attribute's getter must return the key path of this object store, or null if none.

The conversion is done following the normal [[!WEBIDL]] binding logic for DOMString and sequence<DOMString> values, as appropriate.

The returned value is not the same instance that was used when the object store was created. However, if this attribute returns an object (specifically an Array), it returns the same object instance every time it is inspected. Changing the properties of the object has no effect on the object store.

readonly attribute DOMStringList indexNames

The indexNames attribute's getter must return a sorted list of the names of indexes on objects in this object store.

Once the close pending flag is set on the connection, this attribute MUST return a snapshot of the list of names of the indexes taken at the time when the close method was called, even if other connections are later used to change the set of indexes that exist in the object store. In other words, the value of this attribute stays constant for the lifetime of the IDBObjectStore instance, except during an upgrade transaction if createIndex or deleteIndex is called on this IDBObjectStore instance itself.

readonly attribute IDBTransaction transaction
The transaction attribute's getter must return the transaction this object store belongs to.
readonly attribute boolean autoIncrement
The autoIncrement attribute's getter must return true of this object store has a key generator, and false otherwise.
IDBRequest put()
The put(value, key) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. If the object store uses in-line keys and key was given, throw a DataError exception.
  5. If the object store uses out-of-line keys and has no key generator and key was not given, throw a DataError exception.
  6. If key was given:
    1. Let r be the result of running the steps to convert a value to a key with key. If this throws an exception, rethrow it.
    2. If r is invalid, throw a DataError exception.
    3. Let key be r.
  7. Let clone be structured clone of value. If this throws an exception, rethrow the exception.
  8. If the object store uses in-line keys:
    1. Let kpk be the result of running the steps to extract a key from a value using a key path with clone and the object store's key path. If this throws an exception, rethrow it.
    2. If kpk is invalid, throw a DataError exception.
    3. If kpk is not failure, let key be kpk.
    4. Otherwise, if the object store does not have a key generator, throw a DataError exception.
  9. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for storing a record into an object store as operation, using this IDBObjectStore as store, the clone as value, key as key, and with the no-overwrite flag unset.
any value
The value to be stored in the record
optional any key
The key used to identify the record
IDBRequest add()
The add(value, key) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. If the object store uses in-line keys and key was given, throw a DataError exception.
  5. If the object store uses out-of-line keys and has no key generator and key was not given, throw a DataError exception.
  6. If key was given:
    1. Let r be the result of running the steps to convert a value to a key with key. If this throws an exception, rethrow it.
    2. If r is invalid, throw a DataError exception.
    3. Let key be r.
  7. Let clone be structured clone of value. If this throws an exception, rethrow the exception.
  8. If the object store uses in-line keys:
    1. Let kpk be the result of running the steps to extract a key from a value using a key path with clone and the object store's key path. If this throws an exception, rethrow it.
    2. If kpk is invalid, throw a DataError exception.
    3. If kpk is not failure, let key be kpk.
    4. Otherwise, if the object store does not have a key generator, throw a DataError exception.
  9. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for storing a record into an object store as operation, using this IDBObjectStore as store, clone as value, key as key, and with the no-overwrite flag set.

To determine if a transaction has completed successfully, listen to the transaction’s complete event rather than the IDBObjectStore.add request’s success event, because the transaction may still fail after the success event fires.

any value
The value to be stored in the record
optional any key
The key used to identify the record
IDBRequest delete()
The delete(query) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. If this throws an exception, rethrow it.
  5. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for deleting records from an object store as operation, using this IDBObjectStore as store and range.

Unlike other methods which take keys or key ranges, this method does not allow null to be given as key. This is to reduce the risk that a small bug would clear a whole object store.

any query
Key or IDBKeyRange identifying the records to be deleted.
IDBRequest clear()
The clear() method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for clearing an object store as operation, using this IDBObjectStore as store.
IDBRequest get()
The get(query) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for retrieving a value from an object store as operation, using this IDBObjectStore as store and range.

This function produces the same result if a record with the given key doesn't exist as when a record exists, but has undefined as value. If you need to tell the two situations apart, you can use openCursor with the same key. This will return a cursor with undefined as value if a record exists, or no cursor if no such record exists.

any query
Key identifying the record to be retrieved. This can also be an IDBKeyRange in which case the function retrieves the first existing value in that range.
IDBRequest getAll()
The getAll(query, count) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for retrieving multiple values from an object store as operation, using this IDBObjectStore as store, range, and count if given.
optional any query
Key or IDBKeyRange identifying the records to be retrieved. If null or not given, an unbounded key range is used.
[EnforceRange] optional unsigned long count
The maximum number of record values to be retrieved. If more records are in range than are specified, only the first count will be retrieved. If not specified, all records in range will be retrieved.
IDBRequest getAllKeys()
The getAllKeys(query, count) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for retrieving multiple keys from an object store as operation, using this IDBObjectStore as store, range, and count if given.
optional any query
Key or IDBKeyRange identifying the records to be retrieved. If null or not given, an unbounded key range is used.
[EnforceRange] optional unsigned long count
The maximum number of record keys to be retrieved. If more keys are in range than are specified, only the first count will be retrieved. If not specified, all keys in range will be retrieved.
IDBRequest count()
The count(query) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps to count the records in a range as operation, with this object store as source and range.
optional any query
The key or IDBKeyRange indicating the records to count. If null or not given, an unbounded key range is used.
IDBRequest openCursor()
The openCursor(query, direction) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Let cursor be a new cursor with an undefined position, direction set to direction, got value flag unset, and undefined key and value. The source of cursor is the IDBObjectStore this function was called on. The range of cursor is range.
  5. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for iterating a cursor with cursor as operation.
optional any query
The key or IDBKeyRange to use as the cursor's range. If null or not given, an unbounded key range is used.
optional IDBCursorDirection direction = "next"
The cursor's required direction
IDBRequest openKeyCursor()
The openKeyCursor(query, direction) method, when invoked, must run these steps:
  1. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Let cursor be a new cursor with an undefined position, direction set to direction, got value flag unset, and undefined key and value. The source of cursor is the IDBObjectStore this function was called on. The range of cursor is range. The key only flag of cursor is set.
  5. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBObjectStore as source and the steps for iterating a cursor with cursor as operation.
optional any query
The key or IDBKeyRange to use as the cursor's range. If null or not given, an unbounded key range is used.
optional IDBCursorDirection direction = "next"
The cursor's required direction
IDBIndex createIndex()

This method creates and returns a new index with the given name in the object store. Note that this method must only be called from within an upgrade transaction.

The createIndex(name, keyPath, options) method, when invoked, must run these steps:
  1. If this object store's database is not running an upgrade transaction, throw an InvalidStateError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. If an index named name already exists in this object store, throw a ConstraintError exception.
  5. If keyPath is not a valid key path, throw a SyntaxError exception.
  6. Let unique be set if options's unique member is true, and unset otherwise.
  7. Let multiEntry be set if options's multiEntry member is true, and unset otherwise.
  8. If keyPath is a sequence and multiEntry is set, throw an InvalidAccessError exception.
  9. Create a new index in the object store and return an IDBIndex object representing it. Set the created index's name to name and key path to keyPath. If unique is set, set the created index's unique flag. If multiEntry is set, set the created index's multiEntry flag.

The index that is requested to be created can contain constraints on the data allowed in the index's referenced object store, such as requiring uniqueness of the values referenced by the index's keyPath. If the referenced object store already contains data which violates these constraints, this MUST NOT cause the implementation of createIndex to throw an exception or affect what it returns. The implementation MUST still create and return an IDBIndex object, and the implementation MUST queue a task to abort the upgrade transaction which was used for the createIndex call.

This method synchronously modifies the IDBObjectStore.indexNames property on the IDBObjectStore instance on which it was called. Although this method does not return an IDBRequest object, the index creation itself is processed as an asynchronous request within the upgrade transaction.

In some implementations it is possible for the implementation to asynchronously run into problems creating the index after the createIndex function has returned. For example in implementations where metadata about the newly created index is queued up to be inserted into the database asynchronously, or where the implementation might need to ask the user for permission for quota reasons. Such implementations MUST still create and return an IDBIndex object, and once the implementation determines that creating the index has failed, it MUST abort the transaction using the steps for aborting a transaction using an appropriate error as error. For example if creating the index failed due to quota reasons, QuotaExceededError MUST be used as error and if the index can't be created due to unique flag constraints, ConstraintError MUST be used as error.

DOMString name
The name of a new index
(DOMString or sequence<DOMString>) keyPath
The key path used by the new index.
optional IDBIndexParameters options
A dictionary of optional parameters to this function. unique specifies whether the index's unique flag is set. multiEntry specifies whether the index's multiEntry flag is set.
IDBIndex index()
The index(name) method, when invoked, must run these steps:
  1. If there is no index named name in this object store, throw a NotFoundError exception.
  2. If the object store has been deleted, throw an InvalidStateError exception.
  3. Return an IDBIndex representing the index associated with this object store named name. Every call to this method on the same IDBObjectStore instance with the same name returns the same IDBIndex instance.

    The retured IDBIndex instance is specific to this IDBObjectStore instance. If this method is called on a different IDBObjectStore instance, a different IDBIndex instance is returned. A result of this is that different IDBTransaction instances use different IDBIndex instances to represent the same index.

DOMString name
The name of an existing index
void deleteIndex()

This method destroys the index with the given name in the object store. Note that this method must only be called from within an upgrade transaction.

The deleteIndex(name) method, when invoked, must run these steps:
  1. If this object store's database is not running an upgrade transaction, throw an InvalidStateError exception.
  2. If the transaction this IDBObjectStore belongs to is not active, throw a TransactionInactiveError exception.
  3. If the object store has been deleted, throw an InvalidStateError exception.
  4. If there is no index named name in the object store, throw a NotFoundError exception.
  5. Destroy the index named name in the object store.

This method synchronously modifies the IDBObjectStore.indexNames property. However it only changes the IDBObjectStore.indexNames property on the IDBObjectStore instance on which it was called. Although this method does not return an IDBRequest object, the index destruction itself is processed as an asynchronous request within the upgrade transaction.

DOMString indexName
The name of an existing index
(DOMString or sequence<DOMString>)? keyPath = null
boolean autoIncrement = false

The IDBIndex interface

The IDBIndex interface provides access to the metadata of an index. Note however that multiple instances of those interfaces representing the same index can exist.

readonly attribute DOMString name
The name attribute's getter must return the name of this index.
readonly attribute IDBObjectStore objectStore
The objectStore attribute's getter must return a reference to the IDBObjectStore instance for the referenced object store in this IDBIndex's transaction. This MUST return the same IDBObjectStore instance as was used to get a reference to this IDBIndex.
readonly attribute any keyPath

The keyPath attribute's getter must return a value representing the key path of this index, or null if none.

The conversion is done following the normal [[!WEBIDL]] binding logic for DOMString and sequence<DOMString> values, as appropriate.

The returned value is not the same instance that was used when the index was created. However, if this attribute returns an object (specifically an Array), it returns the same object instance every time it is inspected. Changing the properties of the object has no effect on the index.

readonly attribute boolean multiEntry
The multiEntry attribute's getter must return true if the multiEntry flag of this index is set, and false otherwise.
readonly attribute boolean unique
The unique attribute's getter must return true if the unique flag of this index is set, and false otherwise.
IDBRequest get()
The get(query) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for retrieving a referenced value from an index as operation, using this IDBIndex as index and range.

This function produces the same result if a record with the given key doesn't exist as when a record exists, but has undefined as value. If you need to tell the two situations apart, you can use openCursor with the same key. This will return a cursor with undefined as value if a record exists, or no cursor if no such record exists.

any query
Key identifying the record to be retrieved. This can also be an IDBKeyRange in which case the function retrieves the first existing value in that range.
IDBRequest getKey()

Gets the key of the record from the referenced object store entry.

The getKey(query) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query and null disallowed flag set. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for retrieving a value from an index as operation, using this IDBIndex as index and range.
any query
Key identifying the record to be retrieved. This can also be an IDBKeyRange in which case the function retrieves the first existing value in that range.
IDBRequest getAll()
The getAll(query, count) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for retrieving multiple referenced values from an index as operation, using this IDBIndex as index, range, and count if given.
optional any query
Key or IDBKeyRange identifying the records to be retrieved. If null or not given, an unbounded key range is used.
[EnforceRange] optional unsigned long count
The maximum number of record values to be retrieved. If more records are in range than are specified, only the first count will be retrieved. If not specified, all records in range will be retrieved.
IDBRequest getAllKeys()
The getAllKeys(query, count) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for retrieving multiple values from an index as operation, using this IDBIndex as index, range, and count if given.
optional any query
Key or IDBKeyRange identifying the records to be retrieved. If null or not given, an unbounded key range is used.
[EnforceRange] optional unsigned long count
The maximum number of record keys to be retrieved. If more keys are in range than are specified, only the first count will be retrieved. If not specified, all keys in range will be retrieved.
IDBRequest count()
The count(query) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps to count the records in a range as operation, with this index as source and range.
optional any query
Key or IDBKeyRange identifying the records to be counted. If null or not given, an unbounded key range is used.
IDBRequest openCursor()
The openCursor(query, direction) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Let cursor be a new cursor with an undefined position, direction set to direction, got value flag unset, and undefined key and value. The source of cursor is the IDBIndex this function was called on. The range of cursor is range.
  5. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for iterating a cursor with cursor as operation.
optional any query
The key or IDBKeyRange to use as the cursor's range. If null or not given, all records are counted.
optional IDBCursorDirection direction = "next"
The cursor's required direction
IDBRequest openKeyCursor()
The openKeyCursor(query, direction) method, when invoked, must run these steps:
  1. If the transaction this IDBIndex belongs to is not active, throw a TransactionInactiveError exception.
  2. If the index or the index's object store has been deleted, throw an InvalidStateError exception.
  3. Let range be the result of running the steps to convert a value to a key range with query. If this throws an exception, rethrow it.
  4. Let cursor be a new cursor with an undefined position, direction set to direction, got value flag unset, and undefined key and value. The source of cursor is the IDBIndex this function was called on. The range of cursor is range. The key only flag of cursor is set.
  5. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBIndex as source and the steps for iterating a cursor with cursor as operation.
optional any query
The key or IDBKeyRange to use as the cursor's range. If null or not given, all records are counted.
optional IDBCursorDirection direction = "next"
The cursor's required direction
boolean unique = false
boolean multiEntry = false

The IDBKeyRange interface

The IDBKeyRange interface represents a key range.

readonly attribute any lower
The lower attribute's getter must return result of running the steps to convert a key to a value with the lower bound if it is not null, or undefined otherwise.
readonly attribute any upper
The upper attribute's getter must return the result of running the steps to convert a key to a value with the upper bound if it is not null, or undefined otherwise.
readonly attribute boolean lowerOpen
The lowerOpen attribute's getter must return true if the lower open flag is set, and false otherwise.
readonly attribute boolean upperOpen
The upperOpen attribute's getter must return true if the upper open flag is set, and false otherwise.
static IDBKeyRange only()
The only(value) method, when invoked, must run these steps:
  1. Let key be the result of running the steps to convert a value to a key with value. If this throws an an exception, rethrow it.
  2. If key is invalid, throw a DataError exception.
  3. Create and return a new key range containing only key.
any value
The only value
static IDBKeyRange lowerBound()
The lowerBound(lower, lowerOpen) method, when invoked, must run these steps:
  1. Let lowerKey be the result of running the steps to convert a value to a key with lower. If this throws an an exception, rethrow it.
  2. If lowerKey is invalid, throw a DataError exception.
  3. Create and return a new key range with lower bound set to lowerKey, lower open flag set if lowerOpen is true, upper bound set to null and upper open flag set.
any lower
The lower bound value
optional boolean open = false
Specify false if the lower-bound should be included in the key range. Specify true if the lower-bound value should be excluded from the key range. Defaults to false (lower-bound value is included).
static IDBKeyRange upperBound()
The upperBound(upper, upperOpen) method, when invoked, must run these steps:
  1. Let upperKey be the result of running the steps to convert a value to a key with upper. If this throws an an exception, rethrow it.
  2. If upperKey is invalid, throw a DataError exception.
  3. Create and return a new key range with lower bound set to null, lower open flag set, upper bound set if upperKey, and upper open flag set to upperOpen.
any upper
The upper bound value
optional boolean open = false
Specify false if the upper-bound should be included in the key range. Specify true if the upper-bound value should be excluded from the key range. Defaults to false (upper-bound value is included).
static IDBKeyRange bound()
The bound(lower, upper, lowerOpen, upperOpen) method, when invoked, must run these steps:
  1. Let lowerKey be the result of running the steps to convert a value to a key with lower. If this throws an an exception, rethrow it.
  2. If lowerKey is invalid, throw a DataError exception.
  3. Let upperKey be the result of running the steps to convert a value to a key with upper. If this throws an an exception, rethrow it.
  4. If upperKey is invalid, throw a DataError exception.
  5. If lowerKey is greater than upperKey, throw a DataError exception.
  6. Create and return a new key range with lower bound set to lowerKey, lower open flag set if lowerOpen is true, upper bound set to upperKey and upper open flag set if upperOpen is true.
any lower
The lower-bound value
any upper
The upper-bound value
optional boolean lowerOpen = false
Specify false if the lower-bound should be included in the key range. Specify true if the lower-bound value should be excluded from the key range. Defaults to false (lower-bound value is included).
optional boolean upperOpen = false
Specify false if the upper-bound should be included in the key range. Specify true if the upper-bound value should be excluded from the key range. Defaults to false (upper-bound value is included).

The IDBCursor interface

Cursor objects implement the IDBCursor interface. There is only ever one IDBCursor instance representing a given cursor. There is no limit on how many cursors can be used at the same time.

readonly attribute (IDBObjectStore or IDBIndex) source
The source attribute's getter must return the source of this cursor. This attribute never returns null or throws an exception, even if the cursor is currently being iterated, has iterated past its end, or its transaction is not active.
readonly attribute IDBCursorDirection direction
The direction attribute's getter must return the direction of the cursor.
readonly attribute any key
The key attribute's getter must return the result of running the steps to convert a key to a value with the cursor's current key. Note that if this property returns an object (e.g. a Date or Array), it returns the same object instance every time it is inspected, until the cursor's key is changed. This means that if the object is modified, those modifications will be seen by anyone inspecting the value of the cursor. However modifying such an object does not modify the contents of the database.
readonly attribute any primaryKey
The primaryKey attribute's getter must return the result of running the steps to convert a key to a value with the cursor's current effective key. Note that if this property returns an object (e.g. a Date or Array), it returns the same object instance every time it is inspected, until the cursor's effective key is changed. This means that if the object is modified, those modifications will be seen by anyone inspecting the value of the cursor. However modifying such an object does not modify the contents of the database.
IDBRequest update()
The update(value) method, when invoked, must run these steps:
  1. If the transaction this IDBCursor belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBCursor belongs to is not active, throw a TransactionInactiveError exception.
  3. If the cursor's source or effective object store has been deleted, throw an InvalidStateError exception.
  4. If this cursor's got value flag is unset, indicating that the cursor is being iterated or has iterated past its end, throw an InvalidStateError exception.
  5. If this cursor's key only flag is set, throw an InvalidStateError exception.
  6. Let clone be structured clone of value. If this throws an exception, rethrow the exception.
  7. If the effective object store of this cursor uses in-line keys
    1. Let kpk be the result of running the steps to extract a key from a value using a key path with clone and the key path of the effective object store. If this throws an exception, rethrow it.
    2. If kpk is failure, invalid, or not equal to the cursor's effective key, throw a DataError exception.
  8. Run steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBCursor as source and the steps for storing a record into an object store as operation, using this cursor's effective object store as store, the clone as value, this cursor's effective key as key, and with the no-overwrite flag unset.

A result of running the steps for storing a record into an object store is that if the record has been deleted since the cursor moved to it, a new record will be created.

any value
The new value to store at the current position.
void advance()
The advance(count) method, when invoked, must run these steps:
  1. If count is 0 (zero), throw a TypeError.
  2. If the transaction this IDBCursor belongs to is not active, throw a TransactionInactiveError exception.
  3. If the cursor's source or effective object store has been deleted, throw an InvalidStateError exception.
  4. If this cursor's got value flag is unset, indicating that the cursor is being iterated or has iterated past its end, throw an InvalidStateError exception.
  5. Unset the got value flag on the cursor.
  6. Let request be the request created when this cursor was created.
  7. Unset the done flag on request.
  8. Run the steps for asynchronously executing a request with the cursor's source as source and the steps for iterating a cursor with this cursor and count, and request.

Calling this method more than once before new cursor data has been loaded - for example, calling advance() twice from the same onsuccess handler - results in an InvalidStateError exception being thrown on the second call because the cursor's got value flag has been unset.

[EnforceRange] unsigned long count
The number of advances forward the cursor should make.
void continue()
The continue(key) method, when invoked, must run these steps:
  1. If the transaction this IDBCursor belongs to is not active, throw a TransactionInactiveError exception.
  2. If the cursor's source or effective object store has been deleted, throw an InvalidStateError exception.
  3. If this cursor's got value flag is unset, indicating that the cursor is being iterated or has iterated past its end, throw an InvalidStateError exception.
  4. If key is given:
    1. Let r be the result of running the steps to convert a value to a key with key. If this throws an exception, rethrow it.
    2. If r is invalid, throw a DataError exception.
    3. Let key be r.
    4. If key is less than or equal to this cursor's position and this cursor's direction is "next" or "nextunique", throw a DataError exception.
    5. If key is greater than or equal to this cursor's position and this cursor's direction is "prev" or "prevunique", throw a DataError exception.
  5. Unset the got value flag on the cursor.
  6. Let request be the request created when this cursor was created.
  7. Unset the done flag on request.
  8. Run the steps for asynchronously executing a request with the cursor's source as source and the steps for iterating a cursor with this cursor and key (if given), and request.

Calling this method more than once before new cursor data has been loaded - for example, calling continue() twice from the same onsuccess handler - results in an InvalidStateError exception being thrown on the second call because the cursor's got value flag has been unset.

optional any key
The next key to position this cursor at
IDBRequest delete()
The delete() method, when invoked, must run these steps:
  1. If the transaction this IDBCursor belongs to is a read-only transaction, throw a ReadOnlyError exception.
  2. If the transaction this IDBCursor belongs to is not active, throw a TransactionInactiveError exception.
  3. If the cursor's source or effective object store has been deleted, throw an InvalidStateError exception.
  4. If this cursor's got value flag is unset, indicating that the cursor is being iterated or has iterated past its end, throw an InvalidStateError exception.
  5. If this cursor's key only flag is set, throw an InvalidStateError exception.
  6. Run the steps for asynchronously executing a request and return the IDBRequest created by these steps. The steps are run with this IDBCursor as source and the steps for deleting records from an object store as operation, using this cursor's effective object store and effective key as store and key respectively.

A cursor that has the key only flag unset implements the IDBCursorWithValue interface as well.

readonly attribute any value
The value attribute's getter must return the cursor's current value. Note that if this property returns an object, it returns the same object instance every time it is inspected, until the cursor's value is changed. This means that if the object is modified, those modifications will be seen by anyone inspecting the value of the cursor. However modifying such an object does not modify the contents of the database.

The direction of an IDBCursor is represented by the IDBCursorDirection enumeration:

next
The direction of the cursor is "next"
nextunique
The direction of the cursor is "nextunique"
prev
The direction of the cursor is "prev"
prevunique
The direction of the cursor is "prevunique"

The IDBTransaction interface

Transaction objects implement the following interface:

readonly attribute DOMStringList objectStoreNames
The objectStoreNames attribute's getter must run the following steps:
  1. If this transaction is an upgrade transaction, return a sorted list of the names of the object stores in this transaction's connected database.
  2. Otherwise, return a sorted list of the names of the object stores in this transaction's scope.
readonly attribute IDBTransactionMode mode
The mode attribute's getter must return the mode of the transaction.
readonly attribute IDBDatabase db
The db attribute's getter must return the database connection of which this transaction is a part.
readonly attribute DOMException error
The error attribute's getter must run these steps:
  1. If this transaction is not finished, is finished but was successfully committed, or was aborted due to a call to the abort function, return null.
  2. If this transaction was aborted due to a failed request, return the request's error.
  3. Otherwise, this transaction was aborted due to an error when committing the transaction, and not due to a failed request. Return the reason for the transaction failure (e.g. QuotaExceededError or UnknownError).
IDBObjectStore objectStore()
The objectStore(name) method, when invoked, must run these steps:
  1. If there is no object store named name in this transaction's scope, throw a NotFoundError exception.
  2. If the transaction has finished, throw an InvalidStateError exception.
  3. Return an IDBObjectStore representing the object store in this transaction's scope, named name. Every call to this method on the same IDBTransaction instance with the same name returns the same IDBObjectStore instance.

    The returned IDBObjectStore instance is specific to this IDBTransaction. If this method is called on a different IDBTransaction, a different IDBObjectStore instance is returned.

DOMString name
The requested object store
void abort()
The abort() method, when invoked, must run these steps:
  1. If this transaction is finished, throw an InvalidStateError exception.
  2. Unset the transaction's active flag and run the steps for aborting a transaction with null as error.
attribute EventHandler onabort
The event handler for the abort event.
attribute EventHandler oncomplete
The event handler for the complete event.

To determine if a transaction has completed successfully, listen to the transaction’s complete event rather than the success event of a particular request, because the transaction may still fail after the success event fires.

attribute EventHandler onerror
The event handler for the error event.

The mode of an IDBTransaction is represented by the IDBTransactionMode enumeration:

readonly
A read-only transaction.
readwrite
A read/write transaction.
versionchange
An upgrade transaction.

Algorithms

Opening a database

The steps for opening a database are defined in the following steps. The algorithm in these steps takes four arguments: the origin which requested the database to be opened, a database name, a database version, and a request.

  1. Let db be the database named name in the origin origin, or null otherwise.
  2. If db is not null:
    1. Wait until the following conditions are all fulfilled:

      If several requests with the same origin and name are waiting due to the above conditions, and those connections have a higher version than the database's current version, then once any of those connections can proceed to the next step in this algorithm it will immediately start an upgrade transaction. This prevents the other connections from proceeding until that upgrade transaction is finished.

    2. If db was deleted, let db be null.
  3. If version is undefined, let version be 1 if db is null, or db's version otherwise.
  4. If db is null, let db be a new database with name name, version 0 (zero) as version, and with no object stores.
  5. If db's version is greater than version, abort these steps and return a new VersionError.
  6. Let connection be a new connection to db.
  7. If db's version is less than version:
    1. Run the steps for running an upgrade transaction using connection, version and request.
    2. If connection was closed, create and return a new AbortError exception and abort these steps.
    3. If the upgrade transaction was aborted, run the steps for closing a database connection with connection, create and return a new AbortError exception and abort these steps.
  8. Return connection.

Closing a database

The steps for closing a database connection are as follows. These steps take two arguments, a connection object, and an optional forced flag.

  1. Set the close pending flag of connection.
  2. If the forced flag is set, then for each transaction created using connection run the steps for aborting a transaction with transaction and newly created AbortError exception.
  3. Wait for all transactions created using connection to complete. Once they are complete, connection is closed.
  4. If the forced flag is set, then fire a close event at connection. The event MUST use the Event interface and have its type set to "close". The event MUST NOT bubble or be cancelable.

Once the close pending flag has been set no new transactions can be created using connection. All functions that create transactions first check the close pending flag first and throw an exception if it is set.

Once the connection is closed, this can unblock the steps for running an upgrade transaction, and the steps for deleting a database, which both wait for connections to a given database to be closed before continuing.

Deleting a database

The steps for deleting a database are as follows. The algorithm in these steps takes three arguments: the origin that requested the database to be deleted, a database name, and a request.

  1. If there is already a database with the given name from the origin origin, then let db be that database.
  2. If no database was found, then these steps are considered successful. Abort these steps.
  3. Set db's delete pending flag.
  4. Let openConnections be the set of all connections connected to db.
  5. For each entry in openConnections that does not have its close pending flag set, fire a version change event named versionchange at entry with db's version and null.

    Firing this event might cause one or more of the other objects in openConnections to be closed, in which case the versionchange event MUST NOT be fired at those objects if that hasn't yet been done.

  6. If any of the connections in openConnections are still not closed, and request was given, fire a version change event named blocked at request with db's version and null.
  7. Wait until all connections in openConnections are closed and all of their transactions are finished.
  8. Delete db.

Steps for committing a transaction

When taking the steps for committing a transaction the implementation MUST execute the following algorithm. This algorithm takes one argument, the transaction to commit.

  1. All the changes made to the database by the transaction are written to the database.
  2. If an error occurs while writing the changes to the database, abort the transaction by following the steps for aborting a transaction with transaction and an appropriate for the error, for example QuotaExceededError or UnknownError.
  3. Queue a task to dispatch an event at transaction. The event must use the Event interface and have its type set to "complete". The event does not bubble and is not cancelable. The propagation path for the event is transaction's connection and then transaction.

    Even if an exception is thrown from one of the event handlers of this event, the transaction is still committed since writing the database changes happens before the event takes places. Only after the transaction has been successfully written is the "complete" event fired.

Steps for aborting a transaction

When taking the steps for aborting a transaction the implementation MUST execute the following algorithm. This algorithm takes two arguments: the transaction to abort, and error.

  1. All the changes made to the database by the transaction are reverted. For upgrade transactions this includes changes to the set of object stores and indexes, as well as the change to the version. Also run the steps for aborting an upgrade transaction which reverts changes to all IDBDatabase and IDBObjectStore instances.
  2. If error is not null, set transaction's error property to error.
  3. For each request in transaction's request list with done flag unset, abort the steps for asynchronously executing a request for request and queue a task to perform the following steps:
    1. Set the done flag on request.
    2. Set the result of request to undefined.
    3. Set the error of request to a newly created AbortError exception.
    4. Dispatch an event at request. The event must use the Event interface and have its type set to "error". The event bubbles and is cancelable. The propagation path for the event is transaction's connection, then transaction and finally the request. There is no default action for the event.

    This does not always result in any error events being fired. For example if a transaction is aborted due to an error while committing the transaction, or if it was the last remaining request that failed.

  4. Queue a task to dispatch an event at transaction. The event must use the Event interface and have its type set to "abort". The event does bubble but is not cancelable. The propagation path for the event is transaction's connection and then transaction.

Steps for asynchronously executing a request

When taking the steps for asynchronously executing a request the implementation MUST run the following algorithm. The algorithm takes a source object and an operation to perform on a database, and an optional request.

These steps can be aborted at any point if the transaction the created request belongs to is aborted using the steps for aborting a transaction

  1. Let transaction be the transaction associated with source.
  2. If transaction is not active throw a TransactionInactiveError exception.
  3. If request was not given, let request be a new request with source as source
  4. Add request to the end of transaction's request list.
  5. Return request and queue up the execution of the remaining steps in this algorithm.
  6. Wait until all previously added requests in transaction have their done flag set.
  7. Let result be the result of performing operation.
  8. If result is an error, then revert all changes made by operation, set the done flag on request, set result of request to undefined and set the error on request to result. Finally fire an error event at request.
  9. Otherwise, set the done flag on the request, set result of request to result set the error of request to undefined. Finally fire a success event at request.

Upgrade transaction steps

The steps for running an upgrade transaction are as follows. This algorithm takes three arguments: a connection object which is used to update the database, a new version to be set for the database, and a request.

  1. Let openConnections be the set of all connections, except connection, connected to the same database as connection.
  2. For each entry in openConnections that does not have its close pending flag set, fire a version change event named versionchange at entry with db's version and version.

    Firing this event might cause one or more of the other objects in openConnections to be closed, in which case the versionchange event MUST NOT be fired at those objects if that hasn't yet been done.

  3. If any of the connections in openConnections are still not closed, fire a version change event named blocked at request with db's version and version.
  4. Wait until all connections in openConnections are closed and all of their transactions are finished.
  5. Let transaction be a new upgrade transaction with connection used as connection. The scope of transaction includes every object store in connection.
  6. Unset transaction's active flag.
  7. Start transaction. Note that until this transaction is finished, no other connections can be opened to the same database.
  8. Let old version be database's version.
  9. Set the version of database to version. This change is considered part of the transaction, and so if the transaction is aborted, this change is reverted.
  10. Queue a task to run the following steps:
    1. Set request's result to connection.
    2. Set request's transaction to transaction.
    3. Set the done flag on the request.
    4. Fire a version change event named upgradeneeded at request with old version and version.
    5. If an exception was propagated out from any event handler while dispatching the event in the previous step, abort the transaction by following the steps for aborting a transaction with the error property set to a newly created AbortError exception.
  11. Execute transaction.
  12. If transaction is aborted for any reason, the steps for aborting a transaction MUST be run.
  13. When transaction is finished, immediately set request's transaction to null. This MUST be done in the same task as the task firing the complete or abort event, but after those events has been fired.

Steps for aborting an upgrade transaction

The steps for aborting an upgrade transaction are as follows.

  1. IDBDatabase.name is not modified at all. Even if the transaction is used to create a new database, and thus there is no on-disk database, IDBDatabase.name remains as it was.
  2. Revert IDBDatabase.version to the version the on-disk database had before the transaction was started. If the upgrade transaction was started because the database was newly created, revert IDBDatabase.version to version 0; if the transaction was started in response to a version upgrade, revert to the version it had before the transaction was started. Note that the only time that the IDBDatabase.version property ever changes value is during an upgrade transaction.
  3. Revert IDBDatabase.objectStoreNames to the list of names that it had before the transaction was started. If the upgrade transaction was started because the database was newly created, revert it to an empty list. If the upgrade transaction was started in response to a version upgrade, revert to the list of object store names it had before the transaction was started.
  4. Revert IDBObjectStore.indexNames (for each object store) to the list of names that IDBObjectStore.indexNames had before the transaction was started. For any object store that was created by the transaction, revert the list to an empty list. For any object store that existed before the transaction was started, revert to the list of index names it had before the transaction was started. For any object store that was deleted during the transaction, revert the list of names to the list it had before the transaction was started, potentially a non-empty list.

    Although you cannot access object stores by using the IDBTransaction.objectStore function after a transaction is aborted, the execution context may still have references to object store instances. In that case IDBObjectStore.indexNames can still be accessed.

Fire a success event

To fire a success event at a request, the implementation MUST run the following steps:

  1. Set transaction to the transaction associated with the source.
  2. Set the active flag of transaction.
  3. Dispatch an event at request. The event must use the Event interface and have its type set to "success". The event does not bubble and is not cancelable. The propagation path for the event is the transaction's connection, then transaction and finally request.
  4. Unset the active flag of transaction.
  5. If an exception was propagated out from any event handler while dispatching the event in step 3, abort the transaction by following the steps for aborting a transaction with transaction and a newly created AbortError exception.

Fire an error event

To fire an error event at a request, the implementation MUST run the following steps:

  1. Set transaction to the transaction associated with the source.
  2. Set the active flag of transaction.
  3. Dispatch an event at request. The event must use the Event interface and have its type set to "error". The event bubbles and is cancelable. The propagation path for the event is the transaction's connection, then transaction and finally request. The event's default action is to abort the transaction by running the steps for aborting a transaction using transaction and request's error. However the default action is not taken if any of the event handlers threw an exception.
  4. Unset the active flag of transaction.
  5. If an exception was propagated out from any event handler while dispatching the event in step 3, abort the transaction by following the steps for aborting a transaction with transaction and a newly created AbortError exception. This is done even if the error event is not canceled.

    This means that if an error event is fired and any of the event handlers throw an exception, transaction's error property is set to an AbortError exception rather than request's error, even if preventDefault is never called.

Database operations

This section describes various operations done on the data in object stores and indexes in a database. These operations are run by the steps for asynchronously executing a request.

Object Store Storage Operation

The steps for storing a record into an object store with store, value, an optional key, and a no-overwrite flag are as follows.

  1. If store uses in-line keys:
    1. Let kpk be the result of running the steps to extract a key from a value using a key path with value and store's key path.
    2. Assert: kpk will not be invalid or an exception.
    3. If kpk is not failure, let key be kpk.
  2. If store uses a key generator:
    1. If key is undefined:
      1. If the key generator's current number is greater than 253 (9007199254740992), then this operation failed with a ConstraintError. Abort this algorithm without taking any further steps.
      2. Set key to the key generator's current number
      3. Increase the key generator's current number by 1.
      4. If store also uses in-line keys, then run the steps to inject a key into a value using a key path with value, key and store's key path.
    2. Otherwise, if the type of key is number and the value is greater than or equal to the key generator's current number, set the current number to lowest integer greater than key.
  3. If the no-overwrite flag was given to these steps and is set, and a record already exists in store with its key equal to key, then this operation failed with a ConstraintError. Abort this algorithm without taking any further steps.
  4. If a record already exists in store with its key equal to key, then remove the record from store using the steps for deleting records from an object store.
  5. Store a record in store containing key as its key and object as its value. The record is stored in the object store's list such that the list is sorted according key of the records in ascending order.
  6. For each index which reference store, perform the following substeps:
    1. Let index key be the result of running the steps to extract a key from a value using a key path with value, index's key path, and index's multiEntry flag.
    2. If index key is an exception, or invalid, or failure, take no further actions for this index, and continue these substeps for the next index.
    3. If index's multiEntry flag is unset, or if index key is not an array key, and if index already contains a record with key equal to index key, and index has its unique flag set, then this operation failed with a ConstraintError. Abort this algorithm without taking any further steps.
    4. If index's multiEntry flag is set and index key is an array key, and if index already contains a record with key equal to any of the subkeys of index key, and index has its unique flag set, then this operation failed with a ConstraintError. Abort this algorithm without taking any further steps.
    5. If index's multiEntry flag is unset, or if index key is not an array key then store a record in index containing index key as its key and key as its value. The record is stored in index's list of records such that the list is sorted primarily on the records keys, and secondarily on the records values, in ascending order.
    6. If index's multiEntry flag is set and index key is an array key, then for each subkey of the subkeys of index key store a record in index containing subkey as its key and key as its value. The records are stored in index's list of records such that the list is sorted primarily on the records keys, and secondarily on the records values, in ascending order.

      It is valid for there to be no subkeys. In this case no records are added to the index.

      Even if any member of subkeys is an array key, the member is used directly as the key for the index record. Nested array keys are not flattened or "unpacked" to produce multiple rows; only the outer-most array key

  7. Return key.

Object Store Retrieval Operations

The steps for retrieving a value from an object store with store and range are as follows:

  1. Let record be the first record in store's list of records whose key is in range, if any.
  2. If record was not found, return undefined.
  3. Return a structured clone of record's value.

The steps for retrieving multiple values from an object store with store, range and optional count are as follows:

  1. If count is not given, let count be infinity.
  2. Let records be a list containing the first count records in store's list of records whose key is in range.
  3. Let array be a new Array object.
  4. Let index be 0.
  5. For each record in records:
    1. Let entry be a structured clone of record's value.
    2. Let status be CreateDataProperty(array, index, entry).
    3. Assert: status is true.
    4. Increase index by 1.
  6. Return array

The steps for retrieving multiple keys from an object store with store, range and optional count are as follows:

  1. If count is not given, let count be infinity.
  2. Let records be a list containing the first count records in store's list of records whose key is in range.
  3. Let array be a new Array object.
  4. Let index be 0.
  5. For each record in records:
    1. Let entry be the result of running the steps to convert a key to a value with record's key.
    2. Let status be CreateDataProperty(array, index, entry).
    3. Assert: status is true.
    4. Increase index by 1.
  6. Return array

Index Retrieval Operations

The steps for retrieving a referenced value from an index with index and range are as follows.

  1. Let record be the first record in index's list of records whose key is in range, if any.
  2. If record was not found, return undefined.
  3. Return a structured clone of record's referenced value.

The steps for retrieving multiple referenced values from an index with index, range and optional count are as follows:

  1. If count is not given, let count be infinity.
  2. Let records be a list containing the first count records in index's list of records whose key is in range.
  3. Let array be a new Array object.
  4. Let index be 0.
  5. For each record in records:
    1. Let entry be a structured clone of record's referenced value.
    2. Let status be CreateDataProperty(array, index, entry).
    3. Assert: status is true.
    4. Increase index by 1.
  6. Return array

The steps for retrieving a value from an index with index and range are as follows.

  1. Let record be the first record in index's list of records whose key is in range, if any.
  2. If record was not found, return undefined.
  3. Return record's value.

The steps for retrieving multiple values from an index with index, range and optional count are as follows:

  1. If count is not given, let count be infinity.
  2. Let records be a list containing the first count records in index's list of records whose key is in range.
  3. Let array be a new Array object.
  4. Let index be 0.
  5. For each record in records:
    1. Let entry be the result of running the steps to convert a key to a value with record's value.
    2. Let status be CreateDataProperty(array, index, entry).
    3. Assert: status is true.
    4. Increase index by 1.
  6. Return array

Object Store Deletion Operation

The steps for deleting records from an object store with store and range are as follows.

  1. Remove all records, if any, from store's list of records with key in range.
  2. For each index which references store, remove every record from index's list of records whose value is in range, if any such records exist.
  3. Return undefined.

Record Counting Operation

The steps to count the records in a range with source and range are as follows:

  1. Let count be the number of records, if any, in source's list of records with key in range.
  2. Return count.

Object Store Clear Operation

The steps for clearing an object store with store are as follows.

  1. Remove all records from store.
  2. In all indexes which reference store, remove all records.
  3. Return undefined.

Cursor Iteration Operation

The steps for iterating a cursor with cursor, an optional key to iterate to, and an optional count are as follows.

  1. Let source be cursor's source.
  2. Let records be the list of records in source.

    records is always sorted in ascending key order. In the case of source being an index, records is secondarily sorted in ascending value order (where the value in an index is the key of the record in the referenced object store).

  3. Let range be cursor's range.
  4. Let direction be cursor's direction.
  5. Let position be cursor's position.
  6. Let object store position be cursor's object store position.
  7. If count is not given, let count be 1.
  8. While count is greater than 0:
    1. Switch on direction:
      "next"
      Let found record be the first record in records which satisfy all of the following requirements:
      "nextunique"
      Let found record be the first record in records which satisfy all of the following requirements:
      "prev"
      Let found record be the last record in records which satisfy all of the following requirements:
      • If key is defined, the record's key is less than or equal to key.
      • If position is defined, and source is an object store, the record's key is less than position.
      • If position is defined, and source is an index, the record's key is equal to position and the record's value is less than object store position or the record's key is less than position.
      • The record's key is in range.
      "prevunique"
      Let temp record be the last record in records which satisfy all of the following requirements:
      • If key is defined, the record's key is less than or equal to key.
      • If position is defined, the record's key is less than position.
      • The record's key is in range.
      If temp record is defined, let found record be the first record in records whose key is equal to temp record's key.
    2. If found record is not defined:
      1. Set cursor's key to undefined.
      2. If source is an index, set cursor's object store position to undefined.
      3. If cursor's key only flag is unset, set cursor's value to undefined.
      4. Return null.
    3. Let position be found record's key.
    4. If source is an index, let object store position be found record's value.
    5. Decrease count by 1.
  9. Set cursor's position to position.
  10. If source is an index, set cursor's object store position to object store position.
  11. Set cursor's key to found record's key.
  12. If cursor's key only flag is unset, set cursor's value to a structured clone of found record's referenced value.
  13. Set cursor's got value flag.
  14. Return cursor.

ECMAScript binding

This section defines how key values defined in this specification are converted to and from ECMAScript values, and how they may be extracted from and injected into ECMAScript values using key paths. This section references types and algorithms from the ECMAScript Language Specification. [[!ECMA-262]]

Steps to extract a key from a value using a key path

The steps to extract a key from a value using a key path with value, keyPath and an optional multiEntry flag are as follows. The result of these steps is a key, invalid, or failure, or the steps may throw an exception.

  1. Let r be the result of running the steps to evaluate a key path on a value with value and keyPath.
  2. If r is an exception, rethrow it.
  3. If r is failure, return failure.
  4. Let key be the result of running the steps to convert a value to a key with r if the multiEntry flag is unset, and the result of running the steps to convert a value to a multiEntry key with r otherwise.
  5. If key is an exception, rethrow it.
  6. If key is invalid, return invalid.
  7. Return key.

The steps to evaluate a key path on a value with value and keyPath are as follows. The result of these steps is an ECMAScript value or failure, or the steps may throw an exception.

  1. If keyPath is a sequence<DOMString>, run these substeps:
    1. Let result be a new Array ECMAScript object.
    2. For each item in the keyPath sequence, perform the following substeps:
      1. Let key be the result of recursively running the steps to extract a key from a value using a key path using item as keyPath and value as value.
      2. ReturnIfAbrupt(key)
      3. If key is failure, abort the overall algorithm and return failure.
      4. Append the result of the first sub-step to end of result.
    3. Return result.

      This will only ever "recurse" one level since key path sequences can't ever be nested.

  2. If keyPath is the empty string, return value and skip the remaining steps.
  3. Let identifiers be the result of strictly splitting the string keyPath on U+002E FULL STOP characters (.).
  4. For each identifier in identifiers:
    1. If Type(value) is String, and identifier is the last item in identifiers, and identifier is "length", return a Number equal to the number of elements in value.
    2. If Type(identifier) is not Object, return failure.
    3. Let value be value.[[Get]](identifier, value)
    4. ReturnIfAbrupt(value)
    5. If value is Undefined, return failure.
  5. Return value.

Steps to inject a key into a value using a key path

The steps to inject a key into a value using a key path are as follows. The algorithm takes a value, a key and a keyPath.

  1. Let identifiers be the result of strictly splitting the string keyPath on U+002E FULL STOP characters (.).
  2. Assert: identifiers is not empty.
  3. Let last be the last member of identifiers and remove it from the list.
  4. For each remaining identifier in identifiers:
    1. If value is not an Object object or an Array object (see structured clone algorithm [[!HTML5]]), then throw a DataError exception.
    2. Let hop be HasOwnProperty(value, identifier)
    3. ReturnIfAbrupt(hop)
    4. If hop is false:
      1. Let o be a new Object.
      2. Let status be CreateDataProperty(value, identifier, o)
      3. Assert: status is true
    5. Let value be value[[Get]](identifier, value)
    6. Assert: value is not an abrupt completion.
  5. Assert: value is an Object or an Array.
  6. Let keyValue be the result of running the steps to convert a key to a value.
  7. Let status be CreateDataProperty(value, identifier, keyValue)
  8. Assert: status is true

The key path used here is always a DOMString and never a sequence<DOMString> since it is not possible to create a object store whose multiEntry flag is set and whose key path is an sequence<DOMString>.

Steps to convert a key to a value

The steps to convert a key to a value are as follows. These steps take one argument, key, and return an ECMAScript value.

  1. Let type be key's type.
  2. Let value be key's value.
  3. Switch on type:
    number
    Return an ECMAScript Number value equal to value
    string
    Return an ECMAScript String value equal to value
    date
    1. Let date be the result of executing the ECMAScript Date constructor with the single argument value
    2. Assert: date is not an abrupt completion.
    3. Return date
    array
    1. Let array be the result of executing the ECMAScript Array constructor with no arguments.
    2. Assert: array is not an abrupt completion.
    3. Let len be the length of value.
    4. Let index be 0.
    5. While index is less than len:
      1. Let entry be the result of running the steps to convert a key to a value with the indexth entry of value as input.
      2. Let status be CreateDataProperty(array, index, entry).
      3. Assert: status is true.
      4. Increase index by 1.
    6. Return array

Steps to convert a value to a key

The steps to convert a value to a key are as follows. These steps take two arguments, an ECMAScript value input, and an optional set seen. The result of these steps is a key or invalid, or the steps may throw an exception.

  1. If seen was not given, let seen be a new empty set
  2. If input is in seen return invalid
  3. Switch on Type(input):
    Number
    1. If input is NaN then return invalid.
    2. Otherwise, return a new key with type number and value input
    String
    1. Return type string and value input.
    Object
    1. If input has an internal [[DateValue]] slot, then:
      1. Let ms be the value of input's [[DateValue]] internal slot.
      2. If ms is NaN then return invalid.
      3. Otherwise, return type date and value ms.
    2. If IsArray(input), then:
      1. Let len be the ToLength(Get(input, "length"))
      2. Assert: len will never an abrupt completion.
      3. Add input to seen
      4. Let keys be a new empty list
      5. Let index be 0
      6. While index is less than len:
        1. Let entry be input.[[Get]](index, input)
        2. ReturnIfAbrupt(entry)
        3. Let key be the result of running the steps to convert a value to a key with arguments entry and seen
        4. ReturnIfAbrupt(key)
        5. If key is invalid or an exception, then abort these steps and return key.
        6. Append key to keys
        7. Increase index by 1
      7. Return a new array key with value keys.
    Otherwise
    Return invalid

The steps to convert a value to a multiEntry key are as follows. These steps take one argument, an ECMAScript value input. The result of these steps is a key or invalid, or the steps may throw an exception.

  1. If IsArray(input), then:
    1. Let len be the ToLength(Get(input, "length")).
    2. Assert: len will never an abrupt completion.
    3. Let seen be a new set containing only input.
    4. Let keys be a new empty set.
    5. Let index be 0.
    6. While index is less than len:
      1. Let entry be input.[[Get]](index, input)
      2. If entry is not an exception:
        1. Let key be the result of running the steps to convert a value to a key with arguments entry and seen
        2. If key is not invalid or an exception, add key to keys if there are no other members of keys equal to key
      3. Increase index by 1
    7. Return a new array key with value set to a list of the members of keys.
  2. Otherwise, return the result of running the steps to convert a value to a key with argument input.

Privacy

User tracking

A third-party host (or any object capable of getting content distributed to multiple sites) could use a unique identifier stored in its client-side database to track a user across multiple sessions, building a profile of the user's activities. In conjunction with a site that is aware of the user's real id object (for example an e-commerce site that requires authenticated credentials), this could allow oppressive groups to target individuals with greater accuracy than in a world with purely anonymous Web usage.

There are a number of techniques that can be used to mitigate the risk of user tracking:

Blocking third-party storage
User agents MAY restrict access to the database objects to scripts originating at the domain of the top-level document of the browsing context, for instance denying access to the API for pages from other domains running in iframes.
Expiring stored data

User agents MAY automatically delete stored data after a period of time.

This can restrict the ability of a site to track a user, as the site would then only be able to track the user across multiple sessions when he authenticates with the site itself (e.g. by making a purchase or logging in to a service).

However, this also puts the user's data at risk.

Treating persistent storage as cookies

User agents should present the database feature to the user in a way that associates them strongly with HTTP session cookies. [[COOKIES]]

This might encourage users to view such storage with healthy suspicion.

Site-specific white-listing of access to databases

User agents MAY require the user to authorize access to databases before a site can use the feature.

Origin-tracking of stored data

User agents MAY record the origins of sites that contained content from third-party origins that caused data to be stored.

If this information is then used to present the view of data currently in persistent storage, it would allow the user to make informed decisions about which parts of the persistent storage to prune. Combined with a blacklist ("delete this data and prevent this domain from ever storing data again"), the user can restrict the use of persistent storage to sites that he trusts.

Shared blacklists

User agents MAY allow users to share their persistent storage domain blacklists.

This would allow communities to act together to protect their privacy.

While these suggestions prevent trivial use of this API for user tracking, they do not block it altogether. Within a single domain, a site can continue to track the user during a session, and can then pass all this information to the third party along with any identifying information (names, credit card numbers, addresses) obtained by the site. If a third party cooperates with multiple sites to obtain such information, a profile can still be created.

However, user tracking is to some extent possible even with no cooperation from the user agent whatsoever, for instance by using session identifiers in URLs, a technique already commonly used for innocuous purposes but easily repurposed for user tracking (even retroactively). This information can then be shared with other sites, using using visitors' IP addresses and other user-specific data (e.g. user-agent headers and configuration settings) to combine separate sessions into coherent user profiles.

Sensitivity of data

User agents should treat persistently stored data as potentially sensitive; it is quite possible for e-mails, calendar appointments, health records, or other confidential documents to be stored in this mechanism.

To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage.

Authorization

DNS spoofing attacks

Because of the potential for DNS spoofing attacks, one cannot guarantee that a host claiming to be in a certain domain really is from that domain. To mitigate this, pages can use SSL. Pages using SSL can be sure that only pages using SSL that have certificates identifying them as being from the same domain can access their databases.

Cross-directory attacks

Different authors sharing one host name, for example users hosting content on geocities.com, all share one set of databases.

There is no feature to restrict the access by pathname. Authors on shared hosts are therefore recommended to avoid using these features, as it would be trivial for other authors to read the data and overwrite it.

Even if a path-restriction feature was made available, the usual DOM scripting security model would make it trivial to bypass this protection and access the data from any path.

Implementation risks

The two primary risks when implementing these persistent storage features are letting hostile sites read information from other domains, and letting hostile sites write information that is then read from other domains.

Letting third-party sites read data that is not supposed to be read from their domain causes information leakage, For example, a user's shopping wish list on one domain could be used by another domain for targeted advertising; or a user's work-in-progress confidential documents stored by a word-processing site could be examined by the site of a competing company.

Letting third-party sites write data to the persistent storage of other domains can result in information spoofing, which is equally dangerous. For example, a hostile site could add records to a user's wish list; or a hostile site could set a user's session identifier to a known ID that the hostile site can then use to track the user's actions on the victim site.

Thus, strictly following the origin model described in this specification is important for user security.

Requirements

Requirements will be written with an aim to verify that these were actually met by the API specified in this document.

Revision History

The following is an informative summary of the changes since the last publication of this specification. A complete revision history can be found here. For the revision history of the First Edition, see that document's Revision History.

Acknowledgements

Special thanks to Nikunj Mehta, the original author of the first edition, and Jonas Sicking, Eliot Graff, Andrei Popescu, and Jeremy Orlow, additional editors of the First Edition.

Garret Swart was extremely influential in the design of this specification.

Special thanks to Chris Anderson, Pablo Castro, Kristof Degrave, Jake Drew, Ben Dilts, João Eiras: Alec Flett, Dana Florescu, David Grogan, Israel Hilerio, Kyle Huey, Laxminarayan G Kamath A, Anne van Kesteren, Adam Klein, Tobie Langel, Kang-Hao Lu, Andrea Marchesini, Glenn Maynard, Ms2ger, Odin Omdal, Danillo Paiva, Olli Pettay, Simon Pieters, Yonathan Randolph, Arun Ranganathan, Margo Seltzer, Maciej Stachowiak, Ben Turner, Kyaw Tun, Hans Wennborg, Shawn Wilsher, Boris Zbarsky Zhiqiang Zhang, and Kris Zyp, all of whose feedback and suggestions have led to improvements to this specification.