2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.TargetAwareObject}
35 WebInspector.IndexedDBModel = function(target)
37 WebInspector.TargetAwareObject.call(this, target);
38 this._agent = target.indexedDBAgent();
41 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
42 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
44 /** @type {!Map.<!WebInspector.IndexedDBModel.DatabaseId, !WebInspector.IndexedDBModel.Database>} */
45 this._databases = new Map();
46 /** @type {!Object.<string, !Array.<string>>} */
47 this._databaseNamesBySecurityOrigin = {};
51 WebInspector.IndexedDBModel.KeyTypes = {
58 WebInspector.IndexedDBModel.KeyPathTypes = {
64 WebInspector.IndexedDBModel.keyFromIDBKey = function(idbKey)
66 if (typeof(idbKey) === "undefined" || idbKey === null)
70 switch (typeof(idbKey)) {
73 key.type = WebInspector.IndexedDBModel.KeyTypes.NumberType;
77 key.type = WebInspector.IndexedDBModel.KeyTypes.StringType;
80 if (idbKey instanceof Date) {
81 key.date = idbKey.getTime();
82 key.type = WebInspector.IndexedDBModel.KeyTypes.DateType;
83 } else if (idbKey instanceof Array) {
85 for (var i = 0; i < idbKey.length; ++i)
86 key.array.push(WebInspector.IndexedDBModel.keyFromIDBKey(idbKey[i]));
87 key.type = WebInspector.IndexedDBModel.KeyTypes.ArrayType;
96 WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange = function(idbKeyRange)
98 var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
99 if (typeof(idbKeyRange) === "undefined" || idbKeyRange === null)
103 keyRange.lower = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower);
104 keyRange.upper = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper);
105 keyRange.lowerOpen = idbKeyRange.lowerOpen;
106 keyRange.upperOpen = idbKeyRange.upperOpen;
111 * @param {!IndexedDBAgent.KeyPath} keyPath
113 WebInspector.IndexedDBModel.idbKeyPathFromKeyPath = function(keyPath)
116 switch (keyPath.type) {
117 case WebInspector.IndexedDBModel.KeyPathTypes.NullType:
120 case WebInspector.IndexedDBModel.KeyPathTypes.StringType:
121 idbKeyPath = keyPath.string;
123 case WebInspector.IndexedDBModel.KeyPathTypes.ArrayType:
124 idbKeyPath = keyPath.array;
130 WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath = function(idbKeyPath)
132 if (typeof idbKeyPath === "string")
133 return "\"" + idbKeyPath + "\"";
134 if (idbKeyPath instanceof Array)
135 return "[\"" + idbKeyPath.join("\", \"") + "\"]";
139 WebInspector.IndexedDBModel.EventTypes = {
140 DatabaseAdded: "DatabaseAdded",
141 DatabaseRemoved: "DatabaseRemoved",
142 DatabaseLoaded: "DatabaseLoaded"
145 WebInspector.IndexedDBModel.prototype = {
148 for (var securityOrigin in this._databaseNamesBySecurityOrigin)
149 this._removeOrigin(securityOrigin);
150 var securityOrigins = this.target().resourceTreeModel.securityOrigins();
151 for (var i = 0; i < securityOrigins.length; ++i)
152 this._addOrigin(securityOrigins[i]);
155 refreshDatabaseNames: function()
157 for (var securityOrigin in this._databaseNamesBySecurityOrigin)
158 this._loadDatabaseNames(securityOrigin);
162 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
164 refreshDatabase: function(databaseId)
166 this._loadDatabase(databaseId);
170 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
171 * @param {string} objectStoreName
172 * @param {function()} callback
174 clearObjectStore: function(databaseId, objectStoreName, callback)
176 this._agent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName, callback);
180 * @param {!WebInspector.Event} event
182 _securityOriginAdded: function(event)
184 var securityOrigin = /** @type {string} */ (event.data);
185 this._addOrigin(securityOrigin);
189 * @param {!WebInspector.Event} event
191 _securityOriginRemoved: function(event)
193 var securityOrigin = /** @type {string} */ (event.data);
194 this._removeOrigin(securityOrigin);
198 * @param {string} securityOrigin
200 _addOrigin: function(securityOrigin)
202 console.assert(!this._databaseNamesBySecurityOrigin[securityOrigin]);
203 this._databaseNamesBySecurityOrigin[securityOrigin] = [];
204 this._loadDatabaseNames(securityOrigin);
208 * @param {string} securityOrigin
210 _removeOrigin: function(securityOrigin)
212 console.assert(this._databaseNamesBySecurityOrigin[securityOrigin]);
213 for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
214 this._databaseRemoved(securityOrigin, this._databaseNamesBySecurityOrigin[securityOrigin][i]);
215 delete this._databaseNamesBySecurityOrigin[securityOrigin];
219 * @param {string} securityOrigin
220 * @param {!Array.<string>} databaseNames
222 _updateOriginDatabaseNames: function(securityOrigin, databaseNames)
224 var newDatabaseNames = {};
225 for (var i = 0; i < databaseNames.length; ++i)
226 newDatabaseNames[databaseNames[i]] = true;
227 var oldDatabaseNames = {};
228 for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
229 oldDatabaseNames[this._databaseNamesBySecurityOrigin[securityOrigin][i]] = true;
231 this._databaseNamesBySecurityOrigin[securityOrigin] = databaseNames;
233 for (var databaseName in oldDatabaseNames) {
234 if (!newDatabaseNames[databaseName])
235 this._databaseRemoved(securityOrigin, databaseName);
237 for (var databaseName in newDatabaseNames) {
238 if (!oldDatabaseNames[databaseName])
239 this._databaseAdded(securityOrigin, databaseName);
244 * @param {string} securityOrigin
245 * @param {string} databaseName
247 _databaseAdded: function(securityOrigin, databaseName)
249 var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
250 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, databaseId);
254 * @param {string} securityOrigin
255 * @param {string} databaseName
257 _databaseRemoved: function(securityOrigin, databaseName)
259 var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
260 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, databaseId);
264 * @param {string} securityOrigin
266 _loadDatabaseNames: function(securityOrigin)
269 * @param {?Protocol.Error} error
270 * @param {!Array.<string>} databaseNames
271 * @this {WebInspector.IndexedDBModel}
273 function callback(error, databaseNames)
276 console.error("IndexedDBAgent error: " + error);
280 if (!this._databaseNamesBySecurityOrigin[securityOrigin])
282 this._updateOriginDatabaseNames(securityOrigin, databaseNames);
285 this._agent.requestDatabaseNames(securityOrigin, callback.bind(this));
289 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
291 _loadDatabase: function(databaseId)
294 * @param {?Protocol.Error} error
295 * @param {!IndexedDBAgent.DatabaseWithObjectStores} databaseWithObjectStores
296 * @this {WebInspector.IndexedDBModel}
298 function callback(error, databaseWithObjectStores)
301 console.error("IndexedDBAgent error: " + error);
305 if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
307 var databaseModel = new WebInspector.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version, databaseWithObjectStores.intVersion);
308 this._databases.put(databaseId, databaseModel);
309 for (var i = 0; i < databaseWithObjectStores.objectStores.length; ++i) {
310 var objectStore = databaseWithObjectStores.objectStores[i];
311 var objectStoreIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
312 var objectStoreModel = new WebInspector.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
313 for (var j = 0; j < objectStore.indexes.length; ++j) {
314 var index = objectStore.indexes[j];
315 var indexIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
316 var indexModel = new WebInspector.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
317 objectStoreModel.indexes[indexModel.name] = indexModel;
319 databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
322 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, databaseModel);
325 this._agent.requestDatabase(databaseId.securityOrigin, databaseId.name, callback.bind(this));
329 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
330 * @param {string} objectStoreName
331 * @param {webkitIDBKeyRange} idbKeyRange
332 * @param {number} skipCount
333 * @param {number} pageSize
334 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
336 loadObjectStoreData: function(databaseId, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
338 this._requestData(databaseId, databaseId.name, objectStoreName, "", idbKeyRange, skipCount, pageSize, callback);
342 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
343 * @param {string} objectStoreName
344 * @param {string} indexName
345 * @param {webkitIDBKeyRange} idbKeyRange
346 * @param {number} skipCount
347 * @param {number} pageSize
348 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
350 loadIndexData: function(databaseId, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
352 this._requestData(databaseId, databaseId.name, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback);
356 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
357 * @param {string} databaseName
358 * @param {string} objectStoreName
359 * @param {string} indexName
360 * @param {webkitIDBKeyRange} idbKeyRange
361 * @param {number} skipCount
362 * @param {number} pageSize
363 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
365 _requestData: function(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
368 * @param {?Protocol.Error} error
369 * @param {!Array.<!IndexedDBAgent.DataEntry>} dataEntries
370 * @param {boolean} hasMore
371 * @this {WebInspector.IndexedDBModel}
373 function innerCallback(error, dataEntries, hasMore)
376 console.error("IndexedDBAgent error: " + error);
380 if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
383 for (var i = 0; i < dataEntries.length; ++i) {
384 var key = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].key));
385 var primaryKey = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].primaryKey));
386 var value = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].value));
387 entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
389 callback(entries, hasMore);
392 var keyRange = WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
393 this._agent.requestData(databaseId.securityOrigin, databaseName, objectStoreName, indexName, skipCount, pageSize, keyRange ? keyRange : undefined, innerCallback.bind(this));
396 __proto__: WebInspector.TargetAwareObject.prototype
401 * @param {!WebInspector.RemoteObject} key
402 * @param {!WebInspector.RemoteObject} primaryKey
403 * @param {!WebInspector.RemoteObject} value
405 WebInspector.IndexedDBModel.Entry = function(key, primaryKey, value)
408 this.primaryKey = primaryKey;
414 * @param {string} securityOrigin
415 * @param {string} name
417 WebInspector.IndexedDBModel.DatabaseId = function(securityOrigin, name)
419 this.securityOrigin = securityOrigin;
423 WebInspector.IndexedDBModel.DatabaseId.prototype = {
425 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
428 equals: function(databaseId)
430 return this.name === databaseId.name && this.securityOrigin === databaseId.securityOrigin;
435 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
436 * @param {string} version
438 WebInspector.IndexedDBModel.Database = function(databaseId, version, intVersion)
440 this.databaseId = databaseId;
441 this.version = version;
442 this.intVersion = intVersion;
443 this.objectStores = {};
448 * @param {string} name
451 WebInspector.IndexedDBModel.ObjectStore = function(name, keyPath, autoIncrement)
454 this.keyPath = keyPath;
455 this.autoIncrement = autoIncrement;
459 WebInspector.IndexedDBModel.ObjectStore.prototype = {
465 return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
471 * @param {string} name
474 WebInspector.IndexedDBModel.Index = function(name, keyPath, unique, multiEntry)
477 this.keyPath = keyPath;
478 this.unique = unique;
479 this.multiEntry = multiEntry;
482 WebInspector.IndexedDBModel.Index.prototype = {
488 return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);