IndexedDBModel.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Copyright (C) 2012 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  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
  13. * distribution.
  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.
  17. *
  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.
  29. */
  30. /**
  31. * @constructor
  32. * @extends {WebInspector.Object}
  33. */
  34. WebInspector.IndexedDBModel = function()
  35. {
  36. IndexedDBAgent.enable();
  37. WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginAdded, this._securityOriginAdded, this);
  38. WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.SecurityOriginRemoved, this._securityOriginRemoved, this);
  39. this._databases = new Map();
  40. this._databaseNamesBySecurityOrigin = {};
  41. this._reset();
  42. }
  43. WebInspector.IndexedDBModel.KeyTypes = {
  44. NumberType: "number",
  45. StringType: "string",
  46. DateType: "date",
  47. ArrayType: "array"
  48. };
  49. WebInspector.IndexedDBModel.KeyPathTypes = {
  50. NullType: "null",
  51. StringType: "string",
  52. ArrayType: "array"
  53. };
  54. WebInspector.IndexedDBModel.keyFromIDBKey = function(idbKey)
  55. {
  56. if (typeof(idbKey) === "undefined" || idbKey === null)
  57. return null;
  58. var key = {};
  59. switch (typeof(idbKey)) {
  60. case "number":
  61. key.number = idbKey;
  62. key.type = WebInspector.IndexedDBModel.KeyTypes.NumberType;
  63. break;
  64. case "string":
  65. key.string = idbKey;
  66. key.type = WebInspector.IndexedDBModel.KeyTypes.StringType;
  67. break;
  68. case "object":
  69. if (idbKey instanceof Date) {
  70. key.date = idbKey.getTime();
  71. key.type = WebInspector.IndexedDBModel.KeyTypes.DateType;
  72. } else if (idbKey instanceof Array) {
  73. key.array = [];
  74. for (var i = 0; i < idbKey.length; ++i)
  75. key.array.push(WebInspector.IndexedDBModel.keyFromIDBKey(idbKey[i]));
  76. key.type = WebInspector.IndexedDBModel.KeyTypes.ArrayType;
  77. }
  78. break;
  79. default:
  80. return null;
  81. }
  82. return key;
  83. }
  84. WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange = function(idbKeyRange)
  85. {
  86. var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
  87. if (typeof(idbKeyRange) === "undefined" || idbKeyRange === null)
  88. return null;
  89. var keyRange = {};
  90. keyRange.lower = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.lower);
  91. keyRange.upper = WebInspector.IndexedDBModel.keyFromIDBKey(idbKeyRange.upper);
  92. keyRange.lowerOpen = idbKeyRange.lowerOpen;
  93. keyRange.upperOpen = idbKeyRange.upperOpen;
  94. return keyRange;
  95. }
  96. /**
  97. * @param {IndexedDBAgent.KeyPath} keyPath
  98. */
  99. WebInspector.IndexedDBModel.idbKeyPathFromKeyPath = function(keyPath)
  100. {
  101. var idbKeyPath;
  102. switch (keyPath.type) {
  103. case WebInspector.IndexedDBModel.KeyPathTypes.NullType:
  104. idbKeyPath = null;
  105. break;
  106. case WebInspector.IndexedDBModel.KeyPathTypes.StringType:
  107. idbKeyPath = keyPath.string;
  108. break;
  109. case WebInspector.IndexedDBModel.KeyPathTypes.ArrayType:
  110. idbKeyPath = keyPath.array;
  111. break;
  112. }
  113. return idbKeyPath;
  114. }
  115. WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath = function(idbKeyPath)
  116. {
  117. if (typeof idbKeyPath === "string")
  118. return "\"" + idbKeyPath + "\"";
  119. if (idbKeyPath instanceof Array)
  120. return "[\"" + idbKeyPath.join("\", \"") + "\"]";
  121. return null;
  122. }
  123. WebInspector.IndexedDBModel.EventTypes = {
  124. DatabaseAdded: "DatabaseAdded",
  125. DatabaseRemoved: "DatabaseRemoved",
  126. DatabaseLoaded: "DatabaseLoaded"
  127. }
  128. WebInspector.IndexedDBModel.prototype = {
  129. _reset: function()
  130. {
  131. for (var securityOrigin in this._databaseNamesBySecurityOrigin)
  132. this._removeOrigin(securityOrigin);
  133. var securityOrigins = WebInspector.resourceTreeModel.securityOrigins();
  134. for (var i = 0; i < securityOrigins.length; ++i)
  135. this._addOrigin(securityOrigins[i]);
  136. },
  137. refreshDatabaseNames: function()
  138. {
  139. for (var securityOrigin in this._databaseNamesBySecurityOrigin)
  140. this._loadDatabaseNames(securityOrigin);
  141. },
  142. /**
  143. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  144. */
  145. refreshDatabase: function(databaseId)
  146. {
  147. this._loadDatabase(databaseId);
  148. },
  149. /**
  150. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  151. * @param {string} objectStoreName
  152. * @param {function()} callback
  153. */
  154. clearObjectStore: function(databaseId, objectStoreName, callback)
  155. {
  156. IndexedDBAgent.clearObjectStore(databaseId.securityOrigin, databaseId.name, objectStoreName, callback);
  157. },
  158. /**
  159. * @param {WebInspector.Event} event
  160. */
  161. _securityOriginAdded: function(event)
  162. {
  163. var securityOrigin = /** @type {string} */ (event.data);
  164. this._addOrigin(securityOrigin);
  165. },
  166. /**
  167. * @param {WebInspector.Event} event
  168. */
  169. _securityOriginRemoved: function(event)
  170. {
  171. var securityOrigin = /** @type {string} */ (event.data);
  172. this._removeOrigin(securityOrigin);
  173. },
  174. /**
  175. * @param {string} securityOrigin
  176. */
  177. _addOrigin: function(securityOrigin)
  178. {
  179. console.assert(!this._databaseNamesBySecurityOrigin[securityOrigin]);
  180. this._databaseNamesBySecurityOrigin[securityOrigin] = [];
  181. this._loadDatabaseNames(securityOrigin);
  182. },
  183. /**
  184. * @param {string} securityOrigin
  185. */
  186. _removeOrigin: function(securityOrigin)
  187. {
  188. console.assert(this._databaseNamesBySecurityOrigin[securityOrigin]);
  189. for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
  190. this._databaseRemoved(securityOrigin, this._databaseNamesBySecurityOrigin[securityOrigin][i]);
  191. delete this._databaseNamesBySecurityOrigin[securityOrigin];
  192. },
  193. /**
  194. * @param {string} securityOrigin
  195. * @param {Array.<string>} databaseNames
  196. */
  197. _updateOriginDatabaseNames: function(securityOrigin, databaseNames)
  198. {
  199. var newDatabaseNames = {};
  200. for (var i = 0; i < databaseNames.length; ++i)
  201. newDatabaseNames[databaseNames[i]] = true;
  202. var oldDatabaseNames = {};
  203. for (var i = 0; i < this._databaseNamesBySecurityOrigin[securityOrigin].length; ++i)
  204. oldDatabaseNames[this._databaseNamesBySecurityOrigin[securityOrigin][i]] = true;
  205. this._databaseNamesBySecurityOrigin[securityOrigin] = databaseNames;
  206. for (var databaseName in oldDatabaseNames) {
  207. if (!newDatabaseNames[databaseName])
  208. this._databaseRemoved(securityOrigin, databaseName);
  209. }
  210. for (var databaseName in newDatabaseNames) {
  211. if (!oldDatabaseNames[databaseName])
  212. this._databaseAdded(securityOrigin, databaseName);
  213. }
  214. },
  215. /**
  216. * @param {string} securityOrigin
  217. * @param {string} databaseName
  218. */
  219. _databaseAdded: function(securityOrigin, databaseName)
  220. {
  221. var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
  222. this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseAdded, databaseId);
  223. },
  224. /**
  225. * @param {string} securityOrigin
  226. * @param {string} databaseName
  227. */
  228. _databaseRemoved: function(securityOrigin, databaseName)
  229. {
  230. var databaseId = new WebInspector.IndexedDBModel.DatabaseId(securityOrigin, databaseName);
  231. this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseRemoved, databaseId);
  232. },
  233. /**
  234. * @param {string} securityOrigin
  235. */
  236. _loadDatabaseNames: function(securityOrigin)
  237. {
  238. /**
  239. * @param {?Protocol.Error} error
  240. * @param {Array.<string>} databaseNames
  241. */
  242. function callback(error, databaseNames)
  243. {
  244. if (error) {
  245. console.error("IndexedDBAgent error: " + error);
  246. return;
  247. }
  248. if (!this._databaseNamesBySecurityOrigin[securityOrigin])
  249. return;
  250. this._updateOriginDatabaseNames(securityOrigin, databaseNames);
  251. }
  252. IndexedDBAgent.requestDatabaseNames(securityOrigin, callback.bind(this));
  253. },
  254. /**
  255. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  256. */
  257. _loadDatabase: function(databaseId)
  258. {
  259. /**
  260. * @param {?Protocol.Error} error
  261. * @param {IndexedDBAgent.DatabaseWithObjectStores} databaseWithObjectStores
  262. */
  263. function callback(error, databaseWithObjectStores)
  264. {
  265. if (error) {
  266. console.error("IndexedDBAgent error: " + error);
  267. return;
  268. }
  269. if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
  270. return;
  271. var databaseModel = new WebInspector.IndexedDBModel.Database(databaseId, databaseWithObjectStores.version, databaseWithObjectStores.intVersion);
  272. this._databases.put(databaseId, databaseModel);
  273. for (var i = 0; i < databaseWithObjectStores.objectStores.length; ++i) {
  274. var objectStore = databaseWithObjectStores.objectStores[i];
  275. var objectStoreIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(objectStore.keyPath);
  276. var objectStoreModel = new WebInspector.IndexedDBModel.ObjectStore(objectStore.name, objectStoreIDBKeyPath, objectStore.autoIncrement);
  277. for (var j = 0; j < objectStore.indexes.length; ++j) {
  278. var index = objectStore.indexes[j];
  279. var indexIDBKeyPath = WebInspector.IndexedDBModel.idbKeyPathFromKeyPath(index.keyPath);
  280. var indexModel = new WebInspector.IndexedDBModel.Index(index.name, indexIDBKeyPath, index.unique, index.multiEntry);
  281. objectStoreModel.indexes[indexModel.name] = indexModel;
  282. }
  283. databaseModel.objectStores[objectStoreModel.name] = objectStoreModel;
  284. }
  285. this.dispatchEventToListeners(WebInspector.IndexedDBModel.EventTypes.DatabaseLoaded, databaseModel);
  286. }
  287. IndexedDBAgent.requestDatabase(databaseId.securityOrigin, databaseId.name, callback.bind(this));
  288. },
  289. /**
  290. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  291. * @param {string} objectStoreName
  292. * @param {webkitIDBKeyRange} idbKeyRange
  293. * @param {number} skipCount
  294. * @param {number} pageSize
  295. * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
  296. */
  297. loadObjectStoreData: function(databaseId, objectStoreName, idbKeyRange, skipCount, pageSize, callback)
  298. {
  299. this._requestData(databaseId, databaseId.name, objectStoreName, "", idbKeyRange, skipCount, pageSize, callback);
  300. },
  301. /**
  302. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  303. * @param {string} objectStoreName
  304. * @param {string} indexName
  305. * @param {webkitIDBKeyRange} idbKeyRange
  306. * @param {number} skipCount
  307. * @param {number} pageSize
  308. * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
  309. */
  310. loadIndexData: function(databaseId, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
  311. {
  312. this._requestData(databaseId, databaseId.name, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback);
  313. },
  314. /**
  315. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  316. * @param {string} databaseName
  317. * @param {string} objectStoreName
  318. * @param {string} indexName
  319. * @param {webkitIDBKeyRange} idbKeyRange
  320. * @param {number} skipCount
  321. * @param {number} pageSize
  322. * @param {function(Array.<WebInspector.IndexedDBModel.Entry>, boolean)} callback
  323. */
  324. _requestData: function(databaseId, databaseName, objectStoreName, indexName, idbKeyRange, skipCount, pageSize, callback)
  325. {
  326. /**
  327. * @param {?Protocol.Error} error
  328. * @param {Array.<IndexedDBAgent.DataEntry>} dataEntries
  329. * @param {boolean} hasMore
  330. */
  331. function innerCallback(error, dataEntries, hasMore)
  332. {
  333. if (error) {
  334. console.error("IndexedDBAgent error: " + error);
  335. return;
  336. }
  337. if (!this._databaseNamesBySecurityOrigin[databaseId.securityOrigin])
  338. return;
  339. var entries = [];
  340. for (var i = 0; i < dataEntries.length; ++i) {
  341. var key = WebInspector.RemoteObject.fromPayload(dataEntries[i].key);
  342. var primaryKey = WebInspector.RemoteObject.fromPayload(dataEntries[i].primaryKey);
  343. var value = WebInspector.RemoteObject.fromPayload(dataEntries[i].value);
  344. entries.push(new WebInspector.IndexedDBModel.Entry(key, primaryKey, value));
  345. }
  346. callback(entries, hasMore);
  347. }
  348. var keyRange = WebInspector.IndexedDBModel.keyRangeFromIDBKeyRange(idbKeyRange);
  349. IndexedDBAgent.requestData(databaseId.securityOrigin, databaseName, objectStoreName, indexName, skipCount, pageSize, keyRange ? keyRange : undefined, innerCallback.bind(this));
  350. },
  351. __proto__: WebInspector.Object.prototype
  352. }
  353. /**
  354. * @constructor
  355. * @param {WebInspector.RemoteObject} key
  356. * @param {WebInspector.RemoteObject} primaryKey
  357. * @param {WebInspector.RemoteObject} value
  358. */
  359. WebInspector.IndexedDBModel.Entry = function(key, primaryKey, value)
  360. {
  361. this.key = key;
  362. this.primaryKey = primaryKey;
  363. this.value = value;
  364. }
  365. /**
  366. * @constructor
  367. * @param {string} securityOrigin
  368. * @param {string} name
  369. */
  370. WebInspector.IndexedDBModel.DatabaseId = function(securityOrigin, name)
  371. {
  372. this.securityOrigin = securityOrigin;
  373. this.name = name;
  374. }
  375. WebInspector.IndexedDBModel.DatabaseId.prototype = {
  376. /**
  377. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  378. */
  379. equals: function(databaseId)
  380. {
  381. return this.name === databaseId.name && this.securityOrigin === databaseId.securityOrigin;
  382. },
  383. }
  384. /**
  385. * @constructor
  386. * @param {WebInspector.IndexedDBModel.DatabaseId} databaseId
  387. * @param {string} version
  388. */
  389. WebInspector.IndexedDBModel.Database = function(databaseId, version, intVersion)
  390. {
  391. this.databaseId = databaseId;
  392. this.version = version;
  393. this.intVersion = intVersion;
  394. this.objectStores = {};
  395. }
  396. /**
  397. * @constructor
  398. * @param {string} name
  399. * @param {*} keyPath
  400. */
  401. WebInspector.IndexedDBModel.ObjectStore = function(name, keyPath, autoIncrement)
  402. {
  403. this.name = name;
  404. this.keyPath = keyPath;
  405. this.autoIncrement = autoIncrement;
  406. this.indexes = {};
  407. }
  408. WebInspector.IndexedDBModel.ObjectStore.prototype = {
  409. /**
  410. * @type {string}
  411. */
  412. get keyPathString()
  413. {
  414. return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
  415. }
  416. }
  417. /**
  418. * @constructor
  419. * @param {string} name
  420. * @param {*} keyPath
  421. */
  422. WebInspector.IndexedDBModel.Index = function(name, keyPath, unique, multiEntry)
  423. {
  424. this.name = name;
  425. this.keyPath = keyPath;
  426. this.unique = unique;
  427. this.multiEntry = multiEntry;
  428. }
  429. WebInspector.IndexedDBModel.Index.prototype = {
  430. /**
  431. * @type {string}
  432. */
  433. get keyPathString()
  434. {
  435. return WebInspector.IndexedDBModel.keyPathStringFromIDBKeyPath(this.keyPath);
  436. }
  437. }