diff options
Diffstat (limited to 'ext/bg/js/database.js')
-rw-r--r-- | ext/bg/js/database.js | 327 |
1 files changed, 0 insertions, 327 deletions
diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js deleted file mode 100644 index 068f4a5f..00000000 --- a/ext/bg/js/database.js +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2020-2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -class Database { - constructor() { - this._db = null; - this._isOpening = false; - } - - // Public - - async open(databaseName, version, structure) { - if (this._db !== null) { - throw new Error('Database already open'); - } - if (this._isOpening) { - throw new Error('Already opening'); - } - - try { - this._isOpening = true; - this._db = await this._open(databaseName, version, (db, transaction, oldVersion) => { - this._upgrade(db, transaction, oldVersion, structure); - }); - } finally { - this._isOpening = false; - } - } - - close() { - if (this._db === null) { - throw new Error('Database is not open'); - } - - this._db.close(); - this._db = null; - } - - isOpening() { - return this._isOpening; - } - - isOpen() { - return this._db !== null; - } - - transaction(storeNames, mode) { - if (this._db === null) { - throw new Error(this._isOpening ? 'Database not ready' : 'Database not open'); - } - return this._db.transaction(storeNames, mode); - } - - bulkAdd(objectStoreName, items, start, count) { - return new Promise((resolve, reject) => { - if (start + count > items.length) { - count = items.length - start; - } - - if (count <= 0) { - resolve(); - return; - } - - const end = start + count; - let completedCount = 0; - const onError = (e) => reject(e.target.error); - const onSuccess = () => { - if (++completedCount >= count) { - resolve(); - } - }; - - const transaction = this.transaction([objectStoreName], 'readwrite'); - const objectStore = transaction.objectStore(objectStoreName); - for (let i = start; i < end; ++i) { - const request = objectStore.add(items[i]); - request.onerror = onError; - request.onsuccess = onSuccess; - } - }); - } - - getAll(objectStoreOrIndex, query, resolve, reject) { - if (typeof objectStoreOrIndex.getAll === 'function') { - this._getAllFast(objectStoreOrIndex, query, resolve, reject); - } else { - this._getAllUsingCursor(objectStoreOrIndex, query, resolve, reject); - } - } - - getAllKeys(objectStoreOrIndex, query, resolve, reject) { - if (typeof objectStoreOrIndex.getAll === 'function') { - this._getAllKeysFast(objectStoreOrIndex, query, resolve, reject); - } else { - this._getAllKeysUsingCursor(objectStoreOrIndex, query, resolve, reject); - } - } - - find(objectStoreName, indexName, query, predicate=null, defaultValue) { - return new Promise((resolve, reject) => { - const transaction = this.transaction([objectStoreName], 'readonly'); - const objectStore = transaction.objectStore(objectStoreName); - const objectStoreOrIndex = indexName !== null ? objectStore.index(indexName) : objectStore; - const request = objectStoreOrIndex.openCursor(query, 'next'); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = (e) => { - const cursor = e.target.result; - if (cursor) { - const value = cursor.value; - if (typeof predicate !== 'function' || predicate(value)) { - resolve(value); - } else { - cursor.continue(); - } - } else { - resolve(defaultValue); - } - }; - }); - } - - bulkCount(targets, resolve, reject) { - const targetCount = targets.length; - if (targetCount <= 0) { - resolve(); - return; - } - - let completedCount = 0; - const results = new Array(targetCount).fill(null); - - const onError = (e) => reject(e.target.error); - const onSuccess = (e, index) => { - const count = e.target.result; - results[index] = count; - if (++completedCount >= targetCount) { - resolve(results); - } - }; - - for (let i = 0; i < targetCount; ++i) { - const index = i; - const [objectStoreOrIndex, query] = targets[i]; - const request = objectStoreOrIndex.count(query); - request.onerror = onError; - request.onsuccess = (e) => onSuccess(e, index); - } - } - - delete(objectStoreName, key) { - return new Promise((resolve, reject) => { - const transaction = this.transaction([objectStoreName], 'readwrite'); - const objectStore = transaction.objectStore(objectStoreName); - const request = objectStore.delete(key); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = () => resolve(); - }); - } - - bulkDelete(objectStoreName, indexName, query, filterKeys=null, onProgress=null) { - return new Promise((resolve, reject) => { - const transaction = this.transaction([objectStoreName], 'readwrite'); - const objectStore = transaction.objectStore(objectStoreName); - const objectStoreOrIndex = indexName !== null ? objectStore.index(indexName) : objectStore; - - const onGetKeys = (keys) => { - try { - if (typeof filterKeys === 'function') { - keys = filterKeys(keys); - } - this._bulkDeleteInternal(objectStore, keys, onProgress, resolve, reject); - } catch (e) { - reject(e); - } - }; - - this.getAllKeys(objectStoreOrIndex, query, onGetKeys, reject); - }); - } - - static deleteDatabase(databaseName) { - return new Promise((resolve, reject) => { - const request = indexedDB.deleteDatabase(databaseName); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = () => resolve(); - request.onblocked = () => reject(new Error('Database deletion blocked')); - }); - } - - // Private - - _open(name, version, onUpgradeNeeded) { - return new Promise((resolve, reject) => { - const request = indexedDB.open(name, version); - - request.onupgradeneeded = (event) => { - try { - request.transaction.onerror = (e) => reject(e.target.error); - onUpgradeNeeded(request.result, request.transaction, event.oldVersion, event.newVersion); - } catch (e) { - reject(e); - } - }; - - request.onerror = (e) => reject(e.target.error); - request.onsuccess = () => resolve(request.result); - }); - } - - _upgrade(db, transaction, oldVersion, upgrades) { - for (const {version, stores} of upgrades) { - if (oldVersion >= version) { continue; } - - for (const [objectStoreName, {primaryKey, indices}] of Object.entries(stores)) { - const existingObjectStoreNames = transaction.objectStoreNames || db.objectStoreNames; - const objectStore = ( - this._listContains(existingObjectStoreNames, objectStoreName) ? - transaction.objectStore(objectStoreName) : - db.createObjectStore(objectStoreName, primaryKey) - ); - const existingIndexNames = objectStore.indexNames; - - for (const indexName of indices) { - if (this._listContains(existingIndexNames, indexName)) { continue; } - - objectStore.createIndex(indexName, indexName, {}); - } - } - } - } - - _listContains(list, value) { - for (let i = 0, ii = list.length; i < ii; ++i) { - if (list[i] === value) { return true; } - } - return false; - } - - _getAllFast(objectStoreOrIndex, query, resolve, reject) { - const request = objectStoreOrIndex.getAll(query); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = (e) => resolve(e.target.result); - } - - _getAllUsingCursor(objectStoreOrIndex, query, resolve, reject) { - const results = []; - const request = objectStoreOrIndex.openCursor(query, 'next'); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = (e) => { - const cursor = e.target.result; - if (cursor) { - results.push(cursor.value); - cursor.continue(); - } else { - resolve(results); - } - }; - } - - _getAllKeysFast(objectStoreOrIndex, query, resolve, reject) { - const request = objectStoreOrIndex.getAllKeys(query); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = (e) => resolve(e.target.result); - } - - _getAllKeysUsingCursor(objectStoreOrIndex, query, resolve, reject) { - const results = []; - const request = objectStoreOrIndex.openKeyCursor(query, 'next'); - request.onerror = (e) => reject(e.target.error); - request.onsuccess = (e) => { - const cursor = e.target.result; - if (cursor) { - results.push(cursor.primaryKey); - cursor.continue(); - } else { - resolve(results); - } - }; - } - - _bulkDeleteInternal(objectStore, keys, onProgress, resolve, reject) { - const count = keys.length; - if (count === 0) { - resolve(); - return; - } - - let completedCount = 0; - const hasProgress = (typeof onProgress === 'function'); - - const onError = (e) => reject(e.target.error); - const onSuccess = () => { - ++completedCount; - if (hasProgress) { - try { - onProgress(completedCount, count); - } catch (e) { - // NOP - } - } - if (completedCount >= count) { - resolve(); - } - }; - - for (const key of keys) { - const request = objectStore.delete(key); - request.onerror = onError; - request.onsuccess = onSuccess; - } - } -} |