Abstract

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.

Status of This Document

This section describes the status of this document at the time of its publication.
Other documents may supersede this document.
A list of current W3C publications and the latest revision of this technical report can be found in the
W3C technical reports index at http://www.w3.org/TR/.

Publication as a Working Draft does not imply endorsement by the W3C Membership.
This is a draft document and may be updated, replaced or obsoleted by other documents at any time.
It is inappropriate to cite this document as other than work in progress.

1. Introduction

This section is non-normative.

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.

TODO: Add examples using the sync and the async APIs.

2. Conformance

As
well as sections marked as non-normative, all authoring guidelines,
diagrams, examples, and notes in this specification are non-normative.
Everything else in this specification is normative.

The key words must, must not, required, should, should not, recommended, may, and optional in this specification are to be interpreted as described in [RFC2119].

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.

3. Indexed Database API

3.1 Constructs

3.1.1 Database

Every 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. Each database also has a current
version. When a database is first created, its version is 0.

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.

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 a VERSION_CHANGE
transaction.

Databases has a delete pending flag which is used during deletion. When a database is requested
to be deleted the flag is set to true 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
closePending flag which initially is set to false.

When a connection is initially created it is in opened state. The connection
can be closed through several means. If the connection is GCed
or execution context where the connection is 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 closePending flag is always set to true if it hasn't already been.

3.1.2 Object Store

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

Each database contain a set of object stores. The set of object stores
can be changed, but can only be changed using a VERSION_CHANGE transactions. When a new database is
created it doesn't contain any object stores and has the empty string as version.

The 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.

Every object store has a name.
The name is unique within the database to which it belongs. Every object store also optionally has a
key generator and an optional 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.

The object store can derive the key from one of three sources. Which source is used is determined
when the object store is created. The three sources are:

A key generator. A key generator generates a monotonically increasing numbers every time
a key is needed.

3.1.3 Keys

In order to efficiently retrieve records stored in an indexed database,
each record is organized according to its key. A value is said to be a valid key
if it is one of the following types:
Array JavaScript objects [ECMA-262], DOMString [WEBIDL],
Date [ECMA-262] or float [WEBIDL].
However Arrays are only valid keys if every item in the array is defined and is
a valid key (i.e. sparse arrays can not be valid keys).
Any non-numeric properties are ignored, and thus does not affect if the Array is a valid key.
Additionally, if the value is of type float, it is only a valid key if it is not NaN.
Conforming user agentsmust support all valid keys as keys.

For purposes of comparison, all Arrays are greater than all DOMString,
Date and float values; all DOMString values are greater than all
Date and float values; and all Date values are greater than all
float values. Values of type float are compared to other float values
numerically. Values of type Date are compared to other Date values chronologically.
Values of type DOMString are compared to other values of type DOMString
by using the algorithm defined by step 4 of section 11.8.5, The Abstract Relational Comparison Algorithm,
of the ECMAScript Language Specification [ECMA-262].
Values of type Array are compared to other values of type Array as follows:

Let A be the first Array value and B be the second Array
value.

Let length be the lesser of A's length and B's length.

Let i be 0.

If the ith value of A is less than the ith value of
B, then A is less than B. Skip the remaining steps.

If the ith value of A is greater than the ith value of
B, then A is greater than B. Skip the remaining steps.

Increase i by 1.

If i is not equal to length, go back to step 4. Otherwise continue to next step.

If A's length is less than B's length, then A is less
than B. If A's length is greater than B's length, then
A is greater than B. Otherwise A and B are equal.

Note that Arrays that contain other Arrays are allowed as valid keys.
In this case the algorithm above runs recursively when comparing the individual values in the arrays.

As a result of the above rules, negative infinity is the lowest possible value for a key.
There is no highest possible key value.
This is because an array of any candidate highest key followed by another valid key is even higher.

The terms greater than, less than and equal to is defined in the terms of
the above comparisons.

The following examples illustrate the different behaviors when trying to use in-line keys and key generators
to save an object to an object store.

Then the value provided by the key generator is used to populate the key value.
In the example below the key path for the object store is "foo.bar".
The actual object has no value for the bar property, { foo: {} }.
When the object is saved in the object store the bar property is assigned a value of 4
because that is the next key generated by the object store.

Then the value associated with the key path property is used.
The auto-generated key is not used.
In the example below the keypath for the object store is "foo.bar".
The actual object has a value of 10 for the bar property, { foo: { bar: 10} }.
When the object is saved in the object store the bar property keeps its value of 10, because that is the key value.

ECMAScript

"foo.bar"
{ foo: { bar: 10 } }

The following example illustrates the scenario when the specified in-line key is defined through a key path but there is no property matching it.
The value provided by the key generator is then used to populate the key value and the system is responsible
for creating as many properties as it requires to suffice the property dependencies on the hierarchy chain.
In the example below the key path for the object store is "foo.bar.baz".
The actual object has no value for the foo property, { zip: {} }.
When the object is saved in the object store the foo, bar, and baz properties
are created each as a child of the other until a value for foo.bar.baz can be assigned.
The value for foo.bar.baz is the next key generated by the object store.

ECMAScript

"foo.bar.baz"
{ zip: {} }

Attempting to store a property on a primitive value will fail and throw an error.
In the first example below the key path for the object store is "foo".
The actual object is a primitive with the value, 4.
Trying to define a property on that primitive value fails.
The same is true for arrays. Properties are not allowed on an array.
In the second example below, the actual object is an array, [10].
Trying to define a property on the array fails.

ECMAScript

// The key generation will attempt to create and store the key path property on this primitive.
"foo"
4
// The key generation will attempt to create and store the key path property on this array.
"foo"
[10]

3.1.5 Key Path

A key path is a DOMString that defines how to extract a key from a value.
A valid key path is
either the empty string, a JavaScript identifier, or multiple Javascript identifiers separated by
periods (ASCII character code 46) [ECMA-262]. (Note that spaces are not allowed
within a key path.) To evaluate a key path, run the
steps for extracting a key from a value using a key path.

An index is a specialized persistent key-value storage and has a referencedobject 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 reference 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 records key. However unlike object stores,
a given index can contain multiple records with the same key. Such records are additionally sorted according to
the records value.

An index contains a unique flag. When this flag is set to true, 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 also contains a multientry flag. This flag affects how the index behaves when the result of evaluating
the index's key path yields an Array. If the multientry flag is false, then
a single record whose key is an Array is added to the index. If the multientry flag is
true, then the one record is added to the index for each item in the Array. The key for each
record is the value of respective item in the Array.

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

3.1.7 Transaction

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.

All transactions are created through a connection, which is the transaction's
connection.
The 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. The transaction also has a scope that
determines the object stores with which the transaction may interact. Transactions
have an active flag, which determines if new requests can be made
against the transaction. Finally, transactions also contain a request list of
requests which have been made against the transaction.

Each transaction has a fixed scope, determined when the transaction is created.
A transaction's scope remains fixed for the lifetime of that 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.

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.

A transaction is created using
IDBDatabase.transaction.
The arguments passed determine the scope of the transaction and whether the transaction is read-only.
When a transaction is created its active flag is initially set to true.

The implementation must allow requests to be placed against the transaction
whenever the active flag is true. 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
DOMException of type TransactionInactiveError.

Once an implementation is able to enforce the constraints defined for the transaction mode, defined below,
the implementation must queue up an operation to start the transaction asynchronously.
The timing for when this happens is affected by:

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.

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.

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 mustabort the transaction by following the steps for aborting a transaction, otherwise it
mustcommit the transaction by following the steps for committing a transaction.

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 mustabort the transaction.

Transactions are opened in one of three modes. The mode
determines how concurrent access to object stores in the transaction are isolated.

READ_ONLY

READ_WRITE

VERSION_CHANGE

The transaction mode controls whether or not multiple transactions can run currently
and which operations may be performed during the transaction.
The allowed operations are defined in detail below, but in general
transactions opened in READ_ONLY mode are only allowed to perform
operations that do not change data. READ_WRITE transactions are allowed to
read from and write to transactions to existing object stores, whereas
VERSION_CHANGE transactions are allowed to perform any operations, including ones that
delete and create object stores and indexes.

A VERSION_CHANGE transaction can never run concurrently with other transactions. When
a VERSION_CHANGE transaction is created, the implementation must wait to
start the VERSION_CHANGE transaction until no other transactions
against the same database are running. As long as the VERSION_CHANGE transaction is pending, the implementation
must wait before starting any other transactions against the same database
until the VERSION_CHANGE transaction is finished.

Any number of transactions opened in READ_ONLY mode 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 ensures this. The implementation can 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 can 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.

Generally speaking, the above requirements mean that READ_WRITE transactions which have overlapping
scopes always run in the order they were created, and never run
in parallel.

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.

3.1.8 Requests

Each reading and writing operation on a database is done using a request.
Every request represents one read or write operation.
Requests have a done flag which
initially is false, and a source object. Every request
also has a result and an error attribute,
neither of which are accessible until the done flag is set to true.

3.1.9 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 rangemay be lower-bounded or upper-bounded
(there is a value that is, respectively, smaller than or larger than all its elements).
A key range is said to be bounded if it is both lower-bounded and upper-bounded.
If a key range is neither lower-bounded nor upper-bounded it is said to be unbounded.
A key rangemay be open (the key range does not include its endpoints)
or closed (the key range includes its endpoints).
A key range may consist of a single value.

3.1.10 Cursor

Cursors are a transient mechanism used to
iterate over multiple records in a database.
Storage operations are performed on the underlying index or an
object store.

A cursor comprises a range of records in either an index
or an object store. The cursor has a source that indicates
which index or object store is associated with the records over which the cursor is iterating.
A cursor maintains a position over
this series, which moves in a direction that is in either
monotonically increasing or decreasing order of the record keys. Cursors
also have a key and a value
which represent the key and the value of the last iterated record.
Cursors finally have a got value flag. When this flag is false, the cursor is either
in the process of loading the next value or it has reached the end of its range, when it
is true, it indicates that the cursor is currently holding a value and that it is ready to iterate
to the next one.

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 keygreater than the one previously returned. For
a backwards iterating cursor, the situation is opposite and it returns the record
with the highest keyless 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.

3.1.11 Exceptions

Each of the exceptions defined in the IndexedDB spec is a DOMException with a specific type. [DOM4]
Existing DOM Level 4 exceptions will set their code to a
legacy value; however, the new indexedDB type exceptions will have a
code value of 0.
The message value is optional.

IndexedDB uses the following new DOMException types with their various messages.
All of these new types will have a code value of 0 zero.

Type

Message (Optional)

UnknownError

The operation failed for reasons unrelated to the database itself and not covered by any other errors.

ConstraintError

A mutation operation in the transaction failed because a constraint was not satisfied.
For example, an object such as an object store or index already exists and a request attempted to create a new one.

DataError

Data provided to an operation does not meet requirements.

TransactionInactiveError

A request was placed against a transaction which is currently not active, or which is finished.

ReadOnlyError

The mutating operation was attempted in a READ_ONLY transaction.

VersionError

An attempt was made to open a database using a lower version than the existing version.

IndexedDB reuses the following existing DOMException types from [DOM4].
These types will continue to return the codes and names as specified in DOM4;
however, they will have the following messages when thrown from an IndexedDB API:

Type

Message (Optional)

NotFoundError

The operation failed because the requested database object could not be found.
For example, an object store did not exist but was being opened.

InvalidStateError

An operation was called on an object on which it is not allowed or at a time when it is not allowed.
Also occurs if a request is made on a source object that has been deleted or removed.
Use TransactionInactiveError or ReadOnlyError when possible, as they are more specific variations of InvalidStateError.

InvalidAccessError

An invalid operation was performed on an object. For example transaction creation attempt was made,
but an empty scope was provided.

AbortError

A request was aborted, for example through a call to IDBTransaction.abort.

TimeoutError

A lock for the transaction could not be obtained in a reasonable time.

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.

The data being stored could not be cloned by the internal structured cloning algorithm.

3.1.12 Options Object

Options objects are dictionary objects [WEBIDL]
which are used to supply optional parameters to some indexedDB functions like
createObjectStore and
createIndex. The attributes on the object
correspond to optional parameters on the function called.

3.2 Asynchronous APIs

The asynchronous 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.

Attributes

When the done flag is true, getting this
property must return the error of
the request. This is null when no error occurred. When the
done flag is false, getting this property
must throw a DOMException of type InvalidStateError.

When the done flag is true, getting this
property must return the result of
the request. This is undefined when the request resulted
in an error. When the done flag is
false, getting this property must throw a DOMException of type InvalidStateError.

Constants

This state indicates that a request has been started but its results is not
yet available.

When a request is made, a new request is returned with its
readyState set to
LOADING.
If a request completes successfully, the
readyState
is changed to DONE, 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
readyState
is changed to DONE, the
error attribute is set to a DOMError type that matches the error,
and an event with type error is fired at the request.

The open function on IDBDatabase
uses a separate interface for its requests in order to make use of the
blocked event and upgradeneeded event easier.

Attributes

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

No exceptions.

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
or object handle at a time.

If an error is returned from the steps above, the implementation must set the
error attribute of the request
to a DOMError whose type is the same as the error returned,
of the request to null, and dispatch an event at request.
The event must use the Event interface and have its type set
to "error". The event does bubble but is not cancelable. The propagation
path of the event is just the request.

If the steps above are successful, the implementation must set the
error attribute of the request to a DOMError whose type is the same as the error returned,
to the connection created by the steps above and 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 of the event is just the request. If the steps above resulted in a VERSION_CHANGE transaction being
run, then firing the "success" event must be done in the same task as was used

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 page gets a chance to register
a listener for the versionchange event.

Attributes

On getting, this attribute must return the name
of the connecteddatabase. The function must
return this name even if the closePending flag is set on the connection.
In other words, the return value from this function
stays constant for the lifetime of the IDBDatabase instance.

If an objectStore with the same name already
exists, the implementation must throw a DOMException of type ConstraintError.
Otherwise, the implementation must create a new object store and return an IDBObjectStore object representing it.

If the optionalParameters argument is specified and has a keyPath property
which is not undefined or null, then set keyPath to the value
of this property. If keyPath is an Array, then each item in the array is
converted to a string. If keyPath is not an Array, it is converted to a
string.

