Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: lib/ioIndexedDB.js

Issue 29796555: Issue 6621 (Closed)
Patch Set: Created June 1, 2018, 1:58 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/ioIndexedDB.js
diff --git a/lib/ioIndexedDB.js b/lib/ioIndexedDB.js
new file mode 100644
index 0000000000000000000000000000000000000000..5871eed97ef4b554c62e7e1559167ed7648db3c1
--- /dev/null
+++ b/lib/ioIndexedDB.js
@@ -0,0 +1,291 @@
+/*
+ * This file is part of Adblock Plus <https://adblockplus.org/>,
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus 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 Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+
+// from DefaultConfig https://github.com/localForage/localForage/blob/master/src/localforage.js
kzar 2018/06/04 11:06:07 Please could you improve this comment a bit? For e
piscoi.georgiana 2018/06/05 07:15:54 I didn't knew you can link to line numbers. Thanks
+const localForageDefs = {
kzar 2018/06/04 11:06:07 Nit: Would you mind naming this one more consisten
piscoi.georgiana 2018/06/05 07:15:55 Done.
+ dbName: "localforage",
+ storeName: "keyvaluepairs",
+ version: 2
+};
+
+const dbConfig = {
+ dbName: "adbp",
Sebastian Noack 2018/06/01 19:15:46 This acronym seems somewhat bogus. If we abbreviat
piscoi.georgiana 2018/06/05 07:15:55 Done.
+ storeName: "file",
+ keyPath: "fileName",
+ version: 1
+};
+
+let dbInstances = {};
Sebastian Noack 2018/06/01 19:15:46 I wonder whether it would be more appropriate to j
kzar 2018/06/04 11:06:07 Yea, I agree.
piscoi.georgiana 2018/06/05 07:15:54 Done.
piscoi.georgiana 2018/06/05 07:15:55 Done.
+/**
+ * @type Promise
+ */
+let migrationStatus = {};
+
+const keyPrefix = "file:";
+
+ /**
+ * Handles migrating a file, if found, from localforage db to the new one
kzar 2018/06/04 11:06:07 Would you mind specifying what "the new one" is he
piscoi.georgiana 2018/06/05 07:15:54 I've changed it, but I'm not 100% sure
+ * @param {string} fileName
+ * Name of the file to be migrated
+ * @return {Promise}
+ * Promise to be resolved or rejected once the operation is completed
+ */
+function migrateFile(fileName)
+{
+ let fileData;
+ if (!migrationStatus[fileName])
kzar 2018/06/04 11:06:07 Nit: When the body of an if statement spans multip
piscoi.georgiana 2018/06/05 07:15:54 It did pass. My editor shows me lint errors, but I
+ migrationStatus[fileName] = new Promise((resolve, reject) =>
Sebastian Noack 2018/06/01 19:15:46 Perhaps, rather than migrating each file on demand
piscoi.georgiana 2018/06/05 07:15:55 Done.
+ {
+ return openDB(localForageDefs)
+ .then(dbInstance =>
+ getFile(fileName, dbInstance, localForageDefs.storeName))
+ .then(file =>
+ {
+ fileData = file;
Sebastian Noack 2018/06/01 19:15:46 Rather than making fileData a non-local variable,
+ return openDB(dbConfig);
+ })
+ .then(dbInstance =>
+ saveFile(fileData, dbInstance, dbConfig.storeName))
+ .then(() =>
+ deleteFile(
+ fileName,
+ dbInstances[localForageDefs.dbName],
+ localForageDefs.storeName)
+ ).then(() => resolve())
+ .catch(error =>
+ {
+ // file or store wasn't found so no migration needed
+ if (error.type == "NoSuchFile" || error == "NotFoundError")
+ {
+ return resolve();
+ }
+ });
+ });
+ return migrationStatus[fileName];
+}
+
+function fileToKey(fileName)
+{
+ return keyPrefix + fileName;
+}
+
+function formatFile(name, data)
+{
+ return {
+ fileName: fileToKey(name),
+ content: Array.from(data),
+ lastModified: Date.now()
+ };
+}
+
+function openDB(config)
kzar 2018/06/04 11:06:07 I guess we could destructure `config` here? fun
piscoi.georgiana 2018/06/05 07:15:54 Done.
+{
+ return new Promise((resolve, reject) =>
+ {
+ const {dbName, storeName, version} = config;
Sebastian Noack 2018/06/01 19:15:46 Sorry, this is matter of taste, but at least for t
piscoi.georgiana 2018/06/05 07:15:55 For me, I guess it's mostly reflex from working on
+
+ if (dbInstances[dbName])
+ return resolve(dbInstances[dbName]);
+
+ const req = indexedDB.open(dbName, version);
+
+ req.onsuccess = (event) =>
+ {
+ dbInstances[dbName] = event.currentTarget.result;
+ return resolve(dbInstances[dbName]);
+ };
+
+ req.onerror = reject;
+
+ req.onupgradeneeded = (event) =>
+ {
+ event
+ .currentTarget
+ .result
+ .createObjectStore(storeName,
+ {
+ keyPath: dbConfig.keyPath,
+ autoIncrement: true
+ });
+ };
+ });
+}
+
+function getObjectStore(dbInstance, storeName)
+{
+ return dbInstance
+ .transaction([storeName], IDBTransaction.READ_WRITE)
+ .objectStore(storeName);
+}
+
+function getFile(fileName, dbInstance, storeName, from)
+{
+ return new Promise((resolve, reject) =>
+ {
+ const store = getObjectStore(dbInstance, storeName);
+ const req = store.get(fileToKey(fileName));
+
+ req.onsuccess = (event) =>
kzar 2018/06/04 11:06:07 Nit: While we don't have a rule about adding / omi
piscoi.georgiana 2018/06/05 07:15:55 Done.
+ {
+ const result = event.currentTarget.result;
+ if (result)
+ resolve(result);
+ else
+ reject({type: "NoSuchFile"});
kzar 2018/06/04 11:06:07 Nit: This line isn't indented correctly.
piscoi.georgiana 2018/06/05 07:15:54 Done.
+ };
+ req.onerror = reject;
+ });
+}
+
+function saveFile(data, dbInstance, storeName, from)
+{
+ return new Promise((resolve, reject) =>
+ {
+ const store = getObjectStore(dbInstance, storeName);
+ const req = store.put(data);
+
+ req.onsuccess = resolve;
+ req.onerror = reject;
+ });
+}
+
+function deleteFile(fileName, dbInstance, storeName, from)
+{
+ return new Promise((resolve, reject) =>
+ {
+ const store = getObjectStore(dbInstance, storeName);
+ const req = store.delete(fileToKey(fileName));
+
+ req.onsuccess = resolve;
+ req.onerror = reject;
+ });
+}
+
+exports.IO =
+{
+ /**
+ * Writes text lines to a file.
+ * @param {string} fileName
+ * Name of the file to be written
+ * @param {Iterable.<string>} data
+ * An array-like or iterable object containing the lines (without line
+ * endings)
+ * @return {Promise}
+ * Promise to be resolved or rejected once the operation is completed
+ */
+
kzar 2018/06/04 11:06:07 Nit: Please could you remove this newline?
piscoi.georgiana 2018/06/05 07:15:54 Done.
+ writeToFile(fileName, data)
+ {
+ return migrateFile(fileName)
+ .then(() => openDB(dbConfig))
+ .then(dbInstance =>
+ saveFile(
+ formatFile(fileName, data),
+ dbInstance,
+ dbConfig.storeName)
+ );
+ },
+
+ /**
+ * Reads text lines from a file.
+ * @param {string} fileName
+ * Name of the file to be read
+ * @param {TextSink} listener
+ * Function that will be called for each line in the file
+ * @return {Promise}
+ * Promise to be resolved or rejected once the operation is completed
+ */
+ readFromFile(fileName, listener)
+ {
+ return migrateFile(fileName)
+ .then(() => openDB(dbConfig))
+ .then(dbInstance =>
+ getFile(
+ fileName,
+ dbInstance,
+ dbConfig.storeName)
+ ).then(entry =>
+ {
+ for (let line of entry.content)
+ listener(line);
+ });
+ },
+
+ /**
+ * Retrieves file metadata.
+ * @param {string} fileName
+ * Name of the file to be looked up
+ * @return {Promise.<StatData>}
+ * Promise to be resolved with file metadata once the operation is
+ * completed
+ */
+ statFile(fileName)
+ {
+ return migrateFile(fileName)
+ .then(() => openDB(dbConfig))
+ .then(dbInstance =>
+ getFile(
+ fileName,
+ dbInstance,
+ dbConfig.storeName)
+ ).then(entry =>
+ {
+ return {
+ exists: true,
+ lastModified: entry.lastModified
+ };
+ })
+ .catch(error =>
+ {
+ if (error.type == "NoSuchFile")
+ return {exists: false};
+ throw error;
+ });
+ },
+
+ /**
+ * Renames a file.
+ * @param {string} fromFile
+ * Name of the file to be renamed
+ * @param {string} newName
+ * New file name, will be overwritten if exists
+ * @return {Promise}
+ * Promise to be resolved or rejected once the operation is completed
+ */
+
kzar 2018/06/04 11:06:07 Nit: Please could you remove this newline?
piscoi.georgiana 2018/06/05 07:15:55 Done.
+ renameFile(fromFile, newName)
+ {
+ return migrateFile(fromFile)
+ .then(() => openDB(dbConfig))
+ .then(dbInstance => getFile(fromFile, dbInstance, dbConfig.storeName))
+ .then(fileData =>
+ saveFile(
+ {
+ fileName: fileToKey(newName),
+ content: fileData.content,
+ lastModified: fileData.lastModified
+ },
+ dbInstances[dbConfig.dbName],
+ dbConfig.storeName)
+ ).then(() =>
+ deleteFile(fromFile, dbInstances[dbConfig.dbName], dbConfig.storeName));
+ }
+};
+

Powered by Google App Engine
This is Rietveld