diff --git a/src/classes/dexie/dexie-open.ts b/src/classes/dexie/dexie-open.ts index 9dfc89430..1a6990412 100644 --- a/src/classes/dexie/dexie-open.ts +++ b/src/classes/dexie/dexie-open.ts @@ -42,90 +42,105 @@ export function dexieOpen (db: Dexie) { // upgradeTransaction to abort on failure. upgradeTransaction: (IDBTransaction | null) = null, wasCreated = false; - - // safari14Workaround = Workaround by jakearchibald for new nasty bug in safari 14. - return Promise.race([openCanceller, (typeof navigator === 'undefined' ? Promise.resolve() : safari14Workaround()).then(() => new Promise((resolve, reject) => { - // Multiply db.verno with 10 will be needed to workaround upgrading bug in IE: - // IE fails when deleting objectStore after reading from it. - // A future version of Dexie.js will stopover an intermediate version to workaround this. - // At that point, we want to be backward compatible. Could have been multiplied with 2, but by using 10, it is easier to map the number to the real version number. - - throwIfCancelled(); - // If no API, throw! - if (!indexedDB) throw new exceptions.MissingAPI(); - const dbName = db.name; - - const req = state.autoSchema ? - indexedDB.open(dbName) : - indexedDB.open(dbName, Math.round(db.verno * 10)); - if (!req) throw new exceptions.MissingAPI(); // May happen in Safari private mode, see https://github.com/dfahlander/Dexie.js/issues/134 - req.onerror = eventRejectHandler(reject); - req.onblocked = wrap(db._fireOnBlocked); - req.onupgradeneeded = wrap (e => { - upgradeTransaction = req.transaction; - if (state.autoSchema && !db._options.allowEmptyDB) { // Unless an addon has specified db._allowEmptyDB, lets make the call fail. - // Caller did not specify a version or schema. Doing that is only acceptable for opening alread existing databases. - // If onupgradeneeded is called it means database did not exist. Reject the open() promise and make sure that we - // do not create a new database by accident here. - req.onerror = preventDefault; // Prohibit onabort error from firing before we're done! - upgradeTransaction.abort(); // Abort transaction (would hope that this would make DB disappear but it doesnt.) - // Close database and delete it. - req.result.close(); - const delreq = indexedDB.deleteDatabase(dbName); // The upgrade transaction is atomic, and javascript is single threaded - meaning that there is no risk that we delete someone elses database here! - delreq.onsuccess = delreq.onerror = wrap(() => { - reject (new exceptions.NoSuchDatabase(`Database ${dbName} doesnt exist`)); - }); - } else { - upgradeTransaction.onerror = eventRejectHandler(reject); - var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; // Safari 8 fix. - wasCreated = oldVer < 1; - db.idbdb = req.result; - runUpgraders(db, oldVer / 10, upgradeTransaction, reject); - } - }, reject); - - req.onsuccess = wrap (() => { - // Core opening procedure complete. Now let's just record some stuff. - upgradeTransaction = null; - const idbdb = db.idbdb = req.result; - const objectStoreNames = slice(idbdb.objectStoreNames); - if (objectStoreNames.length > 0) try { - const tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly'); - if (state.autoSchema) readGlobalSchema(db, idbdb, tmpTrans); - else { - adjustToExistingIndexNames(db, db._dbSchema, tmpTrans); - if (!verifyInstalledSchema(db, tmpTrans)) { - console.warn(`Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Some queries may fail.`); - } - } - generateMiddlewareStacks(db, tmpTrans); - } catch (e) { - // Safari 8 may bail out if > 1 store names. However, this shouldnt be a showstopper. Issue #120. - // BUGBUG: It will bail out anyway as of Dexie 3. - // Should we support Safari 8 anymore? Believe all - // Dexie users use the shim for that platform anyway?! - // If removing Safari 8 support, go ahead and remove the safariMultiStoreFix() function - // as well as absurd upgrade version quirk for Safari. + const tryOpenDB = () => new Promise((resolve, reject) => { + // Multiply db.verno with 10 will be needed to workaround upgrading bug in IE: + // IE fails when deleting objectStore after reading from it. + // A future version of Dexie.js will stopover an intermediate version to workaround this. + // At that point, we want to be backward compatible. Could have been multiplied with 2, but by using 10, it is easier to map the number to the real version number. + + throwIfCancelled(); + // If no API, throw! + if (!indexedDB) throw new exceptions.MissingAPI(); + const dbName = db.name; + + const req = state.autoSchema ? + indexedDB.open(dbName) : + indexedDB.open(dbName, Math.round(db.verno * 10)); + if (!req) throw new exceptions.MissingAPI(); // May happen in Safari private mode, see https://github.com/dfahlander/Dexie.js/issues/134 + req.onerror = eventRejectHandler(reject); + req.onblocked = wrap(db._fireOnBlocked); + req.onupgradeneeded = wrap (e => { + upgradeTransaction = req.transaction; + if (state.autoSchema && !db._options.allowEmptyDB) { // Unless an addon has specified db._allowEmptyDB, lets make the call fail. + // Caller did not specify a version or schema. Doing that is only acceptable for opening alread existing databases. + // If onupgradeneeded is called it means database did not exist. Reject the open() promise and make sure that we + // do not create a new database by accident here. + req.onerror = preventDefault; // Prohibit onabort error from firing before we're done! + upgradeTransaction.abort(); // Abort transaction (would hope that this would make DB disappear but it doesnt.) + // Close database and delete it. + req.result.close(); + const delreq = indexedDB.deleteDatabase(dbName); // The upgrade transaction is atomic, and javascript is single threaded - meaning that there is no risk that we delete someone elses database here! + delreq.onsuccess = delreq.onerror = wrap(() => { + reject (new exceptions.NoSuchDatabase(`Database ${dbName} doesnt exist`)); + }); + } else { + upgradeTransaction.onerror = eventRejectHandler(reject); + var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; // Safari 8 fix. + wasCreated = oldVer < 1; + db.idbdb = req.result; + runUpgraders(db, oldVer / 10, upgradeTransaction, reject); + } + }, reject); + + req.onsuccess = wrap (() => { + // Core opening procedure complete. Now let's just record some stuff. + upgradeTransaction = null; + const idbdb = db.idbdb = req.result; + + const objectStoreNames = slice(idbdb.objectStoreNames); + if (objectStoreNames.length > 0) try { + const tmpTrans = idbdb.transaction(safariMultiStoreFix(objectStoreNames), 'readonly'); + if (state.autoSchema) readGlobalSchema(db, idbdb, tmpTrans); + else { + adjustToExistingIndexNames(db, db._dbSchema, tmpTrans); + if (!verifyInstalledSchema(db, tmpTrans)) { + console.warn(`Dexie SchemaDiff: Schema was extended without increasing the number passed to db.version(). Some queries may fail.`); + } } - - connections.push(db); // Used for emulating versionchange event on IE/Edge/Safari. - - idbdb.onversionchange = wrap(ev => { - state.vcFired = true; // detect implementations that not support versionchange (IE/Edge/Safari) - db.on("versionchange").fire(ev); - }); - - idbdb.onclose = wrap(ev => { - db.on("close").fire(ev); - }); + generateMiddlewareStacks(db, tmpTrans); + } catch (e) { + // Safari 8 may bail out if > 1 store names. However, this shouldnt be a showstopper. Issue #120. + // BUGBUG: It will bail out anyway as of Dexie 3. + // Should we support Safari 8 anymore? Believe all + // Dexie users use the shim for that platform anyway?! + // If removing Safari 8 support, go ahead and remove the safariMultiStoreFix() function + // as well as absurd upgrade version quirk for Safari. + } + + connections.push(db); // Used for emulating versionchange event on IE/Edge/Safari. + + idbdb.onversionchange = wrap(ev => { + state.vcFired = true; // detect implementations that not support versionchange (IE/Edge/Safari) + db.on("versionchange").fire(ev); + }); + + idbdb.onclose = wrap(ev => { + db.on("close").fire(ev); + }); - if (wasCreated) _onDatabaseCreated(db._deps, dbName); + if (wasCreated) _onDatabaseCreated(db._deps, dbName); - resolve(); + resolve(); - }, reject); - }))]).then(() => { + }, reject); + }).catch(err => { + if (err && err.name === 'UnknownError' && state.PR1398_maxLoop > 0) { + // Bug in Chrome after clearing site data + // https://github.com/dexie/Dexie.js/issues/543#issuecomment-1795736695 + state.PR1398_maxLoop--; + console.warn('Dexie: Workaround for Chrome UnknownError on open()'); + return tryOpenDB(); + } else { + return Promise.reject(err); + } + }); + + // safari14Workaround = Workaround by jakearchibald for new nasty bug in safari 14. + return Promise.race([ + openCanceller, + (typeof navigator === 'undefined' ? Promise.resolve() : safari14Workaround()).then(tryOpenDB) + ]).then(() => { // Before finally resolving the dbReadyPromise and this promise, // call and await all on('ready') subscribers: // Dexie.vip() makes subscribers able to use the database while being opened.