Skip to content

Commit

Permalink
Workaround for UnknownError in Chrome (#543)
Browse files Browse the repository at this point in the history
  • Loading branch information
dfahlander committed Nov 8, 2023
1 parent c2a2aa3 commit 74891ca
Showing 1 changed file with 94 additions and 79 deletions.
173 changes: 94 additions & 79 deletions src/classes/dexie/dexie-open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,90 +38,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._novip.idbdb = req.result;// db._novip is because db can be an Object.create(origDb).
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._novip.idbdb = req.result; // db._novip is because db can be an Object.create(origDb).

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._novip.idbdb = req.result;// db._novip is because db can be an Object.create(origDb).
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._novip.idbdb = req.result; // db._novip is because db can be an Object.create(origDb).

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.
Expand Down

0 comments on commit 74891ca

Please sign in to comment.