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

Side by Side Diff: lib/ioIndexedDB.js

Issue 29796555: Issue 6621 (Closed)
Patch Set: Comment improvements and a rename Created June 5, 2018, 7:05 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« lib/io.js ('K') | « lib/io.js ('k') | metadata.edge » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 "use strict";
19
20
21 // values from the DefaultConfig https://github.com/localForage/localForage/blob /2cdbd74/src/localforage.js#L42-L51
22 const localForageDbConfig = {
23 dbName: "localforage",
24 storeName: "keyvaluepairs",
25 version: 2
26 };
27
28 const dbConfig = {
29 dbName: "adblockplus",
30 storeName: "file",
31 keyPath: "fileName",
32 version: 1
33 };
34
35 let db;
kzar 2018/06/05 12:38:53 I'd rather we stored the connection promise here,
piscoi.georgiana 2018/06/11 07:02:45 Done.
36 let localForageDb;
kzar 2018/06/05 12:38:53 We don't use the localforage database connection o
piscoi.georgiana 2018/06/11 07:02:44 Done.
37 let migrationDone = migrateFiles();
38
39 const keyPrefix = "file:";
40
41 /**
42 * Handles migrating all files from localforage db
43 * used in the previous implementation by the localForage library
44 * to the new adblockplus db that we use as a replacement
45 * @return {Promise}
46 * Promise to be resolved or rejected once the operation is completed
47 */
48 function migrateFiles()
49 {
50 return openDB(dbConfig)
kzar 2018/06/05 12:38:53 Nit: I think we should move the openDB function's
piscoi.georgiana 2018/06/11 07:02:45 Is there any specific rule/preference regarding re
kzar 2018/06/12 10:46:16 No rule that I know of, just something I try to do
Sebastian Noack 2018/06/13 00:55:11 The idea is to increase code locality, i.e. so tha
piscoi.georgiana 2018/06/14 13:41:26 Done.
51 .then(dbInstance =>
52 {
53 db = dbInstance;
kzar 2018/06/05 12:38:53 (I think assigning something from a promise which
piscoi.georgiana 2018/06/11 07:02:44 Done.
54 return openDB(localForageDbConfig);
55 })
56 .then(dbInstance =>
57 {
58 localForageDb = dbInstance;
59 return getAllFiles(localForageDb, localForageDbConfig.storeName);
60 })
61 .then(files =>
62 files.map(file =>
63 Promise.all(saveFile(file, db, dbConfig.storeName))))
kzar 2018/06/05 12:38:53 Supposing a file exists in both databases wouldn't
piscoi.georgiana 2018/06/11 07:02:45 Yes, it will override any file that is present in
kzar 2018/06/12 10:46:16 What do you think Sebastian? I think I'd prefer th
Sebastian Noack 2018/06/13 00:55:11 I think it's fine to copy the file over from local
64 .then(() =>
65 clearObjectStore(localForageDb, localForageDbConfig.storeName));
kzar 2018/06/05 12:38:53 I wonder what will happen the next time we try to
piscoi.georgiana 2018/06/11 07:02:44 No, it won't throw. The cursor value will be null,
kzar 2018/06/12 10:46:16 Acknowledged.
66 }
67
68 function getAllFiles(dbInstance, storeName)
69 {
70 return new Promise((resolve, reject) =>
71 {
72 // edge doesn't currently support getAll method on IDBObjectStore interface
73 // so a cursor is used to iterate over all objects from the store
74 let transaction = dbInstance
75 .transaction([storeName], IDBTransaction.READ_ONLY);
76
77 let store = transaction.objectStore(storeName);
78 let cursorReq = store.openCursor();
79 let filesData = [];
80
81 transaction.oncomplete = event =>
82 {
83 resolve(filesData);
84 };
85
86 cursorReq.onsuccess = event =>
87 {
88 let cursor = event.currentTarget.result;
89 if (cursor)
90 {
91 let value = cursor.value;
92
93 filesData.push({
94 fileName: cursor.key,
95 content: value.content,
96 lastModified: value.lastModified
97 });
98 cursor.continue();
99 }
100 };
101
102 cursorReq.onerror = reject;
103 });
104 }
105
106 function clearObjectStore(dbInstance, storeName)
107 {
108 return new Promise((resolve, reject) =>
109 {
110 let store = getObjectStore(dbInstance, storeName);
111 let req = store.clear();
112
113 req.onsuccess = resolve;
114 req.onerror = reject;
115 });
116 }
117
118 function fileToKey(fileName)
119 {
120 return keyPrefix + fileName;
121 }
122
123 function formatFile(name, data)
124 {
125 return {
126 fileName: fileToKey(name),
127 content: Array.from(data),
128 lastModified: Date.now()
129 };
130 }
131
132 function openDB({dbName, storeName, version, keyPath})
133 {
134 return new Promise((resolve, reject) =>
135 {
136 let req = indexedDB.open(dbName, version);
137
138 req.onsuccess = event =>
139 {
140 return resolve(event.currentTarget.result);
141 };
142
143 req.onerror = reject;
144
145 req.onupgradeneeded = event =>
146 {
147 event
148 .currentTarget
149 .result
150 .createObjectStore(storeName,
151 {
152 keyPath,
153 autoIncrement: true
154 });
155 };
156 });
157 }
158
159 function getObjectStore(dbInstance, storeName)
160 {
161 return dbInstance
162 .transaction([storeName], IDBTransaction.READ_WRITE)
163 .objectStore(storeName);
164 }
165
166 function getFile(fileName, dbInstance, storeName)
167 {
168 return new Promise((resolve, reject) =>
169 {
170 let store = getObjectStore(dbInstance, storeName);
171 let req = store.get(fileToKey(fileName));
172
173 req.onsuccess = event =>
174 {
175 let result = event.currentTarget.result;
176
177 if (result)
178 resolve(result);
179 else
180 reject({type: "NoSuchFile"});
181 };
182 req.onerror = reject;
183 });
184 }
185
186 function saveFile(data, dbInstance, storeName)
187 {
188 return new Promise((resolve, reject) =>
189 {
190 let store = getObjectStore(dbInstance, storeName);
191 let req = store.put(data);
192
193 req.onsuccess = resolve;
194 req.onerror = reject;
195 });
196 }
197
198 function deleteFile(fileName, dbInstance, storeName)
199 {
200 return new Promise((resolve, reject) =>
201 {
202 let store = getObjectStore(dbInstance, storeName);
203 let req = store.delete(fileToKey(fileName));
204
205 req.onsuccess = resolve;
206 req.onerror = reject;
207 });
208 }
209
210 exports.IO =
211 {
212 /**
213 * Writes text lines to a file.
214 * @param {string} fileName
215 * Name of the file to be written
216 * @param {Iterable.<string>} data
217 * An array-like or iterable object containing the lines (without line
218 * endings)
219 * @return {Promise}
220 * Promise to be resolved or rejected once the operation is completed
221 */
222 writeToFile(fileName, data)
223 {
224 return migrationDone
225 .then(() => saveFile(formatFile(fileName, data), db, dbConfig.storeName));
226 },
227
228 /**
229 * Reads text lines from a file.
230 * @param {string} fileName
231 * Name of the file to be read
232 * @param {TextSink} listener
233 * Function that will be called for each line in the file
234 * @return {Promise}
235 * Promise to be resolved or rejected once the operation is completed
236 */
237 readFromFile(fileName, listener)
238 {
239 return migrationDone
240 .then(() => getFile(fileName, db, dbConfig.storeName))
241 .then(entry =>
242 {
243 for (let line of entry.content)
244 listener(line);
245 });
246 },
247
248 /**
249 * Retrieves file metadata.
250 * @param {string} fileName
251 * Name of the file to be looked up
252 * @return {Promise.<StatData>}
253 * Promise to be resolved with file metadata once the operation is
254 * completed
255 */
256 statFile(fileName)
257 {
258 return migrationDone
259 .then(() => getFile(fileName, db, dbConfig.storeName))
260 .then(entry =>
261 {
262 return {
263 exists: true,
264 lastModified: entry.lastModified
265 };
266 })
267 .catch(error =>
268 {
269 if (error.type == "NoSuchFile")
270 return {exists: false};
271 throw error;
272 });
273 },
274
275 /**
276 * Renames a file.
277 * @param {string} fromFile
278 * Name of the file to be renamed
279 * @param {string} newName
280 * New file name, will be overwritten if exists
281 * @return {Promise}
282 * Promise to be resolved or rejected once the operation is completed
283 */
284 renameFile(fromFile, newName)
285 {
286 return migrationDone
287 .then(() => getFile(fromFile, db, dbConfig.storeName))
288 .then(fileData =>
289 saveFile(
290 {
291 fileName: fileToKey(newName),
292 content: fileData.content,
293 lastModified: fileData.lastModified
294 },
295 db,
296 dbConfig.storeName))
297 .then(() => deleteFile(fromFile, db, dbConfig.storeName));
298 }
299 };
300
OLDNEW
« lib/io.js ('K') | « lib/io.js ('k') | metadata.edge » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld