/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Wrapper for an IndexedDB transaction.
*/
goog.provide('goog.db.Transaction');
goog.provide('goog.db.Transaction.TransactionMode');
goog.require('goog.async.Deferred');
goog.require('goog.db.Error');
goog.require('goog.db.ObjectStore');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventTarget');
/**
* Creates a new transaction. Transactions contain methods for accessing object
* stores and are created from the database object. Should not be created
* directly, open a database and call createTransaction on it.
* @see goog.db.IndexedDb#createTransaction
*
* @param {!IDBTransaction} tx IndexedDB transaction to back this wrapper.
* @param {!goog.db.IndexedDb} db The database that this transaction modifies.
* @constructor
* @extends {goog.events.EventTarget}
* @final
*/
goog.db.Transaction = function(tx, db) {
'use strict';
goog.db.Transaction.base(this, 'constructor');
/**
* Underlying IndexedDB transaction object.
*
* @type {!IDBTransaction}
* @private
*/
this.tx_ = tx;
/**
* The database that this transaction modifies.
*
* @type {!goog.db.IndexedDb}
* @private
*/
this.db_ = db;
/**
* Event handler for this transaction.
*
* @type {!goog.events.EventHandler<!goog.db.Transaction>}
* @private
*/
this.eventHandler_ = new goog.events.EventHandler(this);
// TODO(user): remove these casts once the externs file is updated to
// correctly reflect that IDBTransaction extends EventTarget
this.eventHandler_.listen(
/** @type {!EventTarget} */ (this.tx_), 'complete',
goog.bind(
this.dispatchEvent, this, goog.db.Transaction.EventTypes.COMPLETE));
this.eventHandler_.listen(
/** @type {!EventTarget} */ (this.tx_), 'abort',
goog.bind(
this.dispatchEvent, this, goog.db.Transaction.EventTypes.ABORT));
this.eventHandler_.listen(
/** @type {!EventTarget} */ (this.tx_), 'error', this.dispatchError_);
};
goog.inherits(goog.db.Transaction, goog.events.EventTarget);
/**
* Dispatches an error event based on the given event, wrapping the error
* if necessary.
*
* @param {Event} ev The error event given to the underlying IDBTransaction.
* @private
*/
goog.db.Transaction.prototype.dispatchError_ = function(ev) {
'use strict';
if (ev.target instanceof goog.db.Error) {
this.dispatchEvent(
{type: goog.db.Transaction.EventTypes.ERROR, target: ev.target});
} else {
this.dispatchEvent({
type: goog.db.Transaction.EventTypes.ERROR,
target: goog.db.Error.fromRequest(
/** @type {!IDBRequest} */ (ev.target), 'in transaction')
});
}
};
/**
* Event types the Transaction can dispatch. COMPLETE events are dispatched
* when the transaction is committed. If a transaction is aborted it dispatches
* both an ABORT event and an ERROR event with the ABORT_ERR code. Error events
* are dispatched on any error.
*
* @enum {string}
*/
goog.db.Transaction.EventTypes = {
COMPLETE: 'complete',
ABORT: 'abort',
ERROR: 'error'
};
/**
* @return {goog.db.Transaction.TransactionMode} The transaction's mode.
*/
goog.db.Transaction.prototype.getMode = function() {
'use strict';
return /** @type {goog.db.Transaction.TransactionMode} */ (this.tx_.mode);
};
/**
* @return {!goog.db.IndexedDb} The database that this transaction modifies.
*/
goog.db.Transaction.prototype.getDatabase = function() {
'use strict';
return this.db_;
};
/**
* Opens an object store to do operations on in this transaction. The requested
* object store must be one that is in this transaction's scope.
* @see goog.db.IndexedDb#createTransaction
*
* @param {string} name The name of the requested object store.
* @return {!goog.db.ObjectStore} The wrapped object store.
* @throws {goog.db.Error} In case of error getting the object store.
*/
goog.db.Transaction.prototype.objectStore = function(name) {
'use strict';
try {
return new goog.db.ObjectStore(this.tx_.objectStore(name));
} catch (ex) {
throw goog.db.Error.fromException(ex, 'getting object store ' + name);
}
};
/**
* @param {boolean} allowNoopWhenUnsupported Whether it's fine for the method to
* act like no-op if native method is not supported by the browser.
* @throws {!goog.db.Error} In case of error executing the commit.
*/
goog.db.Transaction.prototype.commit = function(allowNoopWhenUnsupported) {
'use strict';
if (!this.tx_.commit && allowNoopWhenUnsupported) {
// Method doesn't exist, and caller is ok with a no-op.
return;
}
try {
this.tx_.commit();
} catch (ex) {
throw goog.db.Error.fromException(ex, 'cannot commit the transaction');
}
};
/**
* @return {!goog.async.Deferred} A deferred that will fire once the
* transaction is complete. It fires the errback chain if an error occurs
* in the transaction, or if it is aborted.
*/
goog.db.Transaction.prototype.wait = function() {
'use strict';
const d = new goog.async.Deferred();
goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.COMPLETE, goog.bind(d.callback, d));
let errorKey;
const abortKey = goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.ABORT, function() {
'use strict';
goog.events.unlistenByKey(errorKey);
d.errback(
new goog.db.Error(
goog.db.Error.ErrorCode.ABORT_ERR,
'waiting for transaction to complete'));
});
errorKey = goog.events.listenOnce(
this, goog.db.Transaction.EventTypes.ERROR, function(e) {
'use strict';
goog.events.unlistenByKey(abortKey);
d.errback(e.target);
});
const db = this.getDatabase();
return d.addCallback(function() {
'use strict';
return db;
});
};
/**
* Aborts this transaction. No pending operations will be applied to the
* database. Dispatches an ABORT event.
*/
goog.db.Transaction.prototype.abort = function() {
'use strict';
this.tx_.abort();
};
/** @override */
goog.db.Transaction.prototype.disposeInternal = function() {
'use strict';
goog.db.Transaction.base(this, 'disposeInternal');
this.eventHandler_.dispose();
};
/**
* The three possible transaction modes.
* @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction
*
* @enum {string}
*/
goog.db.Transaction.TransactionMode = {
READ_ONLY: 'readonly',
READ_WRITE: 'readwrite',
VERSION_CHANGE: 'versionchange'
};