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.SDKModel}
35 WebInspector.IndexedDBModel = function(target)
37 WebInspector.SDKModel.call(this, WebInspector.IndexedDBModel, target);
38 this._agent = target.indexedDBAgent();
40 /** @type {!Map.<!WebInspector.IndexedDBModel.DatabaseId, !WebInspector.IndexedDBModel.Database>} */
41 this._databases = new Map();
42 /** @type {!Object.<string, !Array.<string>>} */
43 this._databaseNamesBySecurityOrigin = {};
46 WebInspector.IndexedDBModel.KeyTypes = {
53 WebInspector.IndexedDBModel.KeyPathTypes = {
63 WebInspector.IndexedDBModel.keyFromIDBKey = function(idbKey)
65 if (typeof(idbKey) === "undefined" || idbKey === null)
69 switch (typeof(idbKey)) {
72 key.type = WebInspector.IndexedDBModel.KeyTypes.NumberType;
76 key.type = WebInspector.IndexedDBModel.KeyTypes.StringType;
79 if (idbKey instanceof Date) {
80 key.date = idbKey.getTime();
81 key.type = WebInspector.IndexedDBModel.KeyTypes.DateType;
82 } else if (Array.isArray(idbKey)) {
84 for (var i = 0; i < idbKey.length; ++i)
85 key.array.push(WebInspector.IndexedDBModel.keyFromIDBKey(idbKey[i]));
86 key.type = WebInspector.IndexedDBModel.KeyTypes.ArrayType;
96 * @param {?IDBKeyRange=} idbKeyRange
97 * @return {?{lower: ?Object, upper: ?Object, lowerOpen: *, upperOpen: *}}
99 WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange = function(idbKeyRange)
101 if (typeof idbKeyRange === "undefined" || idbKeyRange === null)
105 keyRange.lower = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower);
106 keyRange.upper = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper);
107 keyRange.lowerOpen = idbKeyRange.lowerOpen;
108 keyRange.upperOpen = idbKeyRange.upperOpen;
113 * @param {!IndexedDBAgent.KeyPath} keyPath
114 * @return {?string|!Array.<string>|undefined}
116 WebInspector.IndexedDBModel.idbKeyPathFromKeyPath = function(keyPath)
119 switch (keyPath.type) {
120 case WebInspector.IndexedDBModel.KeyPathTypes.NullType:
123 case WebInspector.IndexedDBModel.KeyPathTypes.StringType:
124 idbKeyPath = keyPath.string;
126 case WebInspector.IndexedDBModel.KeyPathTypes.ArrayType:
127 idbKeyPath = keyPath.array;
134 * @param {?string|!Array.<string>|undefined} idbKeyPath
137 WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath = function(idbKeyPath)
139 if (typeof idbKeyPath === "string")
140 return "\"" + idbKeyPath + "\"";
141 if (idbKeyPath instanceof Array)
142 return "[\"" + idbKeyPath.join("\", \"") + "\"]";
146 WebInspector.IndexedDBModel.EventTypes = {
147 DatabaseAdded: "DatabaseAdded",
148 DatabaseRemoved: "DatabaseRemoved",
149 DatabaseLoaded: "DatabaseLoaded"
152 WebInspector.IndexedDBModel.prototype = {
158 this._agent.enable();
160 this.target().resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
161 this.target().resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
163 var securityOrigins = this.target().resourceTreeModel.securityOrigins();
164 for (var i = 0; i < securityOrigins.length; ++i)
165 this._addOrigin(securityOrigins[i]);
167 this._enabled = true;
170 refreshDatabaseNames: function()
172 for (var securityOrigin in this._databaseNamesBySecurityOrigin)
173 this._loadDatabaseNames(securityOrigin);
177 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
179 refreshDatabase: function(databaseId)
181 this._loadDatabase(databaseId);
185 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
186 * @param {string} objectStoreName
187 * @param {function()} callback
189 clearObjectStore: function(databaseId, objectStoreName, callback)
191 this._agent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName, callback);
195 * @param {!WebInspector.Event} event
197 _securityOriginAdded: function(event)
199 var securityOrigin = /** @type {string} */ (event.data);
200 this._addOrigin(securityOrigin);
204 * @param {!WebInspector.Event} event
206 _securityOriginRemoved: function(event)
208 var securityOrigin = /** @type {string} */ (event.data);
209 this._removeOrigin(securityOrigin);
213 * @param {string} securityOrigin
215 _addOrigin: function(securityOrigin)
217 console.assert(!this._databaseNamesBySecurityOrigin[securityOrigin]);
218 this._databaseNamesBySecurityOrigin[securityOrigin] = [];
219 this._loadDatabaseNames(securityOrigin);
223 * @param {string} securityOrigin
225 _removeOrigin: function(securityOrigin)
227 console.assert(this._databaseNamesBySecurityOrigin[securityOrigin]);
228 for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
229 this._databaseRemoved(securityOrigin, this._databaseNamesBySecurityOrigin[securityOrigin][i]);
230 delete this._databaseNamesBySecurityOrigin[securityOrigin];
234 * @param {string} securityOrigin
235 * @param {!Array.<string>} databaseNames
237 _updateOriginDatabaseNames: function(securityOrigin, databaseNames)
239 var newDatabaseNames = databaseNames.keySet();
240 var oldDatabaseNames = this._databaseNamesBySecurityOrigin[securityOrigin].keySet();
242 this._databaseNamesBySecurityOrigin[securityOrigin] = databaseNames;
244 for (var databaseName in oldDatabaseNames) {
245 if (!newDatabaseNames[databaseName])
246 this._databaseRemoved(securityOrigin, databaseName);
248 for (var databaseName in newDatabaseNames) {
249 if (!oldDatabaseNames[databaseName])
250 this._databaseAdded(securityOrigin, databaseName);
255 * @return {!Array.<!WebInspector.IndexedDBModel.DatabaseId>}
257 databases: function()
260 for (var securityOrigin in this._databaseNamesBySecurityOrigin) {
261 var databaseNames = this._databaseNamesBySecurityOrigin[securityOrigin];
262 for (var i = 0; i < databaseNames.length; ++i) {
263 result.push(new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseNames[i]));
270 * @param {string} securityOrigin
271 * @param {string} databaseName
273 _databaseAdded: function(securityOrigin, databaseName)
275 var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
276 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, databaseId);
280 * @param {string} securityOrigin
281 * @param {string} databaseName
283 _databaseRemoved: function(securityOrigin, databaseName)
285 var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
286 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, databaseId);
290 * @param {string} securityOrigin
292 _loadDatabaseNames: function(securityOrigin)
295 * @param {?Protocol.Error} error
296 * @param {!Array.<string>} databaseNames
297 * @this {WebInspector.IndexedDBModel}
299 function callback(error, databaseNames)
302 console.error("IndexedDBAgent error: " + error);
306 if (!this._databaseNamesBySecurityOrigin[securityOrigin])
308 this._updateOriginDatabaseNames(securityOrigin, databaseNames);
311 this._agent.requestDatabaseNames(securityOrigin, callback.bind(this));
315 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
317 _loadDatabase: function(databaseId)
320 * @param {?Protocol.Error} error
321 * @param {!IndexedDBAgent.DatabaseWithObjectStores} databaseWithObjectStores
322 * @this {WebInspector.IndexedDBModel}
324 function callback(error, databaseWithObjectStores)
327 console.error("IndexedDBAgent error: " + error);
331 if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
333 var databaseModel = new WebInspector.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version, databaseWithObjectStores.intVersion);
334 this._databases.set(databaseId, databaseModel);
335 for (var i = 0; i < databaseWithObjectStores.objectStores.length; ++i) {
336 var objectStore = databaseWithObjectStores.objectStores[i];
337 var objectStoreIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
338 var objectStoreModel = new WebInspector.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
339 for (var j = 0; j < objectStore.indexes.length; ++j) {
340 var index = objectStore.indexes[j];
341 var indexIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
342 var indexModel = new WebInspector.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
343 objectStoreModel.indexes[indexModel.name] = indexModel;
345 databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
348 this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, databaseModel);
351 this._agent.requestDatabase(databaseId.securityOrigin, databaseId.name, callback.bind(this));
355 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
356 * @param {string} objectStoreName
357 * @param {?IDBKeyRange} idbKeyRange
358 * @param {number} skipCount
359 * @param {number} pageSize
360 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
362 loadObjectStoreData: function(databaseId, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
364 this._requestData(databaseId, databaseId.name, objectStoreName, "", idbKeyRange, skipCount, pageSize, callback);
368 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
369 * @param {string} objectStoreName
370 * @param {string} indexName
371 * @param {?IDBKeyRange} idbKeyRange
372 * @param {number} skipCount
373 * @param {number} pageSize
374 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
376 loadIndexData: function(databaseId, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
378 this._requestData(databaseId, databaseId.name, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback);
382 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
383 * @param {string} databaseName
384 * @param {string} objectStoreName
385 * @param {string} indexName
386 * @param {?IDBKeyRange} idbKeyRange
387 * @param {number} skipCount
388 * @param {number} pageSize
389 * @param {function(!Array.<!WebInspector.IndexedDBModel.Entry>, boolean)} callback
391 _requestData: function(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
394 * @param {?Protocol.Error} error
395 * @param {!Array.<!IndexedDBAgent.DataEntry>} dataEntries
396 * @param {boolean} hasMore
397 * @this {WebInspector.IndexedDBModel}
399 function innerCallback(error, dataEntries, hasMore)
402 console.error("IndexedDBAgent error: " + error);
406 if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
409 for (var i = 0; i < dataEntries.length; ++i) {
410 var key = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].key));
411 var primaryKey = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].primaryKey));
412 var value = WebInspector.RemoteObject.fromLocalObject(JSON.parse(dataEntries[i].value));
413 entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
415 callback(entries, hasMore);
418 var keyRange = WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
419 this._agent.requestData(databaseId.securityOrigin, databaseName, objectStoreName, indexName, skipCount, pageSize, keyRange ? keyRange : undefined, innerCallback.bind(this));
422 __proto__: WebInspector.SDKModel.prototype
427 * @param {!WebInspector.RemoteObject} key
428 * @param {!WebInspector.RemoteObject} primaryKey
429 * @param {!WebInspector.RemoteObject} value
431 WebInspector.IndexedDBModel.Entry = function(key, primaryKey, value)
434 this.primaryKey = primaryKey;
440 * @param {string} securityOrigin
441 * @param {string} name
443 WebInspector.IndexedDBModel.DatabaseId = function(securityOrigin, name)
445 this.securityOrigin = securityOrigin;
449 WebInspector.IndexedDBModel.DatabaseId.prototype = {
451 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
454 equals: function(databaseId)
456 return this.name === databaseId.name && this.securityOrigin === databaseId.securityOrigin;
461 * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
462 * @param {string} version
463 * @param {number} intVersion
465 WebInspector.IndexedDBModel.Database = function(databaseId, version, intVersion)
467 this.databaseId = databaseId;
468 this.version = version;
469 this.intVersion = intVersion;
470 this.objectStores = {};
475 * @param {string} name
477 * @param {boolean} autoIncrement
479 WebInspector.IndexedDBModel.ObjectStore = function(name, keyPath, autoIncrement)
482 this.keyPath = keyPath;
483 this.autoIncrement = autoIncrement;
487 WebInspector.IndexedDBModel.ObjectStore.prototype = {
493 return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
499 * @param {string} name
501 * @param {boolean} unique
502 * @param {boolean} multiEntry
504 WebInspector.IndexedDBModel.Index = function(name, keyPath, unique, multiEntry)
507 this.keyPath = keyPath;
508 this.unique = unique;
509 this.multiEntry = multiEntry;
512 WebInspector.IndexedDBModel.Index.prototype = {
518 return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
523 * @param {!WebInspector.Target} target
524 * @return {?WebInspector.IndexedDBModel}
526 WebInspector.IndexedDBModel.fromTarget = function(target)
528 var model = /** @type {?WebInspector.IndexedDBModel} */ (target.model(WebInspector.IndexedDBModel));
530 model = new WebInspector.IndexedDBModel(target);