From 922de73f135a30e68f3aa2938ad13032a42a31c5 Mon Sep 17 00:00:00 2001 From: k88hudson Date: Wed, 2 May 2018 16:13:38 -0400 Subject: [PATCH] Bug 1458640 - Prevent any fatal indexedDB errors from crashing section --- system-addon/lib/ASRouter.jsm | 2 +- system-addon/lib/ActivityStreamStorage.jsm | 22 ++++++++++++++++- .../unit/lib/ActivityStreamStorage.test.js | 24 +++++++++++++++---- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/system-addon/lib/ASRouter.jsm b/system-addon/lib/ASRouter.jsm index 6925a6e46..c0a99f27d 100644 --- a/system-addon/lib/ASRouter.jsm +++ b/system-addon/lib/ASRouter.jsm @@ -91,7 +91,7 @@ class _ASRouter { this.initialized = true; this._storage = storage; - const blockList = await this._storage.get("blockList"); + const blockList = await this._storage.get("blockList") || []; this.setState({blockList}); } diff --git a/system-addon/lib/ActivityStreamStorage.jsm b/system-addon/lib/ActivityStreamStorage.jsm index 8d267e2a0..bc5e8a015 100644 --- a/system-addon/lib/ActivityStreamStorage.jsm +++ b/system-addon/lib/ActivityStreamStorage.jsm @@ -16,7 +16,7 @@ this.ActivityStreamStorage = class ActivityStreamStorage { } get db() { - return this._db || (this._db = this._openDatabase()); + return this._db || (this._db = this.createOrOpenDb()); } /** @@ -65,6 +65,26 @@ this.ActivityStreamStorage = class ActivityStreamStorage { }); } + /** + * createOrOpenDb - Open a db (with this.dbName) if it exists. + * If it does not exist, create it. + * If an error occurs, deleted the db and attempt to + * re-create it. + * @returns Promise that resolves with a db instance + */ + async createOrOpenDb() { + try { + const db = await this._openDatabase(); + return db; + } catch (e) { + if (this.telemetry) { + this.telemetry.handleUndesiredEvent({data: {event: "INDEXEDDB_OPEN_FAILED"}}); + } + await IndexedDB.deleteDatabase(this.dbName); + return this._openDatabase(); + } + } + async _requestWrapper(request) { let result = null; try { diff --git a/system-addon/test/unit/lib/ActivityStreamStorage.test.js b/system-addon/test/unit/lib/ActivityStreamStorage.test.js index 952c54dd1..1f306ce36 100644 --- a/system-addon/test/unit/lib/ActivityStreamStorage.test.js +++ b/system-addon/test/unit/lib/ActivityStreamStorage.test.js @@ -9,7 +9,10 @@ describe("ActivityStreamStorage", () => { let storage; beforeEach(() => { sandbox = sinon.sandbox.create(); - indexedDB = {open: sandbox.stub().resolves({})}; + indexedDB = { + open: sandbox.stub().resolves({}), + deleteDatabase: sandbox.stub().resolves() + }; overrider.set({IndexedDB: indexedDB}); storage = new ActivityStreamStorage({ storeNames: ["storage_test"], @@ -19,12 +22,25 @@ describe("ActivityStreamStorage", () => { afterEach(() => { sandbox.restore(); }); - it("should not throw an error when accessing db", async () => { - assert.ok(storage.db); - }); it("should throw if required arguments not provided", () => { assert.throws(() => new ActivityStreamStorage({telemetry: true})); }); + describe(".db", () => { + it("should not throw an error when accessing db", async () => { + assert.ok(storage.db); + }); + + it("should delete and recreate the db if opening db fails", async () => { + const newDb = {}; + indexedDB.open.onFirstCall().rejects(new Error("fake error")); + indexedDB.open.onSecondCall().resolves(newDb); + + const db = await storage.db; + assert.calledOnce(indexedDB.deleteDatabase); + assert.calledTwice(indexedDB.open); + assert.equal(db, newDb); + }); + }); describe("#getDbTable", () => { let testStorage; let storeStub;