ActivityStreamStorage.jsm 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. ChromeUtils.defineModuleGetter(this, "IndexedDB", "resource://gre/modules/IndexedDB.jsm");
  2. this.ActivityStreamStorage = class ActivityStreamStorage {
  3. /**
  4. * @param storeNames Array of strings used to create all the required stores
  5. */
  6. constructor({storeNames, telemetry}) {
  7. if (!storeNames) {
  8. throw new Error("storeNames required");
  9. }
  10. this.dbName = "ActivityStream";
  11. this.dbVersion = 3;
  12. this.storeNames = storeNames;
  13. this.telemetry = telemetry;
  14. }
  15. get db() {
  16. return this._db || (this._db = this.createOrOpenDb());
  17. }
  18. /**
  19. * Public method that binds the store required by the consumer and exposes
  20. * the private db getters and setters.
  21. *
  22. * @param storeName String name of desired store
  23. */
  24. getDbTable(storeName) {
  25. if (this.storeNames.includes(storeName)) {
  26. return {
  27. get: this._get.bind(this, storeName),
  28. getAll: this._getAll.bind(this, storeName),
  29. set: this._set.bind(this, storeName),
  30. };
  31. }
  32. throw new Error(`Store name ${storeName} does not exist.`);
  33. }
  34. async _getStore(storeName) {
  35. return (await this.db).objectStore(storeName, "readwrite");
  36. }
  37. _get(storeName, key) {
  38. return this._requestWrapper(async () => (await this._getStore(storeName)).get(key));
  39. }
  40. _getAll(storeName) {
  41. return this._requestWrapper(async () => (await this._getStore(storeName)).getAll());
  42. }
  43. _set(storeName, key, value) {
  44. return this._requestWrapper(async () => (await this._getStore(storeName)).put(value, key));
  45. }
  46. _openDatabase() {
  47. return IndexedDB.open(this.dbName, {version: this.dbVersion}, db => {
  48. // If provided with array of objectStore names we need to create all the
  49. // individual stores
  50. this.storeNames.forEach(store => {
  51. if (!db.objectStoreNames.contains(store)) {
  52. this._requestWrapper(() => db.createObjectStore(store));
  53. }
  54. });
  55. });
  56. }
  57. /**
  58. * createOrOpenDb - Open a db (with this.dbName) if it exists.
  59. * If it does not exist, create it.
  60. * If an error occurs, deleted the db and attempt to
  61. * re-create it.
  62. * @returns Promise that resolves with a db instance
  63. */
  64. async createOrOpenDb() {
  65. try {
  66. const db = await this._openDatabase();
  67. return db;
  68. } catch (e) {
  69. if (this.telemetry) {
  70. this.telemetry.handleUndesiredEvent({data: {event: "INDEXEDDB_OPEN_FAILED"}});
  71. }
  72. await IndexedDB.deleteDatabase(this.dbName);
  73. return this._openDatabase();
  74. }
  75. }
  76. async _requestWrapper(request) {
  77. let result = null;
  78. try {
  79. result = await request();
  80. } catch (e) {
  81. if (this.telemetry) {
  82. this.telemetry.handleUndesiredEvent({data: {event: "TRANSACTION_FAILED"}});
  83. }
  84. throw e;
  85. }
  86. return result;
  87. }
  88. };
  89. function getDefaultOptions(options) {
  90. return {collapsed: !!options.collapsed};
  91. }
  92. const EXPORTED_SYMBOLS = ["ActivityStreamStorage", "getDefaultOptions"];