Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: src/js/polyfills/storage.polyfill.js

Issue 1593443003: Finished storage polyfill. (Closed) Base URL: sso://user/alger/caterpillar@master
Patch Set: Response to CR Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/js/polyfills/storage.manifest.json ('k') | tests/js/polyfills/storage.polyfill.test.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 Google Inc. All Rights Reserved. 1 // Copyright 2015 Google Inc. All Rights Reserved.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 /** 15 /**
16 * Polyfill for Chrome Apps' storage API. 16 * Polyfill for Chrome Apps' storage API.
17 */ 17 */
18 18
19 'use strict'; 19 'use strict';
20 20
21 if (!chrome.storage) 21 if (!chrome.storage)
22 chrome.storage = {}; 22 chrome.storage = {};
23 23
24 chrome.caterpillar.storage = {};
25
24 (function() { 26 (function() {
25 27
26 /** 28 /**
27 * Triggers a chrome.storage.onChanged event on self. 29 * Triggers a chrome.storage.onChanged event on self.
28 * 30 *
29 * This is used internally within the polyfill to manage change events; event 31 * This is used internally within the polyfill to manage change events; event
30 * information is stored in event.detail. 32 * information is stored in event.detail.
31 * 33 *
32 * @param {object} items Object mapping keys to StorageChanges containing the 34 * @param {object} items Object mapping keys to StorageChanges containing the
33 * new and old values of each item. 35 * new and old values of each item.
34 */ 36 */
35 function triggerOnChanged(items) { 37 function triggerOnChanged(items) {
36 var event = new CustomEvent('chrome.storage.onChanged', {'detail': items}); 38 var event = new CustomEvent('chrome.storage.onChanged', {'detail': items});
37 self.dispatchEvent(event); 39 self.dispatchEvent(event);
38 }; 40 };
39 41
40 /** 42 /**
43 * Stores onChanged event listeners.
44 */
45 var onChangedListeners = [];
46
47 /**
41 * Represents a change in stored data. 48 * Represents a change in stored data.
42 */ 49 */
43 chrome.storage.StorageChange = class { 50 chrome.storage.StorageChange = class {
44 /** 51 /**
45 * @param {object=} opt_oldValue The old value of the item. 52 * @param {object=} opt_oldValue The old value of the item.
46 * @param {object=} opt_newValue The new value of the item. 53 * @param {object=} opt_newValue The new value of the item.
47 */ 54 */
48 constructor(opt_oldValue, opt_newValue) { 55 constructor(opt_oldValue, opt_newValue) {
49 this.oldValue = opt_oldValue; 56 this.oldValue = opt_oldValue;
50 this.newValue = opt_newValue; 57 this.newValue = opt_newValue;
51 }; 58 }
52 }; 59 };
53 60
54 /** 61 /**
55 * Represents an area where data can be stored. 62 * Represents an area where data can be stored.
56 * 63 *
57 * Chrome Apps have three such areas - sync, local, managed. 64 * Chrome Apps have three such areas - sync, local, managed.
58 */ 65 */
59 chrome.storage.StorageArea = class { 66 chrome.storage.StorageArea = class {
60 /** 67 /**
61 * Gets one or more items from storage. 68 * Gets one or more items from storage.
62 * 69 *
63 * @param {string | string[] | object} opt_keys A single key to get, list 70 * @param {string= | string[]= | object=} opt_keys A single key to get, list
64 * of keys to get, or a dictionary specifying default values. Empty lists 71 * of keys to get, or a dictionary specifying default values. Empty lists
65 * or objects return empty results. Pass null to get all contents. 72 * or objects return empty results. Pass null to get all contents.
66 * @param {function} callback Callback with storage items, or on failure. 73 * @param {function} callback Callback with storage items, or on failure.
67 */ 74 */
68 get(opt_keys, callback) { 75 get(opt_keys, callback) {
69 // Handle the optional argument being *first*. 76 // Juggle arguments.
70 if (callback === undefined) { 77 if (callback === undefined) {
71 // Keys wasn't actually given, the callback was. 78 // Keys wasn't actually given, the callback was.
72 callback = opt_keys; 79 callback = opt_keys;
73 opt_keys = null; 80 opt_keys = null;
74 } 81 }
75 82
76 // Four scenarios: 83 // Four scenarios:
77 // 1. Input is a single string key; retrieve associated value. 84 // 1. Input is a single string key; retrieve associated value.
78 // 2. Input is multiple string keys; retrieve associated values. 85 // 2. Input is multiple string keys; retrieve associated values.
79 // 3. Input is a map from string key to default value; retrieve associated 86 // 3. Input is a map from string key to default value; retrieve associated
80 // values and rely on defaults if necessary. 87 // values and rely on defaults if necessary.
81 // 4. Input is null; retrieve all key/value pairs. 88 // 4. Input is null; retrieve all key/value pairs.
82 89
83 var handleError = function(err) { 90 var handleError = function(err) {
84 chrome.caterpillar.setError('Error retrieving values: ' + err); 91 chrome.caterpillar.setError('Error retrieving values: ' + err);
85 callback(); 92 callback();
86 } 93 }
87 94
95 if (typeof opt_keys === 'string')
96 opt_keys = [opt_keys];
97
88 if (opt_keys === null) { 98 if (opt_keys === null) {
89 // null input; get all key/value pairs. 99 // null input; get all key/value pairs.
90 var items = {}; 100 var items = {};
91 localforage.iterate(function (value, key) { items[key] = value; }) 101 localforage.iterate(function (value, key) { items[key] = value; })
92 .then(callback.bind(this, items)) 102 .then(callback.bind(this, items))
93 .catch(handleError); 103 .catch(handleError);
94 } else if (Array.isArray(opt_keys)) { 104 } else if (Array.isArray(opt_keys)) {
95 // Array input; get associated values of each key. 105 // Array input; get associated values of each key.
96 var valuePromises = opt_keys.map(key => localforage.getItem(key)); 106 var valuePromises = opt_keys.map(key => localforage.getItem(key));
97 Promise.all(valuePromises) 107 Promise.all(valuePromises)
98 .then(values => { 108 .then(values => {
99 // The callback expects a map from keys to values, but 109 // The callback expects a map from keys to values, but
100 // localforage just gives us values. 110 // localforage just gives us values.
101 var items = {}; 111 var items = {};
102 for (var i = 0; i < opt_keys.length; i++) { 112 for (var i = 0; i < opt_keys.length; i++) {
103 items[opt_keys[i]] = values[i]; 113 items[opt_keys[i]] = values[i];
104 } 114 }
105 callback(items); 115 callback(items);
106 }) 116 })
107 .catch(handleError); 117 .catch(handleError);
108 } else if (typeof opt_keys === 'string') {
109 // String input; get associated value of the key.
110 localforage.getItem(opt_keys).then(value => {
111 var items = {};
112 items[opt_keys] = value;
113 callback(items);
114 }).catch(handleError);
115 } else { 118 } else {
116 // Object input; get associated values with defaults. 119 // Object input; get associated values with defaults.
117 var keys = Object.keys(opt_keys); 120 var keys = Object.keys(opt_keys);
118 Promise.all(keys.map(key => localforage.getItem(key))) 121 Promise.all(keys.map(key => localforage.getItem(key)))
119 .then(values => { 122 .then(values => {
120 var items = {}; 123 var items = {};
121 for (var i = 0; i < keys.length; i++) { 124 for (var i = 0; i < keys.length; i++) {
122 if (values[i] === null) { 125 if (values[i] === null) {
123 items[keys[i]] = opt_keys[keys[i]]; 126 items[keys[i]] = opt_keys[keys[i]];
124 } else { 127 } else {
125 items[keys[i]] = values[i]; 128 items[keys[i]] = values[i];
126 } 129 }
127 } 130 }
128 callback(items); 131 callback(items);
129 }) 132 })
130 .catch(handleError); 133 .catch(handleError);
131 } 134 }
132 } 135 }
136
137 /**
138 * Gets the amount of space (in bytes) being used by one or more items.
139 *
140 * Not implemented in this polyfill.
141 *
142 * @param {string= | string[]= | object=} opt_keys A single key to get,
143 * list of keys to get, or a dictionary specifying default values. Empty
144 * lists or objects return 0. Pass null to get total usage.
145 * @param {function} callback Callback with bytes in use, or failure.
146 */
147 getBytesInUse(opt_keys, callback) {
148 // Juggle arguments.
149 if (callback === undefined) {
150 callback = opt_keys;
151 opt_keys = null;
152 }
153 // IndexedDB doesn't support this, so neither does localforage.
154 chrome.caterpillar.setError('getBytesInUse not implemented.');
155 callback();
156 }
157
158 /**
159 * Sets multiple items.
160 *
161 * @param {object} items An object which gives key/value pairs to update
162 * storage with. Other key/value pairs in storage will not be affected.
163 * @param {function=} opt_callback Callback on success or failure.
164 */
165 set(items, opt_callback) {
166 var keys = Object.keys(items);
167 try {
168 // We need to trigger an event containing all the old values and new value s.
169 // To do that, we first need the old values.
170 this.get(keys, function (oldItems) {
171 // Now that we have the old values, we can set the new values.
172 Promise.all(keys.map(key => localforage.setItem(key, items[key])))
173 // Then setup the input and trigger the event.
174 .then(function() {
175 var changes = {};
176 for (var key of keys) {
177 changes[key] = new chrome.storage.StorageChange(
178 oldItems[key], items[key]);
179 }
180 triggerOnChanged(changes);
181 if (opt_callback)
182 opt_callback();
183 });
184 });
185 } catch (e) {
186 chrome.caterpillar.setError('Error setting values: ' + (e.message || e));
187 if (opt_callback)
188 opt_callback();
189 }
190 }
191
192 /**
193 * Removes one or more items from storage.
194 *
195 * @param {string || string[]} keys A single key or a list of keys to
196 * remove.
197 * @param {function=} opt_callback Callback on success or failure.
198 */
199 remove(keys, opt_callback) {
200 var handleError = function(err) {
201 chrome.caterpillar.setError('Error removing keys: ' + err);
202 if (opt_callback)
203 opt_callback();
204 };
205 try {
206 this.get(keys, function(items) {
207 if (typeof keys === 'string')
208 keys = [keys];
209
210 Promise.all(keys.map(key => localforage.removeItem(key)))
211 .then(function() {
212 var changes = {};
213 for (var key of keys) {
214 changes[key] = new chrome.storage.StorageChange(
215 items[key], null);
216 }
217 triggerOnChanged(changes);
218 if (opt_callback)
219 opt_callback();
220 })
221 });
222 } catch (e) {
223 handleError(e.message || e);
224 }
225 }
226
227 /**
228 * Removes all items from storage.
229 *
230 * @param {function=} opt_callback Callback on success or failure.
231 */
232 clear(opt_callback) {
233 try {
234 this.get(function(items) {
235 var changes = {};
236 for (var key in items) {
237 changes[key] = new chrome.storage.StorageChange(items[key], null);
238 }
239 localforage.clear().then(function() {
240 triggerOnChanged(changes);
241 if (opt_callback)
242 opt_callback();
243 });
244 });
245 } catch (e) {
246 chrome.caterpillar.setError('Error clearing values: ' + (e.message || e));
247 if (opt_callback)
248 opt_callback();
249 }
250 }
133 }; 251 };
134 252
135 /** 253 /**
136 * Items in the local storage area are stored locally. 254 * Items in the local storage area are stored locally.
137 */ 255 */
138 chrome.storage.local = new chrome.storage.StorageArea(); 256 chrome.storage.local = new chrome.storage.StorageArea();
139 257
140 /** 258 /**
141 * Items in the sync storage area would be synced using Chrome Sync; in this 259 * Items in the sync storage area would be synced using Chrome Sync; in this
142 * polyfill we just use local storage. 260 * polyfill we just use local storage.
143 */ 261 */
144 chrome.storage.sync = chrome.storage.local; 262 chrome.storage.sync = chrome.storage.local;
145 263
146 /** 264 /**
147 * Items in the managed storage area are read-only usually; in this polyfill 265 * Items in the managed storage area are read-only usually; in this polyfill
148 * managed is the same as local storage. 266 * managed is the same as local storage.
149 */ 267 */
150 chrome.storage.managed = chrome.storage.local; 268 chrome.storage.managed = chrome.storage.local;
151 269
152 /** 270 /**
153 * Namespace. 271 * Namespace.
154 */ 272 */
155 chrome.storage.onChanged = {}; 273 chrome.storage.onChanged = {};
156 274
157 /** 275 /**
158 * Adds an event listener for the onChanged event. 276 * Adds an event listener for the onChanged event.
159 * 277 *
160 * @param {function} callback Function taking a changes object of key/value 278 * @param {function} callback Function taking an object mapping changed keys to
161 * pairs. 279 * StorageChanges and an area name (though the latter will always be null).
162 */ 280 */
163 chrome.storage.onChanged.addListener = function(callback) { 281 chrome.storage.onChanged.addListener = function(callback) {
164 self.addEventListener('chrome.storage.onChanged', e => { 282 var listener = function(e) {
165 callback(e.detail); 283 callback(e.detail, null);
166 }); 284 };
285
286 self.addEventListener('chrome.storage.onChanged', listener);
287 onChangedListeners.push(listener);
288 };
289
290 /**
291 * Resets onChanged event listeners. Used for testing.
292 */
293 chrome.caterpillar.storage.resetOnChangedListenersForTests = function() {
294 for (var listener of onChangedListeners) {
295 self.removeEventListener('chrome.storage.onChanged', listener);
296 }
297
298 onChangedListeners.length = 0;
167 }; 299 };
168 300
169 }).call(this); 301 }).call(this);
OLDNEW
« no previous file with comments | « src/js/polyfills/storage.manifest.json ('k') | tests/js/polyfills/storage.polyfill.test.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698