Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 123 items[keys[i]] = opt_keys[keys[i]]; | 130 items[keys[i]] = opt_keys[keys[i]]; |
| 124 } else { | 131 } else { |
| 125 items[keys[i]] = values[i]; | 132 items[keys[i]] = values[i]; |
| 126 } | 133 } |
| 127 } | 134 } |
| 128 callback(items); | 135 callback(items); |
| 129 }) | 136 }) |
| 130 .catch(handleError); | 137 .catch(handleError); |
| 131 } | 138 } |
| 132 } | 139 } |
| 140 | |
| 141 /** | |
| 142 * Gets the amount of space (in bytes) being used by one or more items. | |
| 143 * | |
| 144 * Not implemented in this polyfill. | |
| 145 * | |
| 146 * @param {string= | string[]= | object=} opt_keys A single key to get, | |
| 147 * list of keys to get, or a dictionary specifying default values. Empty | |
|
raymes
2016/01/18 00:25:20
nit: 4 space indent (here and below)
Matthew Alger
2016/01/18 00:37:37
Done.
| |
| 148 * lists or objects return 0. Pass null to get total usage. | |
| 149 * @param {function} callback Callback with bytes in use, or failure. | |
| 150 */ | |
| 151 getBytesInUse(opt_keys, callback) { | |
| 152 // Juggle arguments. | |
| 153 if (callback === undefined) { | |
| 154 callback = opt_keys; | |
| 155 opt_keys = null; | |
| 156 } | |
| 157 // IndexedDB doesn't support this, so neither does localforage. | |
| 158 chrome.caterpillar.setError('getBytesInUse not implemented.'); | |
| 159 callback(); | |
| 160 } | |
| 161 | |
| 162 /** | |
| 163 * Sets multiple items. | |
| 164 * | |
| 165 * @param {object} items An object which gives key/value pairs to update | |
| 166 * storage with. Other key/value pairs in storage will not be affected. | |
| 167 * @param {function=} opt_callback Callback on success or failure. | |
| 168 */ | |
| 169 set(items, opt_callback) { | |
| 170 var keys = Object.keys(items); | |
| 171 try { | |
| 172 // We need to trigger an event containing all the old values and new value s. | |
| 173 // To do that, we first need the old values. | |
| 174 this.get(keys, function (oldItems) { | |
| 175 // Now that we have the old values, we can set the new values. | |
| 176 Promise.all(keys.map(key => localforage.setItem(key, items[key]))) | |
| 177 // Then setup the input and trigger the event. | |
| 178 .then(function() { | |
| 179 var changes = {}; | |
| 180 for (var key of keys) { | |
| 181 changes[key] = new chrome.storage.StorageChange( | |
| 182 oldItems[key], items[key]); | |
| 183 } | |
| 184 triggerOnChanged(changes); | |
| 185 if (opt_callback !== undefined) | |
| 186 opt_callback(); | |
| 187 }); | |
| 188 }); | |
| 189 } catch (e) { | |
| 190 chrome.caterpillar.setError('Error setting values: ' + (e.message || e)); | |
| 191 opt_callback(); | |
|
raymes
2016/01/18 00:25:20
what if opt_callback is undefined?
Matthew Alger
2016/01/18 00:37:37
Done.
| |
| 192 } | |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Removes one or more items from storage. | |
| 197 * | |
| 198 * @param {string || string[]} keys A single key or a list of keys to | |
| 199 * remove. | |
| 200 * @param {function=} opt_callback Callback on success or failure. | |
| 201 */ | |
| 202 remove(keys, opt_callback) { | |
| 203 var handleError = function(err) { | |
| 204 chrome.caterpillar.setError('Error removing keys: ' + err); | |
| 205 opt_callback(); | |
|
raymes
2016/01/18 00:25:20
what if opt_callback is undefined?
Matthew Alger
2016/01/18 00:37:37
Done.
| |
| 206 }; | |
| 207 try { | |
| 208 this.get(keys, function(items) { | |
| 209 if (Array.isArray(keys)) { | |
|
raymes
2016/01/18 00:25:20
If keys is a string, you could turn it into an arr
Matthew Alger
2016/01/18 00:37:37
Oh, good idea! Done, and done in get too.
| |
| 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 opt_callback(); | |
|
raymes
2016/01/18 00:25:20
what if opt_callback is undefined?
Matthew Alger
2016/01/18 00:37:37
Done.
| |
| 219 }) | |
| 220 } else { | |
| 221 localforage.removeItem(keys).then(function() { | |
| 222 var changes = {}; | |
| 223 changes[keys] = new chrome.storage.StorageChange( | |
| 224 items[keys], null); | |
| 225 triggerOnChanged(changes); | |
| 226 opt_callback(); | |
| 227 }); | |
| 228 } | |
| 229 }); | |
| 230 } catch (e) { | |
| 231 handleError(e.message || e); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * Removes all items from storage. | |
| 237 * | |
| 238 * @param {function=} opt_callback Callback on success or failure. | |
| 239 */ | |
| 240 clear(opt_callback) { | |
| 241 try { | |
| 242 this.get(function(items) { | |
| 243 var changes = {}; | |
| 244 for (var key in items) { | |
| 245 changes[key] = new chrome.storage.StorageChange(items[key], null); | |
| 246 } | |
| 247 triggerOnChanged(changes); | |
| 248 localforage.clear(opt_callback); | |
|
raymes
2016/01/18 00:25:20
I think it's probably better to clear things befor
Matthew Alger
2016/01/18 00:37:37
Do you mean it's better to clear things before the
| |
| 249 }); | |
| 250 } catch (e) { | |
| 251 chrome.caterpillar.setError('Error clearing values: ' + (e.message || e)); | |
| 252 if (opt_callback) | |
|
raymes
2016/01/18 00:25:20
This is inconsistent with other places where you h
Matthew Alger
2016/01/18 00:37:37
Changed everything to just check opt_callback's tr
| |
| 253 opt_callback(); | |
| 254 } | |
| 255 } | |
| 133 }; | 256 }; |
| 134 | 257 |
| 135 /** | 258 /** |
| 136 * Items in the local storage area are stored locally. | 259 * Items in the local storage area are stored locally. |
| 137 */ | 260 */ |
| 138 chrome.storage.local = new chrome.storage.StorageArea(); | 261 chrome.storage.local = new chrome.storage.StorageArea(); |
| 139 | 262 |
| 140 /** | 263 /** |
| 141 * Items in the sync storage area would be synced using Chrome Sync; in this | 264 * Items in the sync storage area would be synced using Chrome Sync; in this |
| 142 * polyfill we just use local storage. | 265 * polyfill we just use local storage. |
| 143 */ | 266 */ |
| 144 chrome.storage.sync = chrome.storage.local; | 267 chrome.storage.sync = chrome.storage.local; |
| 145 | 268 |
| 146 /** | 269 /** |
| 147 * Items in the managed storage area are read-only usually; in this polyfill | 270 * Items in the managed storage area are read-only usually; in this polyfill |
| 148 * managed is the same as local storage. | 271 * managed is the same as local storage. |
| 149 */ | 272 */ |
| 150 chrome.storage.managed = chrome.storage.local; | 273 chrome.storage.managed = chrome.storage.local; |
| 151 | 274 |
| 152 /** | 275 /** |
| 153 * Namespace. | 276 * Namespace. |
| 154 */ | 277 */ |
| 155 chrome.storage.onChanged = {}; | 278 chrome.storage.onChanged = {}; |
| 156 | 279 |
| 157 /** | 280 /** |
| 158 * Adds an event listener for the onChanged event. | 281 * Adds an event listener for the onChanged event. |
| 159 * | 282 * |
| 160 * @param {function} callback Function taking a changes object of key/value | 283 * @param {function} callback Function taking an object mapping changed keys to |
| 161 * pairs. | 284 * StorageChanges and an area name (though the latter will always be null). |
| 162 */ | 285 */ |
| 163 chrome.storage.onChanged.addListener = function(callback) { | 286 chrome.storage.onChanged.addListener = function(callback) { |
| 164 self.addEventListener('chrome.storage.onChanged', e => { | 287 var listener = function(e) { |
| 165 callback(e.detail); | 288 callback(e.detail, null); |
| 166 }); | 289 }; |
| 290 | |
| 291 self.addEventListener('chrome.storage.onChanged', listener); | |
| 292 onChangedListeners.push(listener); | |
| 293 }; | |
| 294 | |
| 295 /** | |
| 296 * Resets onChanged event listeners. Used for testing. | |
| 297 */ | |
| 298 chrome.caterpillar.storage.resetOnChangedListenersForTests = function() { | |
| 299 for (var listener of onChangedListeners) { | |
| 300 self.removeEventListener('chrome.storage.onChanged', listener); | |
| 301 } | |
| 302 | |
| 303 onChangedListeners.length = 0; | |
| 167 }; | 304 }; |
| 168 | 305 |
| 169 }).call(this); | 306 }).call(this); |
| OLD | NEW |