mirror of
https://github.com/zero-peak/ZeroOmega.git
synced 2025-01-22 15:08:12 -05:00
256 lines
8.3 KiB
JavaScript
256 lines
8.3 KiB
JavaScript
import FDBDatabase from "./FDBDatabase.js";
|
|
import FDBOpenDBRequest from "./FDBOpenDBRequest.js";
|
|
import FDBVersionChangeEvent from "./FDBVersionChangeEvent.js";
|
|
import cmp from "./lib/cmp.js";
|
|
import Database from "./lib/Database.js";
|
|
import enforceRange from "./lib/enforceRange.js";
|
|
import { AbortError, VersionError } from "./lib/errors.js";
|
|
import FakeEvent from "./lib/FakeEvent.js";
|
|
import { queueTask } from "./lib/scheduling.js";
|
|
const waitForOthersClosedDelete = (databases, name, openDatabases, cb) => {
|
|
const anyOpen = openDatabases.some(openDatabase2 => {
|
|
return !openDatabase2._closed && !openDatabase2._closePending;
|
|
});
|
|
if (anyOpen) {
|
|
queueTask(() => waitForOthersClosedDelete(databases, name, openDatabases, cb));
|
|
return;
|
|
}
|
|
databases.delete(name);
|
|
cb(null);
|
|
};
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-deleting-a-database
|
|
const deleteDatabase = (databases, name, request, cb) => {
|
|
try {
|
|
const db = databases.get(name);
|
|
if (db === undefined) {
|
|
cb(null);
|
|
return;
|
|
}
|
|
db.deletePending = true;
|
|
const openDatabases = db.connections.filter(connection => {
|
|
return !connection._closed && !connection._closePending;
|
|
});
|
|
for (const openDatabase2 of openDatabases) {
|
|
if (!openDatabase2._closePending) {
|
|
const event = new FDBVersionChangeEvent("versionchange", {
|
|
newVersion: null,
|
|
oldVersion: db.version
|
|
});
|
|
openDatabase2.dispatchEvent(event);
|
|
}
|
|
}
|
|
const anyOpen = openDatabases.some(openDatabase3 => {
|
|
return !openDatabase3._closed && !openDatabase3._closePending;
|
|
});
|
|
if (request && anyOpen) {
|
|
const event = new FDBVersionChangeEvent("blocked", {
|
|
newVersion: null,
|
|
oldVersion: db.version
|
|
});
|
|
request.dispatchEvent(event);
|
|
}
|
|
waitForOthersClosedDelete(databases, name, openDatabases, cb);
|
|
} catch (err) {
|
|
cb(err);
|
|
}
|
|
};
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
|
|
const runVersionchangeTransaction = (connection, version, request, cb) => {
|
|
connection._runningVersionchangeTransaction = true;
|
|
const oldVersion = connection.version;
|
|
const openDatabases = connection._rawDatabase.connections.filter(otherDatabase => {
|
|
return connection !== otherDatabase;
|
|
});
|
|
for (const openDatabase2 of openDatabases) {
|
|
if (!openDatabase2._closed && !openDatabase2._closePending) {
|
|
const event = new FDBVersionChangeEvent("versionchange", {
|
|
newVersion: version,
|
|
oldVersion
|
|
});
|
|
openDatabase2.dispatchEvent(event);
|
|
}
|
|
}
|
|
const anyOpen = openDatabases.some(openDatabase3 => {
|
|
return !openDatabase3._closed && !openDatabase3._closePending;
|
|
});
|
|
if (anyOpen) {
|
|
const event = new FDBVersionChangeEvent("blocked", {
|
|
newVersion: version,
|
|
oldVersion
|
|
});
|
|
request.dispatchEvent(event);
|
|
}
|
|
const waitForOthersClosed = () => {
|
|
const anyOpen2 = openDatabases.some(openDatabase2 => {
|
|
return !openDatabase2._closed && !openDatabase2._closePending;
|
|
});
|
|
if (anyOpen2) {
|
|
queueTask(waitForOthersClosed);
|
|
return;
|
|
}
|
|
|
|
// Set the version of database to version. This change is considered part of the transaction, and so if the
|
|
// transaction is aborted, this change is reverted.
|
|
connection._rawDatabase.version = version;
|
|
connection.version = version;
|
|
|
|
// Get rid of this setImmediate?
|
|
const transaction = connection.transaction(connection.objectStoreNames, "versionchange");
|
|
request.result = connection;
|
|
request.readyState = "done";
|
|
request.transaction = transaction;
|
|
transaction._rollbackLog.push(() => {
|
|
connection._rawDatabase.version = oldVersion;
|
|
connection.version = oldVersion;
|
|
});
|
|
const event = new FDBVersionChangeEvent("upgradeneeded", {
|
|
newVersion: version,
|
|
oldVersion
|
|
});
|
|
request.dispatchEvent(event);
|
|
transaction.addEventListener("error", () => {
|
|
connection._runningVersionchangeTransaction = false;
|
|
// throw arguments[0].target.error;
|
|
// console.log("error in versionchange transaction - not sure if anything needs to be done here", e.target.error.name);
|
|
});
|
|
transaction.addEventListener("abort", () => {
|
|
connection._runningVersionchangeTransaction = false;
|
|
request.transaction = null;
|
|
queueTask(() => {
|
|
cb(new AbortError());
|
|
});
|
|
});
|
|
transaction.addEventListener("complete", () => {
|
|
connection._runningVersionchangeTransaction = false;
|
|
request.transaction = null;
|
|
// Let other complete event handlers run before continuing
|
|
queueTask(() => {
|
|
if (connection._closePending) {
|
|
cb(new AbortError());
|
|
} else {
|
|
cb(null);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
waitForOthersClosed();
|
|
};
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-opening-a-database
|
|
const openDatabase = (databases, name, version, request, cb) => {
|
|
let db = databases.get(name);
|
|
if (db === undefined) {
|
|
db = new Database(name, 0);
|
|
databases.set(name, db);
|
|
}
|
|
if (version === undefined) {
|
|
version = db.version !== 0 ? db.version : 1;
|
|
}
|
|
if (db.version > version) {
|
|
return cb(new VersionError());
|
|
}
|
|
const connection = new FDBDatabase(db);
|
|
if (db.version < version) {
|
|
runVersionchangeTransaction(connection, version, request, err => {
|
|
if (err) {
|
|
// DO THIS HERE: ensure that connection is closed by running the steps for closing a database connection before these
|
|
// steps are aborted.
|
|
return cb(err);
|
|
}
|
|
cb(null, connection);
|
|
});
|
|
} else {
|
|
cb(null, connection);
|
|
}
|
|
};
|
|
class FDBFactory {
|
|
cmp = cmp;
|
|
_databases = new Map();
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-deleteDatabase-IDBOpenDBRequest-DOMString-name
|
|
deleteDatabase(name) {
|
|
const request = new FDBOpenDBRequest();
|
|
request.source = null;
|
|
queueTask(() => {
|
|
const db = this._databases.get(name);
|
|
const oldVersion = db !== undefined ? db.version : 0;
|
|
deleteDatabase(this._databases, name, request, err => {
|
|
if (err) {
|
|
request.error = new DOMException(err.message, err.name);
|
|
request.readyState = "done";
|
|
const event = new FakeEvent("error", {
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
event.eventPath = [];
|
|
request.dispatchEvent(event);
|
|
return;
|
|
}
|
|
request.result = undefined;
|
|
request.readyState = "done";
|
|
const event2 = new FDBVersionChangeEvent("success", {
|
|
newVersion: null,
|
|
oldVersion
|
|
});
|
|
request.dispatchEvent(event2);
|
|
});
|
|
});
|
|
return request;
|
|
}
|
|
|
|
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-open-IDBOpenDBRequest-DOMString-name-unsigned-long-long-version
|
|
open(name, version) {
|
|
if (arguments.length > 1 && version !== undefined) {
|
|
// Based on spec, not sure why "MAX_SAFE_INTEGER" instead of "unsigned long long", but it's needed to pass
|
|
// tests
|
|
version = enforceRange(version, "MAX_SAFE_INTEGER");
|
|
}
|
|
if (version === 0) {
|
|
throw new TypeError();
|
|
}
|
|
const request = new FDBOpenDBRequest();
|
|
request.source = null;
|
|
queueTask(() => {
|
|
openDatabase(this._databases, name, version, request, (err, connection) => {
|
|
if (err) {
|
|
request.result = undefined;
|
|
request.readyState = "done";
|
|
request.error = new DOMException(err.message, err.name);
|
|
const event = new FakeEvent("error", {
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
event.eventPath = [];
|
|
request.dispatchEvent(event);
|
|
return;
|
|
}
|
|
request.result = connection;
|
|
request.readyState = "done";
|
|
const event2 = new FakeEvent("success");
|
|
event2.eventPath = [];
|
|
request.dispatchEvent(event2);
|
|
});
|
|
});
|
|
return request;
|
|
}
|
|
|
|
// https://w3c.github.io/IndexedDB/#dom-idbfactory-databases
|
|
databases() {
|
|
return new Promise(resolve => {
|
|
const result = [];
|
|
for (const [name, database] of this._databases) {
|
|
result.push({
|
|
name,
|
|
version: database.version
|
|
});
|
|
}
|
|
resolve(result);
|
|
});
|
|
}
|
|
toString() {
|
|
return "[object IDBFactory]";
|
|
}
|
|
}
|
|
export default FDBFactory; |