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: Remaining change from p3 and changes from p4 Created June 14, 2018, 1:36 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « 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
22 // https://github.com/localForage/localForage/blob/2cdbd74/src/localforage.js#L4 2-L51
23 const localForageDbConfig = {
24 dbName: "localforage",
25 storeName: "keyvaluepairs",
26 version: 2
27 };
28
29 const dbConfig = {
30 dbName: "adblockplus",
31 storeName: "file",
32 keyPath: "fileName",
33 version: 1
34 };
35
36 let db = openDB(dbConfig);
37 let migrationDone = migrateFiles();
38
39 const keyPrefix = "file:";
40
41 function openDB({dbName, storeName, version, keyPath})
42 {
43 return new Promise((resolve, reject) =>
44 {
45 let req = indexedDB.open(dbName, version);
46
47 req.onsuccess = event =>
48 {
49 return resolve(event.currentTarget.result);
50 };
51
52 req.onerror = reject;
53
54 req.onupgradeneeded = event =>
55 {
56 event
57 .currentTarget
58 .result
59 .createObjectStore(storeName,
60 {
61 keyPath,
62 autoIncrement: true
63 });
64 };
65 });
66 }
67
68 /**
69 * Handles migrating all files from localforage db
70 * used in the previous implementation by the localForage library
71 * to the new adblockplus db that we use as a replacement
72 * @return {Promise}
73 * Promise to be resolved or rejected once the operation is completed
74 */
75 function migrateFiles()
76 {
77 return openDB(localForageDbConfig)
78 .then(localForageDb =>
79 getAllFiles(localForageDb, localForageDbConfig.storeName)
80 .then(files =>
81 db.then(dbInstance =>
82 Promise.all(files.map(file =>
83 saveFile(file, dbInstance, dbConfig.storeName)))))
84 .then(() =>
85 clearObjectStore(localForageDb, localForageDbConfig.storeName))
86 );
87 }
88
89 function getAllFiles(dbInstance, storeName)
90 {
91 return new Promise((resolve, reject) =>
92 {
93 // edge doesn't currently support getAll method on IDBObjectStore interface
94 // so a cursor is used to iterate over all objects from the store
95 let transaction = dbInstance
96 .transaction([storeName], IDBTransaction.READ_ONLY);
97
98 let store = transaction.objectStore(storeName);
99 let cursorReq = store.openCursor();
100 let filesData = [];
101
102 transaction.oncomplete = event =>
103 {
104 resolve(filesData);
105 };
106
107 cursorReq.onsuccess = event =>
108 {
109 let cursor = event.currentTarget.result;
110 if (cursor)
111 {
112 let value = cursor.value;
113
114 filesData.push({
115 fileName: cursor.key,
116 content: value.content,
117 lastModified: value.lastModified
118 });
119 cursor.continue();
120 }
121 };
122
123 cursorReq.onerror = reject;
124 });
125 }
126
127 function clearObjectStore(dbInstance, storeName)
128 {
129 return new Promise((resolve, reject) =>
130 {
131 let store = getObjectStore(dbInstance, storeName);
132 let req = store.clear();
133
134 req.onsuccess = resolve;
135 req.onerror = reject;
136 });
137 }
138
139 function fileToKey(fileName)
140 {
141 return keyPrefix + fileName;
142 }
143
144 function formatFile(name, data)
145 {
146 return {
147 fileName: fileToKey(name),
148 content: Array.from(data),
149 lastModified: Date.now()
150 };
151 }
152
153 function getObjectStore(dbInstance, storeName)
154 {
155 return dbInstance
156 .transaction([storeName], IDBTransaction.READ_WRITE)
157 .objectStore(storeName);
158 }
159
160 function getFile(fileName, dbInstance, storeName)
161 {
162 return new Promise((resolve, reject) =>
163 {
164 let store = getObjectStore(dbInstance, storeName);
165 let req = store.get(fileToKey(fileName));
166
167 req.onsuccess = event =>
168 {
169 let result = event.currentTarget.result;
170
171 if (result)
172 resolve(result);
173 else
174 reject({type: "NoSuchFile"});
175 };
176 req.onerror = reject;
177 });
178 }
179
180 function saveFile(data, dbInstance, storeName)
181 {
182 return new Promise((resolve, reject) =>
183 {
184 let store = getObjectStore(dbInstance, storeName);
185 let req = store.put(data);
186
187 req.onsuccess = resolve;
188 req.onerror = reject;
189 });
190 }
191
192 function deleteFile(fileName, dbInstance, storeName)
193 {
194 return new Promise((resolve, reject) =>
195 {
196 let store = getObjectStore(dbInstance, storeName);
197 let req = store.delete(fileToKey(fileName));
198
199 req.onsuccess = resolve;
200 req.onerror = reject;
201 });
202 }
203
204 exports.IO =
205 {
206 /**
207 * Writes text lines to a file.
208 * @param {string} fileName
209 * Name of the file to be written
210 * @param {Iterable.<string>} data
211 * An array-like or iterable object containing the lines (without line
212 * endings)
213 * @return {Promise}
214 * Promise to be resolved or rejected once the operation is completed
215 */
216 writeToFile(fileName, data)
217 {
218 return migrationDone
219 .then(() =>
220 db.then(dbInstance =>
221 saveFile(
222 formatFile(fileName, data), dbInstance, dbConfig.storeName)));
223 },
224
225 /**
226 * Reads text lines from a file.
227 * @param {string} fileName
228 * Name of the file to be read
229 * @param {TextSink} listener
230 * Function that will be called for each line in the file
231 * @return {Promise}
232 * Promise to be resolved or rejected once the operation is completed
233 */
234 readFromFile(fileName, listener)
235 {
236 return migrationDone
237 .then(() =>
238 db.then(dbInstance =>
239 getFile(fileName, dbInstance, dbConfig.storeName))
240 .then(entry =>
241 {
242 for (let line of entry.content)
243 listener(line);
244 }));
245 },
246
247 /**
248 * Retrieves file metadata.
249 * @param {string} fileName
250 * Name of the file to be looked up
251 * @return {Promise.<StatData>}
252 * Promise to be resolved with file metadata once the operation is
253 * completed
254 */
255 statFile(fileName)
256 {
257 return migrationDone
258 .then(() =>
259 db.then(dbInstance =>
260 getFile(fileName, dbInstance, dbConfig.storeName))
261 .then(entry =>
262 {
263 return {
264 exists: true,
265 lastModified: entry.lastModified
266 };
267 }))
268 .catch(error =>
269 {
270 if (error.type == "NoSuchFile")
271 return {exists: false};
272 throw error;
273 });
274 },
275
276 /**
277 * Renames a file.
278 * @param {string} fromFile
279 * Name of the file to be renamed
280 * @param {string} newName
281 * New file name, will be overwritten if exists
282 * @return {Promise}
283 * Promise to be resolved or rejected once the operation is completed
284 */
285 renameFile(fromFile, newName)
286 {
287 return migrationDone
288 .then(() =>
289 db.then(dbInstance =>
290 getFile(fromFile, dbInstance, dbConfig.storeName)
291 .then(fileData =>
292 saveFile(
293 {
294 fileName: fileToKey(newName),
295 content: fileData.content,
296 lastModified: fileData.lastModified
297 },
298 dbInstance,
299 dbConfig.storeName))
300 .then(() => deleteFile(fromFile, dbInstance, dbConfig.storeName))));
301 }
302 };
303
OLDNEW
« no previous file with comments | « lib/io.js ('k') | metadata.edge » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld