| /* | |
| * Copyright (C) 2012 Google Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: | |
| * | |
| * * Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * * Redistributions in binary form must reproduce the above | |
| * copyright notice, this list of conditions and the following disclaimer | |
| * in the documentation and/or other materials provided with the | |
| * distribution. | |
| * * Neither the name of Google Inc. nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /** | |
| * @implements {Protocol.StorageDispatcher} | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel = class extends SDK.SDKModel { | |
| /** | |
| * @param {!SDK.Target} target | |
| */ | |
| constructor(target) { | |
| super(target); | |
| target.registerStorageDispatcher(this); | |
| this._securityOriginManager = target.model(SDK.SecurityOriginManager); | |
| this._indexedDBAgent = target.indexedDBAgent(); | |
| this._storageAgent = target.storageAgent(); | |
| /** @type {!Map.<!Resources.IndexedDBModel.DatabaseId, !Resources.IndexedDBModel.Database>} */ | |
| this._databases = new Map(); | |
| /** @type {!Object.<string, !Array.<string>>} */ | |
| this._databaseNamesBySecurityOrigin = {}; | |
| this._originsUpdated = new Set(); | |
| this._throttler = new Common.Throttler(1000); | |
| } | |
| /** | |
| * @param {*} idbKey | |
| * @return {({ | |
| * array: (!Array<?>|undefined), | |
| * date: (number|undefined), | |
| * number: (number|undefined), | |
| * string: (string|undefined), | |
| * type: !Protocol.IndexedDB.KeyType<string> | |
| * }|undefined)} | |
| */ | |
| static keyFromIDBKey(idbKey) { | |
| if (typeof (idbKey) === 'undefined' || idbKey === null) { | |
| return undefined; | |
| } | |
| let type; | |
| const key = {}; | |
| switch (typeof(idbKey)) { | |
| case 'number': | |
| key.number = idbKey; | |
| type = Resources.IndexedDBModel.KeyTypes.NumberType; | |
| break; | |
| case 'string': | |
| key.string = idbKey; | |
| type = Resources.IndexedDBModel.KeyTypes.StringType; | |
| break; | |
| case 'object': | |
| if (idbKey instanceof Date) { | |
| key.date = idbKey.getTime(); | |
| type = Resources.IndexedDBModel.KeyTypes.DateType; | |
| } else if (Array.isArray(idbKey)) { | |
| key.array = []; | |
| for (let i = 0; i < idbKey.length; ++i) { | |
| key.array.push(Resources.IndexedDBModel.keyFromIDBKey(idbKey[i])); | |
| } | |
| type = Resources.IndexedDBModel.KeyTypes.ArrayType; | |
| } | |
| break; | |
| default: | |
| return undefined; | |
| } | |
| key.type = /** @type {!Protocol.IndexedDB.KeyType<string>} */ (type); | |
| return key; | |
| } | |
| /** | |
| * @param {!IDBKeyRange} idbKeyRange | |
| * @return {!Protocol.IndexedDB.KeyRange} | |
| */ | |
| static _keyRangeFromIDBKeyRange(idbKeyRange) { | |
| const keyRange = {}; | |
| keyRange.lower = Resources.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower); | |
| keyRange.upper = Resources.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper); | |
| keyRange.lowerOpen = !!idbKeyRange.lowerOpen; | |
| keyRange.upperOpen = !!idbKeyRange.upperOpen; | |
| return keyRange; | |
| } | |
| /** | |
| * @param {!Protocol.IndexedDB.KeyPath} keyPath | |
| * @return {?string|!Array.<string>|undefined} | |
| */ | |
| static idbKeyPathFromKeyPath(keyPath) { | |
| let idbKeyPath; | |
| switch (keyPath.type) { | |
| case Resources.IndexedDBModel.KeyPathTypes.NullType: | |
| idbKeyPath = null; | |
| break; | |
| case Resources.IndexedDBModel.KeyPathTypes.StringType: | |
| idbKeyPath = keyPath.string; | |
| break; | |
| case Resources.IndexedDBModel.KeyPathTypes.ArrayType: | |
| idbKeyPath = keyPath.array; | |
| break; | |
| } | |
| return idbKeyPath; | |
| } | |
| /** | |
| * @param {?string|!Array.<string>|undefined} idbKeyPath | |
| * @return {?string} | |
| */ | |
| static keyPathStringFromIDBKeyPath(idbKeyPath) { | |
| if (typeof idbKeyPath === 'string') { | |
| return '"' + idbKeyPath + '"'; | |
| } | |
| if (idbKeyPath instanceof Array) { | |
| return '["' + idbKeyPath.join('", "') + '"]'; | |
| } | |
| return null; | |
| } | |
| enable() { | |
| if (this._enabled) { | |
| return; | |
| } | |
| this._indexedDBAgent.enable(); | |
| this._securityOriginManager.addEventListener( | |
| SDK.SecurityOriginManager.Events.SecurityOriginAdded, this._securityOriginAdded, this); | |
| this._securityOriginManager.addEventListener( | |
| SDK.SecurityOriginManager.Events.SecurityOriginRemoved, this._securityOriginRemoved, this); | |
| for (const securityOrigin of this._securityOriginManager.securityOrigins()) { | |
| this._addOrigin(securityOrigin); | |
| } | |
| this._enabled = true; | |
| } | |
| /** | |
| * @param {string} origin | |
| */ | |
| clearForOrigin(origin) { | |
| if (!this._enabled || !this._databaseNamesBySecurityOrigin[origin]) { | |
| return; | |
| } | |
| this._removeOrigin(origin); | |
| this._addOrigin(origin); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| */ | |
| async deleteDatabase(databaseId) { | |
| if (!this._enabled) { | |
| return; | |
| } | |
| await this._indexedDBAgent.deleteDatabase(databaseId.securityOrigin, databaseId.name); | |
| this._loadDatabaseNames(databaseId.securityOrigin); | |
| } | |
| async refreshDatabaseNames() { | |
| for (const securityOrigin in this._databaseNamesBySecurityOrigin) { | |
| await this._loadDatabaseNames(securityOrigin); | |
| } | |
| this.dispatchEventToListeners(Resources.IndexedDBModel.Events.DatabaseNamesRefreshed); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| */ | |
| refreshDatabase(databaseId) { | |
| this._loadDatabase(databaseId, true); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {string} objectStoreName | |
| * @return {!Promise} | |
| */ | |
| clearObjectStore(databaseId, objectStoreName) { | |
| return this._indexedDBAgent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {string} objectStoreName | |
| * @param {!IDBKeyRange} idbKeyRange | |
| * @return {!Promise} | |
| */ | |
| deleteEntries(databaseId, objectStoreName, idbKeyRange) { | |
| const keyRange = Resources.IndexedDBModel._keyRangeFromIDBKeyRange(idbKeyRange); | |
| return this._indexedDBAgent.deleteObjectStoreEntries( | |
| databaseId.securityOrigin, databaseId.name, objectStoreName, keyRange); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _securityOriginAdded(event) { | |
| const securityOrigin = /** @type {string} */ (event.data); | |
| this._addOrigin(securityOrigin); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _securityOriginRemoved(event) { | |
| const securityOrigin = /** @type {string} */ (event.data); | |
| this._removeOrigin(securityOrigin); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| */ | |
| _addOrigin(securityOrigin) { | |
| console.assert(!this._databaseNamesBySecurityOrigin[securityOrigin]); | |
| this._databaseNamesBySecurityOrigin[securityOrigin] = []; | |
| this._loadDatabaseNames(securityOrigin); | |
| if (this._isValidSecurityOrigin(securityOrigin)) { | |
| this._storageAgent.trackIndexedDBForOrigin(securityOrigin); | |
| } | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| */ | |
| _removeOrigin(securityOrigin) { | |
| console.assert(this._databaseNamesBySecurityOrigin[securityOrigin]); | |
| for (let i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i) { | |
| this._databaseRemoved(securityOrigin, this._databaseNamesBySecurityOrigin[securityOrigin][i]); | |
| } | |
| delete this._databaseNamesBySecurityOrigin[securityOrigin]; | |
| if (this._isValidSecurityOrigin(securityOrigin)) { | |
| this._storageAgent.untrackIndexedDBForOrigin(securityOrigin); | |
| } | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @return {boolean} | |
| */ | |
| _isValidSecurityOrigin(securityOrigin) { | |
| const parsedURL = Common.ParsedURL.fromString(securityOrigin); | |
| return !!parsedURL && parsedURL.scheme.startsWith('http'); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @param {!Array.<string>} databaseNames | |
| */ | |
| _updateOriginDatabaseNames(securityOrigin, databaseNames) { | |
| const newDatabaseNames = new Set(databaseNames); | |
| const oldDatabaseNames = new Set(this._databaseNamesBySecurityOrigin[securityOrigin]); | |
| this._databaseNamesBySecurityOrigin[securityOrigin] = databaseNames; | |
| for (const databaseName of oldDatabaseNames) { | |
| if (!newDatabaseNames.has(databaseName)) { | |
| this._databaseRemoved(securityOrigin, databaseName); | |
| } | |
| } | |
| for (const databaseName of newDatabaseNames) { | |
| if (!oldDatabaseNames.has(databaseName)) { | |
| this._databaseAdded(securityOrigin, databaseName); | |
| } | |
| } | |
| } | |
| /** | |
| * @return {!Array.<!Resources.IndexedDBModel.DatabaseId>} | |
| */ | |
| databases() { | |
| const result = []; | |
| for (const securityOrigin in this._databaseNamesBySecurityOrigin) { | |
| const databaseNames = this._databaseNamesBySecurityOrigin[securityOrigin]; | |
| for (let i = 0; i < databaseNames.length; ++i) { | |
| result.push(new Resources.IndexedDBModel.DatabaseId(securityOrigin, databaseNames[i])); | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @param {string} databaseName | |
| */ | |
| _databaseAdded(securityOrigin, databaseName) { | |
| const databaseId = new Resources.IndexedDBModel.DatabaseId(securityOrigin, databaseName); | |
| this.dispatchEventToListeners(Resources.IndexedDBModel.Events.DatabaseAdded, {model: this, databaseId: databaseId}); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @param {string} databaseName | |
| */ | |
| _databaseRemoved(securityOrigin, databaseName) { | |
| const databaseId = new Resources.IndexedDBModel.DatabaseId(securityOrigin, databaseName); | |
| this.dispatchEventToListeners( | |
| Resources.IndexedDBModel.Events.DatabaseRemoved, {model: this, databaseId: databaseId}); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @return {!Promise<!Array.<string>>} databaseNames | |
| */ | |
| async _loadDatabaseNames(securityOrigin) { | |
| const databaseNames = await this._indexedDBAgent.requestDatabaseNames(securityOrigin); | |
| if (!databaseNames) { | |
| return []; | |
| } | |
| if (!this._databaseNamesBySecurityOrigin[securityOrigin]) { | |
| return []; | |
| } | |
| this._updateOriginDatabaseNames(securityOrigin, databaseNames); | |
| return databaseNames; | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {boolean} entriesUpdated | |
| */ | |
| async _loadDatabase(databaseId, entriesUpdated) { | |
| const databaseWithObjectStores = | |
| await this._indexedDBAgent.requestDatabase(databaseId.securityOrigin, databaseId.name); | |
| if (!databaseWithObjectStores) { | |
| return; | |
| } | |
| if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin]) { | |
| return; | |
| } | |
| const databaseModel = new Resources.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version); | |
| this._databases.set(databaseId, databaseModel); | |
| for (const objectStore of databaseWithObjectStores.objectStores) { | |
| const objectStoreIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath); | |
| const objectStoreModel = | |
| new Resources.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement); | |
| for (let j = 0; j < objectStore.indexes.length; ++j) { | |
| const index = objectStore.indexes[j]; | |
| const indexIDBKeyPath = Resources.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath); | |
| const indexModel = | |
| new Resources.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry); | |
| objectStoreModel.indexes[indexModel.name] = indexModel; | |
| } | |
| databaseModel.objectStores[objectStoreModel.name] = objectStoreModel; | |
| } | |
| this.dispatchEventToListeners( | |
| Resources.IndexedDBModel.Events.DatabaseLoaded, | |
| {model: this, database: databaseModel, entriesUpdated: entriesUpdated}); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {string} objectStoreName | |
| * @param {?IDBKeyRange} idbKeyRange | |
| * @param {number} skipCount | |
| * @param {number} pageSize | |
| * @param {function(!Array.<!Resources.IndexedDBModel.Entry>, boolean)} callback | |
| */ | |
| loadObjectStoreData(databaseId, objectStoreName, idbKeyRange, skipCount, pageSize, callback) { | |
| this._requestData(databaseId, databaseId.name, objectStoreName, '', idbKeyRange, skipCount, pageSize, callback); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {string} objectStoreName | |
| * @param {string} indexName | |
| * @param {?IDBKeyRange} idbKeyRange | |
| * @param {number} skipCount | |
| * @param {number} pageSize | |
| * @param {function(!Array.<!Resources.IndexedDBModel.Entry>, boolean)} callback | |
| */ | |
| loadIndexData(databaseId, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback) { | |
| this._requestData( | |
| databaseId, databaseId.name, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {string} databaseName | |
| * @param {string} objectStoreName | |
| * @param {string} indexName | |
| * @param {?IDBKeyRange} idbKeyRange | |
| * @param {number} skipCount | |
| * @param {number} pageSize | |
| * @param {function(!Array.<!Resources.IndexedDBModel.Entry>, boolean)} callback | |
| */ | |
| async _requestData(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback) { | |
| const keyRange = idbKeyRange ? Resources.IndexedDBModel._keyRangeFromIDBKeyRange(idbKeyRange) : undefined; | |
| const response = await this._indexedDBAgent.invoke_requestData({ | |
| securityOrigin: databaseId.securityOrigin, | |
| databaseName, | |
| objectStoreName, | |
| indexName, | |
| skipCount, | |
| pageSize, | |
| keyRange | |
| }); | |
| if (response[Protocol.Error]) { | |
| console.error('IndexedDBAgent error: ' + response[Protocol.Error]); | |
| return; | |
| } | |
| const runtimeModel = this.target().model(SDK.RuntimeModel); | |
| if (!runtimeModel || !this._databaseNamesBySecurityOrigin[databaseId.securityOrigin]) { | |
| return; | |
| } | |
| const dataEntries = response.objectStoreDataEntries; | |
| const entries = []; | |
| for (const dataEntry of dataEntries) { | |
| const key = runtimeModel.createRemoteObject(dataEntry.key); | |
| const primaryKey = runtimeModel.createRemoteObject(dataEntry.primaryKey); | |
| const value = runtimeModel.createRemoteObject(dataEntry.value); | |
| entries.push(new Resources.IndexedDBModel.Entry(key, primaryKey, value)); | |
| } | |
| callback(entries, response.hasMore); | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {!Resources.IndexedDBModel.ObjectStore} objectStore | |
| * @return {!Promise<?Resources.IndexedDBModel.ObjectStoreMetadata>} | |
| */ | |
| async getMetadata(databaseId, objectStore) { | |
| const databaseOrigin = databaseId.securityOrigin; | |
| const databaseName = databaseId.name; | |
| const objectStoreName = objectStore.name; | |
| const response = | |
| await this._indexedDBAgent.invoke_getMetadata({securityOrigin: databaseOrigin, databaseName, objectStoreName}); | |
| if (response[Protocol.Error]) { | |
| console.error('IndexedDBAgent error: ' + response[Protocol.Error]); | |
| return null; | |
| } | |
| return {entriesCount: response.entriesCount, keyGeneratorValue: response.keyGeneratorValue}; | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| */ | |
| async _refreshDatabaseList(securityOrigin) { | |
| const databaseNames = await this._loadDatabaseNames(securityOrigin); | |
| for (const databaseName of databaseNames) { | |
| this._loadDatabase(new Resources.IndexedDBModel.DatabaseId(securityOrigin, databaseName), false); | |
| } | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @override | |
| */ | |
| indexedDBListUpdated(securityOrigin) { | |
| this._originsUpdated.add(securityOrigin); | |
| this._throttler.schedule(() => { | |
| const promises = Array.from(this._originsUpdated, securityOrigin => { | |
| this._refreshDatabaseList(securityOrigin); | |
| }); | |
| this._originsUpdated.clear(); | |
| return Promise.all(promises); | |
| }); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @param {string} databaseName | |
| * @param {string} objectStoreName | |
| * @override | |
| */ | |
| indexedDBContentUpdated(securityOrigin, databaseName, objectStoreName) { | |
| const databaseId = new Resources.IndexedDBModel.DatabaseId(securityOrigin, databaseName); | |
| this.dispatchEventToListeners( | |
| Resources.IndexedDBModel.Events.IndexedDBContentUpdated, | |
| {databaseId: databaseId, objectStoreName: objectStoreName, model: this}); | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @override | |
| */ | |
| cacheStorageListUpdated(securityOrigin) { | |
| } | |
| /** | |
| * @param {string} securityOrigin | |
| * @override | |
| */ | |
| cacheStorageContentUpdated(securityOrigin) { | |
| } | |
| }; | |
| SDK.SDKModel.register(Resources.IndexedDBModel, SDK.Target.Capability.Storage, false); | |
| Resources.IndexedDBModel.KeyTypes = { | |
| NumberType: 'number', | |
| StringType: 'string', | |
| DateType: 'date', | |
| ArrayType: 'array' | |
| }; | |
| Resources.IndexedDBModel.KeyPathTypes = { | |
| NullType: 'null', | |
| StringType: 'string', | |
| ArrayType: 'array' | |
| }; | |
| /** @enum {symbol} */ | |
| Resources.IndexedDBModel.Events = { | |
| DatabaseAdded: Symbol('DatabaseAdded'), | |
| DatabaseRemoved: Symbol('DatabaseRemoved'), | |
| DatabaseLoaded: Symbol('DatabaseLoaded'), | |
| DatabaseNamesRefreshed: Symbol('DatabaseNamesRefreshed'), | |
| IndexedDBContentUpdated: Symbol('IndexedDBContentUpdated') | |
| }; | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel.Entry = class { | |
| /** | |
| * @param {!SDK.RemoteObject} key | |
| * @param {!SDK.RemoteObject} primaryKey | |
| * @param {!SDK.RemoteObject} value | |
| */ | |
| constructor(key, primaryKey, value) { | |
| this.key = key; | |
| this.primaryKey = primaryKey; | |
| this.value = value; | |
| } | |
| }; | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel.DatabaseId = class { | |
| /** | |
| * @param {string} securityOrigin | |
| * @param {string} name | |
| */ | |
| constructor(securityOrigin, name) { | |
| this.securityOrigin = securityOrigin; | |
| this.name = name; | |
| } | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @return {boolean} | |
| */ | |
| equals(databaseId) { | |
| return this.name === databaseId.name && this.securityOrigin === databaseId.securityOrigin; | |
| } | |
| }; | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel.Database = class { | |
| /** | |
| * @param {!Resources.IndexedDBModel.DatabaseId} databaseId | |
| * @param {number} version | |
| */ | |
| constructor(databaseId, version) { | |
| this.databaseId = databaseId; | |
| this.version = version; | |
| this.objectStores = {}; | |
| } | |
| }; | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel.ObjectStore = class { | |
| /** | |
| * @param {string} name | |
| * @param {*} keyPath | |
| * @param {boolean} autoIncrement | |
| */ | |
| constructor(name, keyPath, autoIncrement) { | |
| this.name = name; | |
| this.keyPath = keyPath; | |
| this.autoIncrement = autoIncrement; | |
| this.indexes = {}; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| get keyPathString() { | |
| return /** @type {string}*/ ( | |
| Resources.IndexedDBModel.keyPathStringFromIDBKeyPath(/** @type {string}*/ (this.keyPath))); | |
| } | |
| }; | |
| /** | |
| * @typedef {{ | |
| * entriesCount: number, | |
| * keyGeneratorValue: number | |
| * }} | |
| */ | |
| Resources.IndexedDBModel.ObjectStoreMetadata; | |
| /** | |
| * @unrestricted | |
| */ | |
| Resources.IndexedDBModel.Index = class { | |
| /** | |
| * @param {string} name | |
| * @param {*} keyPath | |
| * @param {boolean} unique | |
| * @param {boolean} multiEntry | |
| */ | |
| constructor(name, keyPath, unique, multiEntry) { | |
| this.name = name; | |
| this.keyPath = keyPath; | |
| this.unique = unique; | |
| this.multiEntry = multiEntry; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| get keyPathString() { | |
| return /** @type {string}*/ ( | |
| Resources.IndexedDBModel.keyPathStringFromIDBKeyPath(/** @type {string}*/ (this.keyPath))); | |
| } | |
| }; |