If the optionalParameters parameter is specified, and autoIncrement is set to true, and
the keyPath parameter is specified to the empty string, or specified to an Array
and one of the items is an empty string, this function must throw a InvalidAccessError exception.

In some implementations it's possible for the implementation to run into problems
after queuing up an operation to create the objectStore after the createObjectStore function has returned.
For example in implementations where metadata about the newly created objectStore 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. Instead,
once the implementation realizes that creating the objectStore has failed, it must abort the transaction
using the steps for aborting a transaction.

This method, when called must execute the
steps for creating a transaction in an asychronous
fashion. The storeNames and mode arguments are
forwarded to the algorithm as-is. The callback argument is set to null.
The timeout argument is set to infinite.
The connection argument is set to the IDBDatabase that the
transaction() method was called on.

Methods

add

This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is
has its mode set to READ_ONLY. If any of the following conditions are true, this method
throws a DOMException of type DataError:

The object store uses in-line keys and
the key parameter was provided.

This method creates and returns a new index with the given name and parameters in the
connecteddatabase. If this function is called from
outside a VERSION_CHANGEtransaction callback, the implementation
must throw an DOMException of type InvalidStateError exception.
If an index with the same name already exists, the implementation must throw a DOMException of type ConstraintError.
Otherwise, the implementation must create a new index and return an IDBIndex object representing it. The created
index has its unique and multientry flags are set to the values of the
unique and multientry properties in the optionalParameters argument.

If the keyPath argument is an Array, then each item in the array is
converted to a string. If keyPath is not an Array, it is converted to a
string.

If keyPath is an Array and any items in the array is not a valid key path,
or if keyPath is a string and is not a valid key path then a DOMException
of type SyntaxErrormust be thrown. If keyPath is
and Array and the multientry property in the optionalParameters is true,
then a DOMException of type NotSupportedErrormust be thrown. Otherwise set the created object store's
key path is set to the value of keyPath.

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.
Instead the implementation must queue up an operation to abort the VERSION_CHANGE transaction
which was used for the createIndex call.

In some implementations it's 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. Instead,
once the implementation realizes that creating the index has failed, it must abort the transaction
using the steps for aborting a transaction.

This method throws a ReadOnlyError if the transaction which this IDBObjectStore belongs to is
has its mode set to READ_ONLY. If the key parameter is not a valid key
or a key range this method throws a DOMException of type DataError.

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.

If the range parameter is a key range then the cursor's rangemust be set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is
has its mode set to READ_ONLY. If any of the following conditions are true, this method
throws a DOMException of type DataError:

The object store uses in-line keys and
the key parameter was provided.

Methods

count

If the optional key parameter is not a valid key or a key range,
this method throws a DOMException of type DataError.
This method runs the steps for asynchronously executing a request and returns the IDBRequest created by these steps.
The steps are run with this IDBIndex as source and the steps for iterating a cursor as operation,
using the created cursor as cursor.
If provided, use the key parameter as key, otherwise, use undefined as key.
If the result of the algorithm is null return 0 (zero) as the result for the request.
Otherwise, use the return cursor to determine the total number of objects that share the
key or key range and return that value as the result for the request.

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.

The newly created cursor must have an undefined position, a direction set to the value
of the direction parameter, false as iterable flag value, and undefined
key and value. The source
of the cursor is the IDBIndex this function was called on.

If the range parameter is a key range then the cursor's range is set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

The newly created cursor must have an undefined position, a direction set to the value
of the direction parameter, false as iterable flag value, and undefined
key and value. The source
of the cursor is the IDBIndex this function was called on.

If the range parameter is a key range then the cursor's range is set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

Attributes

