Source: js/migrators/browser_migrator.js

  1. 'use strict';
  2. /* global BookmarksDatabase */
  3. /* global Places */
  4. // https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabaseException
  5. // IDBDatabaseException obselete
  6. /** Support different browser versions of IndexedDB */
  7. var idb = window.indexedDB || window.webkitIndexedDB ||
  8. window.mozIndexedDB || window.msIndexedDB;
  9. /**
  10. * Provide access to bookmarks, topsites, history, search engines and settings
  11. * in IndexedDB.
  12. * @namespace BrowserDB
  13. */
  14. var BrowserDB = {
  15. init: function browserDB_init(callback) {
  16. this.db.open(callback);
  17. },
  18. /**
  19. * Get all bookmarks.
  20. * @param {Function} callback Runs on success with an array of bookmarks
  21. */
  22. getBookmarks: function browserDB_getBookmarks(callback) {
  23. this.db.getAllBookmarks(callback);
  24. },
  25. /**
  26. * Reads visits from the database.
  27. */
  28. readVisits: function browserDB_readVisits() {
  29. return this.db.readVisits();
  30. },
  31. removeDB: function browserDB_removeDB() {
  32. this.db.removeDB();
  33. }
  34. };
  35. /**
  36. * @memberOf BrowserDB
  37. * @namespace BrowserDB.db
  38. */
  39. BrowserDB.db = {
  40. _db: null,
  41. upgradeFrom: -1,
  42. /**
  43. * Open a IndexedDB database with name as 'browser' and version as 7.
  44. * @param {Function} callback The callback to be run on success
  45. */
  46. open: function db_open(callback) {
  47. const DB_VERSION = 7;
  48. const DB_NAME = 'browser';
  49. var request = idb.open(DB_NAME, DB_VERSION);
  50. request.onupgradeneeded = (function onUpgradeNeeded(e) {
  51. console.log('Browser db upgrade needed, migrating so doesn\'t matter.');
  52. this._db = e.target.result;
  53. }).bind(this);
  54. request.onsuccess = (function onSuccess(e) {
  55. this._db = e.target.result;
  56. callback();
  57. }).bind(this);
  58. request.onerror = (function onDatabaseError(e) {
  59. console.log('Error opening browser database');
  60. }).bind(this);
  61. },
  62. removeDB: function db_remove() {
  63. this._db.close();
  64. var request = window.indexedDB.deleteDatabase('browser');
  65. request.onsuccess = function onSuccess(e) {
  66. console.log('Browser Database migrated and deleted!');
  67. };
  68. request.onerror = function onSuccess(e) {
  69. console.log('Database not deleted!');
  70. };
  71. },
  72. getAllBookmarks: function db_getAllBookmarks(callback) {
  73. var bookmarks = [];
  74. var db = this._db;
  75. function makeBookmarkProcessor(bookmark) {
  76. return function(e) {
  77. var place = e.target.result;
  78. bookmark.iconUri = place.iconUri;
  79. bookmarks.push(bookmark);
  80. };
  81. }
  82. var transaction = db.transaction(['bookmarks', 'places']);
  83. var bookmarksStore = transaction.objectStore('bookmarks');
  84. var bookmarksIndex = bookmarksStore.index('timestamp');
  85. var placesStore = transaction.objectStore('places');
  86. bookmarksIndex.openCursor(null, 'prev').onsuccess =
  87. function onSuccess(e) {
  88. var cursor = e.target.result;
  89. if (cursor) {
  90. var bookmark = cursor.value;
  91. placesStore.get(bookmark.uri).onsuccess =
  92. makeBookmarkProcessor(bookmark);
  93. cursor.continue();
  94. }
  95. };
  96. transaction.oncomplete = function db_bookmarkTransactionComplete() {
  97. callback(bookmarks);
  98. };
  99. },
  100. readVisits: function db_readHistory() {
  101. var places = {};
  102. var db = this._db;
  103. var transaction = db.transaction(['visits']);
  104. var bookmarksStore = transaction.objectStore('visits');
  105. var bookmarksIndex = bookmarksStore.index('timestamp');
  106. function onCursor(e) {
  107. var cursor = e.target.result;
  108. if (cursor) {
  109. if (!(cursor.value.uri in places)) {
  110. places[cursor.value.uri] = {
  111. visits: []
  112. };
  113. }
  114. var place = places[cursor.value.uri];
  115. if (cursor.value.title) {
  116. place.title = cursor.value.title;
  117. }
  118. place.visits.unshift(cursor.value.timestamp);
  119. if (place.visits.length > 20) {
  120. place.visits.length = 20;
  121. }
  122. cursor.continue();
  123. }
  124. }
  125. return new Promise(function(resolve) {
  126. transaction.oncomplete = function() {
  127. resolve(places);
  128. };
  129. bookmarksIndex.openCursor(null, 'prev').onsuccess = onCursor;
  130. });
  131. }
  132. };
  133. var BrowserMigrator = function BrowserMigrator() {
  134. };
  135. BrowserMigrator.prototype = {
  136. /**
  137. * A list of pending bookmarks to migrate.
  138. */
  139. _pendingBookmarks: [],
  140. start: function() {
  141. BrowserDB.init(() => {
  142. BrowserDB.readVisits().then(visits => {
  143. this._saveVisits(visits).then(() => {
  144. BrowserDB.getBookmarks(bookmarks => {
  145. this._pendingBookmarks = bookmarks;
  146. this._iteratePendingBookmarks();
  147. });
  148. });
  149. });
  150. });
  151. },
  152. _saveVisits: function(visits) {
  153. var urls = Object.keys(visits);
  154. var places = new Places();
  155. return new Promise(function(resolve) {
  156. function saveVisit() {
  157. if (!urls.length) {
  158. resolve();
  159. return;
  160. }
  161. var url = urls.shift();
  162. places.setVisits(url, visits[url].visits).then(saveVisit);
  163. }
  164. places.start().then(saveVisit);
  165. });
  166. },
  167. _iteratePendingBookmarks: function() {
  168. // If there are no bookmarks left, we're done
  169. if (!this._pendingBookmarks.length) {
  170. BrowserDB.removeDB();
  171. window.close();
  172. return;
  173. }
  174. var nextBookmark = this._pendingBookmarks.shift();
  175. var descriptor = {
  176. id: nextBookmark.uri,
  177. url: nextBookmark.uri,
  178. name: nextBookmark.title,
  179. icon: nextBookmark.iconUri
  180. };
  181. BookmarksDatabase.add(descriptor).then(() => {
  182. this._iteratePendingBookmarks();
  183. });
  184. }
  185. };