Returns the cursor's current key.
Note that if this property returns an object (specifically an 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.

Returns the cursor's current effective key.
Note that if this property returns an object (specifically an 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.

On getting, returns the IDBObjectStore or IDBIndex
which this cursor is iterating. This function
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.

No exceptions.

Methods

advance

This method runs the steps for asynchronously executing a request.
However, the steps are slightly modified such that instead of creating a new IDBRequest, it reuses the
request originally created when this cursor was created. The done flag
on the request is set to false before the request is returned. The steps are run with the cursor's
source as source. The operation
runs the steps for iterating a cursorcount number of times
with null as key and this cursor as cursor.

Before this method returns, unless an exception was thrown, it sets the got value flag on the cursor to false.

Calling this method more than once before new cursor data has been loaded is not allowed
and results in a DOMException of type InvalidStateError being thrown.
For example, calling continue() twice from the same onsuccess handler
results in a DOMException of type InvalidStateError being thrown on the second call.

If the value for count is 0 (zero) or a negative number, this method must
throw a DOMException of type TypeError.

If this cursor's got value flag is false, this method throws a DOMException of type InvalidStateError.
If the key parameter is specified and fulfills any of these conditions this
method must throw a DOMException of type DataError:

Otherwise this method runs the steps for asynchronously executing a request.
However, the steps are slightly modified such that instead of creating a new IDBRequest, it reuses the
request originally created when this cursor was created. The done flag
on the request is set to false before the request is returned. The steps are run with the cursor's
source as source and the steps for iterating a cursor as operation,
using this cursor as cursor and the key parameter as key.

Before this method returns, unless an exception was thrown, it sets the got value flag on the cursor to false.

Calling this method more than once before new cursor data has been loaded is not allowed
and results in a DOMException of type InvalidStateError being thrown.
For example, calling continue() twice from the same onsuccess handler
results in a DOMException of type InvalidStateError being thrown on the second call.

This method throws a DOMException of type ReadOnlyError if the transaction which this IDBCursor belongs to
has its mode set to READ_ONLY. If this cursor's got value flag is false,
or if this cursor was created using
openKeyCursor a
DOMException of type InvalidStateError is thrown.

indicates that this cursor should yield all records, not including
duplicates and its direction is monotonically increasing
order of keys. For every key with duplicate values, only the first
record is yielded.

indicates that this cursor should yield all records, not including
duplicates and its direction is monotonically decreasing
order of keys. For every key with duplicate values, only the first
record is yielded.

Attributes

Returns 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 timeout parameter does not affect how long the upgradeCallback can run.

If an error is returned from the steps above, then the implementation must
throw a DOMException with its name and message set to appropriate values for the error.

If the steps above are successful, the implementation must create an IDBDatabaseSync
object representing the created connection and return it.

Processing a open call may take a long time as it could require running a
VERSION_CHANGE transaction which requires all other connections to the database to be closed, which
in turn may depend on user input or other long-running tasks. If blocking for a long period of time
is not acceptable for a given scenario then the asynchronous API should be used for version changes.

If the value for version is 0 (zero) or a negative number, this method must
throw a DOMException of type TypeError.

If the optionalParameters argument is specified and has a keyPath property
which is not null, then set keyPath to the value
of this property. If keyPath is an Array, then each item in the array is
converted to a string. If keyPath is not an Array, it is converted to a
string.

If the optionalParameters parameter is specified, and autoIncrement is set to true, and
the keyPath parameter is specified to the empty string, or specified to an Array
and one of the items is an empty string, this function must throw a InvalidAccessError exception.

This method, when called must execute the
steps for creating a transaction in a sychronous
fashion. The storeNames, callback, mode, and
timeout arguments are forwarded to the algorithm as-is. The connection
argument is set to the IDBDatabaseSync that the transaction() method was
called on.

var range = new IDBKeyRange.bound(2, 4);
var cursor = store.openCursor(range);
// each value is a contact and each key is the id for that
// contact whose id is between 2 and 4, both inclusive
cursor.continue();

Methods

add

This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStoreSync belongs to is
has its mode set to READ_ONLY. If any of the following conditions are true, this method
throws a DOMException of type DataError:

The object store uses in-line keys and
the key parameter was provided.

This creates and returns a new index with the given name and parameters in the
connecteddatabase. Note that this method must only
be called from a VERSION_CHANGEtransaction. The created
index has its unique and multientry flags are set to the values of the
unique and multientry properties in the optionalParameters argument.

If the keyPath argument is an Array, then each item in the array is
converted to a string. If keyPath is not an Array, it is converted to a
string.

If keyPath is an Array and any items in the array is not a valid key path,
or if keyPath is a string and is not a valid key path, then a DOMException
of type SyntaxErrormust be thrown. If keyPath is
and Array and the multientry property in the optionalParameters is true,
then a DOMException of type NotSupportedErrormust be thrown. Otherwise set the created object store's
key path is set to the value of keyPath.

This method was not called from a VERSION_CHANGEtransaction. Also occurs if a request is made on a source object that has been deleted or removed.

ConstraintError

An index with the same name, compared in a
case-sensitive manner, already exists in the
connecteddatabase.
Also thrown when creating a unique index on top of an object store
that already contains records that violate the unique constraint.

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.

If the range parameter is a key range then the cursor's range is set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStoreSync belongs to
has its mode set to READ_ONLY. If any of the following conditions are true, this method
throws a DOMException of type DataError:

The object store uses in-line keys and
the key parameter was provided.

Additionally, all the records of an object store matching
a certain range index keys can be retrieved in key order. When
objects are retrieved from the Contactobject store,
they are arranged by the value of the id attribute. On the
other hand, when objects are retrieved using the ContactNameindex, they are arranged by the value of the name
property.

ECMAScript

var range = new IDBKeyRange.bound('L', 'M');
var cursor = index.openCursor(range);
// each value is a contact and each key is the name for that
// contact whose name's first letter is either L or M
cursor.continue();

If, on the other hand, we only want the names and keys but not the whole Contact
objects for a given range, then we can use a different mechanism for that.

ECMAScript

var range = new IDBKeyRange.bound('L', 'M');
var cursor = index.openKeyCursor(range);
// each value is a contact id and each key is the name for that
// contact whose name's first letter is either L or M
cursor.continue();

Methods

count

If the optional key parameter is not a valid key or a key range,
this method throws a DOMException of type DataError.
This method runs the steps for asynchronously executing a request and returns the IDBRequest created by these steps.
The steps are run with this IDBIndex as source and the steps for iterating a cursor as operation,
using the created cursor as cursor.
If provided, use the key parameter as key, otherwise, use undefined as key.
If the result of the algorithm is null return 0 (zero) as the result for the request.
Otherwise, use the return cursor to determine the total number of objects that share the
key or key range and return that value as the result for the request.

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.

If the range parameter is a key range then the cursor's range is set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

If the range parameter is a key range then the cursor's range is set to that
range. Otherwise, if the range parameter is a valid key then the cursor's range
is set to key range containing only that key value. If the range parameter is not specified,
the cursor's key range is left as undefined.

To start at a certain key and end in the last record, i.e.,
for a lower-bounded cursor, while skipping duplicates,
the cursor should be created with both the required
start key and the direction parameter.

It is also possible to create a bounded cursor, i.e., with
application-specified starting and ending points, the
cursor should be created with both the required keys.
If the range is inclusive of both keys, then additional
flags are required. In the following example, all keys
with values in the inclusive range (start,
end) are returned with all their duplicates,
from the beginning of the range to its end.

Attributes

Returns the cursor's current effective key.
Note that if this property returns an object (specifically an Array), it returns the same
object instance every time it is inspected, until the cursor is iterated.
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.

On getting, returns the IDBObjectStoreSync or IDBIndexSync
which this cursor is iterating. This function
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.

Before this method returns, unless an exception was thrown, it sets the got value flag in the cursor to false.

Calling this method more than once before new cursor data has been loaded is not allowed
and results in a DOMException of type InvalidStateError being thrown.
For example, calling continue() twice from the same onsuccess handler
results in a DOMException of type InvalidStateError being thrown on the second call.

If the value for count is 0 (zero) or a negative number, this method must
throw a DOMException of type TypeError.

If this cursor's got value flag is false, this method throws a DOMException of type InvalidStateError.
If the key parameter is specified and fulfills any of these conditions this
method must throw a DOMException of type DataError:

Before this method returns, unless an exception was thrown, it sets the got value flag in the cursor to false.

Calling this method more than once before new cursor data has been loaded is not allowed
and results in a DOMException of type InvalidStateError being thrown.
For example, calling continue() twice from the same onsuccess handler
results in a DOMException of type InvalidStateError being thrown on the second call.

indicates that this cursor should yield all records, not including
duplicates and its direction is monotonically increasing
order of keys. For every key with duplicate values, only the first
record is yielded.

indicates that this cursor should yield all records, not including
duplicates and its direction is monotonically decreasing
order of keys. For every key with duplicate values, only the first
record is yielded.

Attributes

Returns 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 is iterated.
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.

4. Algorithms

4.1 Opening a database

The steps for opening a database are defined in the following steps. The algorithm in these steps
takes three required arguments: an origin, which requested the database to be opened,
a database name, and a database version. The algorithm also takes two optional
arguments, a request which represents a request used when opening the database is done by using
an asynchronous API or a upgrade callback which represents the callback used when opening the database
is done by using the synchronous API.

If these steps fail for any reason, return an error with the appropriate type and abort
this algorithm.

If there is already a database with the given name from the origin origin, then
let db be that database.

If db was found in previous step, wait until the following conditions are all fulfilled:

If db has its delete pending flag set, wait until db has
been deleted.

These steps are not run for any other connections with the same origin
and name but with a higher version.

If several connections with the same origin, name and version are opened at the same time,
and that version is a higher version that the database's current version, then once any of
those connections can proceed to the next step in this algorithm it will immediately start
a VERSION_CHANGE transaction. This prevents the other connections from proceeding until that
VERSION_CHANGE transaction is finished.

This means that if two databases with the same name and origin, but with different versions, are
being opened at the same time, the one with the highest version will attempt to be opened first.
If it is able to successfully open, then the one with the lower version will receive an error.

If no database with the given name from the origin origin was found, or if it was
deleted during the previous step, then create a database with name name,
with 0 as version, and with no object stores. Let db
be the new database.

If the version of db is higher than version, abort these steps and return
a DOMError of type VersionError.

If the previous step resulted in an error, then return that error and abort these steps.
If the VERSION_CHANGE transaction in the previous step was aborted, or if
connection is closed, return a
DOMError of type AbortError and abort these steps.
In either of these cases, ensure that connection is closed
by running the steps for closing a database connection before these steps are aborted.

Return connection.

4.2 Transaction Creation steps

When the user agent is to create a transaction
it must run the following steps. This algorithm takes five parameters:
A connection, a mode, a list of storeNames of
object stores to be included in the scope of the transaction, a
timeout for the transaction starting,
and a callback parameter for synchronously created transactions.

If storeNames is of type DOMStringList or Array leave it as is. Otherwise,
interpret it as an Array with one value, and that value is the stringified version of
storeNames. If any of the strings in storeNames identifies an object store which doesn't
exist, throw a DOMException of type NotFoundError. If storeNames is an empty list
throw a DOMException of type InvalidAccessError.

If these steps are running asynchronously, return the created transaction and queue up the the remaining steps.
When control is returned to the event loop, the implementation must set the active flag to false.

Because the asynchronous API always passes in a timeout of infinite, only the synchronous
API will ever time out.

If these steps are running synchronously, the implementation must synchronously call callback
with a single parameter which is the transaction. If an exception is thrown and not caught within the
scope of the callback, the implementation must abort the transaction by following the steps for
aborting a transaction, abort this algorithm without taking any further steps, and re-throw the exception.

If these steps are running synchronously, the implementation must commit the transaction synchrnously.

4.3 Steps for committing a transaction

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

All the changes made to the database the transaction uses are written to the database.

Queue up an operation 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.

4.4 Steps for aborting a transaction

When taking the steps for aborting a transaction the implementation must
execute the following algorithm. This algorithm takes two parameter, the transaction to abort
and an error object.

All the changes made to the database the transaction uses are reverted. For VERSION_CHANGE
transactions this includes changes to the set of object stores and indexes, as well
as the change to the version.

Queue up an operation 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.

Create an IDBRequest object and set request to this object. Set request's
source to source and add request to the end of the
list of requests in transaction.
Return this object and queue up the execution of the remaining steps in this algorithm.

Cursors override this step to reuse an existing IDBRequest. However they still put the
IDBRequest at the end of the list of requests in transaction.

If performing operation succeeded then set the done flag
on the request to true, set result of the request
to the result of the request and set the error attribute of the request to undefined.
Finally fire a success event at request.

If performing operation failed then revert all changes made by operation,
set the done flag on the request to true,
set result of the request
to undefined and set the error attribute on the request
to the a DOMError with the same error type of the operation that failed.
Finally fire an error event at request.

This only reverts the changes done by this request, not any other changes made by the transaction.

When taking the steps for extracting a key from a value using a key path, the implementation
must run the following algorithm. The algorithm takes a key path named keyPath and a
value named value and in some cases returns a key which may or may not be a
valid key.

If keyPath is the empty string, return value and skip the remaining steps.

Let remainingKeypath be keyPath and object be value.

If remainingKeypath has a period in it, assign remainingKeypath to be
everything after the first period and assign attribute to be everything before that
first period. Otherwise, assignattribute
to be remainingKeypath and assign remainingKeypath to be null.

If object does not have an attribute named attribute, then skip the rest of
these steps and no value is returned.

Assign object to be the value of the attribute named attribute on object.

If remainingKeypath is not null, go to step 3.

Return object.

4.8 VERSION_CHANGE transaction steps

The steps for running a VERSION_CHANGE transaction are
as follows. This algorithm takes two required parameters - a connection object which is used
to update the database a new version to be set for the database.
The algorithm also takes two optional arguments, a request which represents a request
used when the asynchronous API is used, or a upgrade callback which
represents the callback used when the synchronous API is used.

Fire a versionchange event at each object in openDatabases that is open. The event must not
be fired on objects which has the closePending flag set. The event must use the
IDBVersionChangeEvent interface and have the
oldVersion property set to db's
version and have the newVersion
property set to version.
This event must not bubble or be cancelable. The propagation path for the event is just the IDBDatabase
object itself.

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

If running asynchronously and any of the connections in openDatabases are still not closed,
queue up a blocked event for the request. The event must use the
IDBVersionChangeEvent interface and have the
oldVersion property set to db's
version and have the newVersion
property set to version.
This event must not bubble or be cancelable. The propagation path for the event is just request.

Wait until either all objects in openDatabases are closed and all of
their transactions are finished.

If .close() is called immediately but a transaction associated with the connection
keeps running for a "long time", should we also fire a blocked event?

If, while we're waiting here, someone calls open with a version number higher than version,
we should probably let that upgrade run first and bail here if it was successful

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.
If the VERSION_CHANGE transaction fails to commit set the version attribute on the database to undefined.

If running asynchronously, schedule a task to run the following steps:

When the transaction is finished, if these steps are run asynchronously, immediately set request's
transaction property 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.

4.9 Database closing steps

The steps for closing a database connection are as follows. These steps take one argument, a connection object.

Wait for all transactions created using connection to complete.
Once they are complete, connection is closed.

Once the closePending flag has ben set to true no new transactions can be
created using connection. All functions that
create transactions first check the the closePending flag first and
throw an exception if it is true.

4.10 Database deletion steps

The steps for deleting a database are as follows. The algorithm in these steps
take three arguments. An origin which requested the database to be deleted,
a database name. It also optionally takes a request which represents
a request used when deleting the database is done using an asynchronous API.

If there is already a database with the given name from the origin origin, then
let db be that database.

If no database was found, then these steps are considered successful. Abort these steps.

Fire a versionchange event at each object in openDatabases that is open. The event must not
be fired on objects which has the closePending flag set. The event must use the
IDBVersionChangeEvent interface and have the
oldVersion property set to db's
version and have the newVersion
property set to null.
This event must not bubble or be cancelable.

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

If any of the connections in openDatabases are still not closed, and request was provided,
fire a blocked event at request. The event must use the
IDBVersionChangeEvent interface and have the
oldVersion property set to db's
version and have the newVersion
property set to null.
This event must not bubble or be cancelable.

Wait until all objects in openDatabases are closed and all of
their transactions are finished.

Should we allow blocked to be fired here too, if waiting takes "too long"?

Delete db.

4.11 Fire a success event

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

Dispatch an event at request. The event must use
the Event interface and have its type set to "success".
The event does not bubble or be cancelable. The
propagation path for the event is transaction's connection,
then transaction and finally request.

If store uses a key generator and key is undefined, set
key to the next generated key. If store also uses in-line keys,
then set the property in value pointed to by store's
key path to the new value for key.

If store uses a key generator, this key generator was not used to generate a value
for key in the previous step, key is defined to a long
or a float and this number is larger than, or equal to, the next key that store's
key generator would generate, change store's key generator such that the next
key it generates is the lowest integer larger than key.

If the no-overwrite flag was passed to these steps and is set, and
a record already exists in store with its key equal tokey, then
terminate these steps and set the error attribute to
a DOMError of type ConstraintError
and abort this algorithm without taking any further steps.

Store a record in store containing key as its key and object as its
value. The record is stored in the the object store's list such
that the list is sorted according key of the records in ascending order.

If there are any indexes which referencestore, perform the
following sub steps on each such index.

Set index to the index.

If index's key path is a string, then
evaluate it on
value. If this does not yield a value don't take any further actions for this index.
Otherwise set the result to index key.

If index's key path is an Array, then set
index key to a newly created empty Array. For each item in
index's key pathevaluate
the item on value. If this does not yield a value don't take any further actions for this index.
Otherwise add the value to the end of the index keyArray.

The places invoking these steps ensures that index key is always a valid key by the time
we get to this step.

If index's multientry flag is false, or if index key is not an Array,
and if index already contains a record with keyequal toindex key,
and index has it's unique flag set to true, then
set the error attribute to a DOMError of type ConstraintError
and abort this algorithm without taking any further steps.

If index's multientry flag is true and index key is an Array,
and if index already contains a record with keyequal to any of the
values in index key, and index has it's unique flag set to true, then set
set the error attribute to a DOMError of type ConstraintError
and abort this algorithm without taking any further steps.

If index's multientry flag is false, or if index key is not an Array,
then store a record in index containig 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.

If index's multientry flag is true and index key is an Array,
then for each item in index key store a record in index containig
the items value 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.

Note that it is legal for the Array to have length 0, in this case no records are added to
the index.

If any of the items in the Array are themselves an Array, then the inner
Array is used as a key for that entry. In other words, Arrays are not recursively
"unpacked" to produce multiple rows. Only the outer-most Array is.

The result of this algorithm is key.

5.2 Object Store Retrieval Operation

The steps for retrieving a value from an object store are
as follows. These steps must be run with two parameters - the record
key and the object store.

Let key be the key and store be the object
store passed to these steps.

If key is not a key range then retreive the record
with key key from store. If key is a key range, then
retreive the first record from store whose key is inkey.

If no record was found, the result of this algorithm is undefined.

The result of this algorithm is a new structured clone of the value in the found record.

5.3 Index Referenced Value Retrieval Operation

The steps for retrieving a referenced value from an index are
as follows. These steps must be run with two parameters - the record
key and the index.

6. Privacy

6.1 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.

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.

6.2 Cookie resurrection

If the user interface for persistent storage presents data in the
persistent storage features described in this specification
separately from data in HTTP session cookies, then users are likely
to delete data in one and not the other. This would allow sites to
use the two features as redundant backup for each other, defeating a
user's attempts to protect his privacy.

6.3 Sensitivity of data

User agents should treat persistently stored data as potentially
sensitive; it's 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.

7. Authorization

7.1 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.

7.2 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.

7.3 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.

A. Requirements

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

B. Acknowledgements

Special thanks and great appreciation to Nikunj Mehta, the original author of this specification,
who was employed by Oracle Corp when he wrote the early drafts.

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

Special thanks to Chris Anderson, Pablo Castro, Dana Florescu, Israel Hilerio,
Arun Ranganathan, Margo Seltzer, Ben Turner, Shawn Wilsher, and Kris Zyp,
all of whose feedback and suggestions have led to improvements to this specification.