Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* Copyright 2015 The Chromium Authors. All rights reserved. | 1 /* Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 * Use of this source code is governed by a BSD-style license that can be | 2 * Use of this source code is governed by a BSD-style license that can be |
| 3 * found in the LICENSE file. */ | 3 * found in the LICENSE file. */ |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview | 6 * @fileoverview |
| 7 * 'cr-settings-prefs' models Chrome settings and preferences, listening for | 7 * 'cr-settings-prefs' exposes a sinlgeton model of Chrome settings and |
| 8 * changes to Chrome prefs whitelisted in chrome.settingsPrivate. | 8 * preferences, which listens to changes to Chrome prefs whitelisted in |
| 9 * When changing prefs in this element's 'prefs' property via the UI, this | 9 * chrome.settingsPrivate. When changing prefs in this element's 'prefs' |
| 10 * element tries to set those preferences in Chrome. Whether or not the calls to | 10 * property via the UI, the model tries to set those preferences in Chrome. |
| 11 * settingsPrivate.setPref succeed, 'prefs' is eventually consistent with the | 11 * Whether or not the calls to settingsPrivate.setPref succeed, 'prefs' |
| 12 * Chrome pref store. | 12 * is eventually consistent with the Chrome pref store. |
| 13 * | 13 * |
| 14 * Example: | 14 * Example: |
| 15 * | 15 * |
| 16 * <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs> | 16 * <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs> |
| 17 * <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page> | 17 * <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page> |
| 18 * | 18 * |
| 19 * @group Chrome Settings Elements | 19 * @group Chrome Settings Elements |
| 20 * @element cr-settings-prefs | 20 * @element cr-settings-prefs |
| 21 */ | 21 */ |
| 22 | 22 |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 properties: { | 129 properties: { |
| 130 /** | 130 /** |
| 131 * Object containing all preferences, for use by Polymer controls. | 131 * Object containing all preferences, for use by Polymer controls. |
| 132 */ | 132 */ |
| 133 prefs: { | 133 prefs: { |
| 134 type: Object, | 134 type: Object, |
| 135 notify: true, | 135 notify: true, |
| 136 }, | 136 }, |
| 137 | 137 |
| 138 /** | 138 /** |
| 139 * Shared private state. | |
| 140 * @type {!Element} | |
| 141 */ | |
| 142 prefsPrivate_: { | |
| 143 type: Object, | |
| 144 value: document.createElement('cr-settings-prefs-private'), | |
| 145 }, | |
| 146 }, | |
| 147 | |
| 148 observers: [ | |
| 149 'prefsChanged_(prefs.*)', | |
| 150 ], | |
| 151 | |
| 152 /** @override */ | |
| 153 ready: function() { | |
| 154 this.prefsPrivate_.initialize(); | |
| 155 this.startListening_(); | |
| 156 }, | |
| 157 | |
| 158 /** | |
| 159 * Binds this.prefs to the cr-settings-prefs-private's shared prefs once | |
| 160 * preferences are initialized. | |
| 161 * @private | |
| 162 */ | |
| 163 startListening_: function() { | |
| 164 CrSettingsPrefs.initialized.then(function() { | |
| 165 // Ignore changes to prevent prefsChanged_ from notifying prefsPrivate_. | |
| 166 this.runWhileIgnoringChanges_(function() { | |
| 167 this.prefs = this.prefsPrivate_.prefs; | |
| 168 this.stopListening_(); | |
| 169 this.listen( | |
| 170 this.prefsPrivate_, 'prefs-changed', 'prefsPrivateChanged_'); | |
| 171 }); | |
| 172 }.bind(this)); | |
| 173 }, | |
| 174 | |
| 175 /** | |
| 176 * Stops listening for changes to cr-settings-prefs-private's shared prefs. | |
| 177 * @private | |
| 178 */ | |
| 179 stopListening_: function() { | |
| 180 this.unlisten( | |
| 181 this.prefsPrivate_, 'prefs-changed', 'prefsPrivateChanged_'); | |
| 182 }, | |
| 183 | |
| 184 /** | |
| 185 * Handles changes reported by prefsPrivate_. | |
| 186 * @private | |
| 187 */ | |
| 188 prefsPrivateChanged_: function(e) { | |
| 189 // Ignore changes because we've defeated Polymer's dirty-checking. | |
| 190 this.runWhileIgnoringChanges_(function() { | |
| 191 // Forward notification to host. | |
| 192 this.fire(e.type, e.detail, {bubbles: false}); | |
| 193 }); | |
| 194 }, | |
| 195 | |
| 196 /** | |
| 197 * Forwards changes to this.prefs to cr-settings-prefs-private. | |
| 198 * @private | |
| 199 */ | |
| 200 prefsChanged_: function(info) { | |
| 201 // Ignore prefsPrivate_ of changes that came from it so we don't | |
| 202 // re-processing changes made in other instances of this element. | |
| 203 if (!this.ignoreChanges_) | |
| 204 this.prefsPrivate_.fire('prefs-changed', info, {bubbles: false}); | |
| 205 }, | |
| 206 | |
| 207 /** | |
| 208 * Sets ignoreChanged_ before calling the function to suppress change | |
| 209 * events that are manually handled. | |
| 210 * @param {!function()} fn | |
| 211 * @private | |
| 212 */ | |
| 213 runWhileIgnoringChanges_: function(fn) { | |
|
Dan Beam
2015/09/22 06:12:31
assert(!this.ignoreChanges_, 'update the code to s
michaelpg
2015/09/22 21:38:36
Done.
| |
| 214 this.ignoreChanges_ = true; | |
| 215 fn.call(this); | |
| 216 // We can unset ignoreChanges_ now because change notifications | |
| 217 // are synchronous. | |
| 218 this.ignoreChanges_ = false; | |
| 219 }, | |
| 220 | |
| 221 /** | |
| 222 * Uninitializes this element to remove it from tests. Also resets | |
| 223 * cr-settings-prefs-private, allowing newly created elements to | |
| 224 * re-initialize it. | |
| 225 */ | |
| 226 resetForTesting: function() { | |
| 227 this.stopListening_(); | |
| 228 this.prefsPrivate_.resetForTesting(); | |
| 229 }, | |
| 230 }); | |
| 231 | |
| 232 /** | |
| 233 * Privately used element that contains, listens to and updates the shared | |
| 234 * prefs state. | |
| 235 */ | |
| 236 Polymer({ | |
| 237 is: 'cr-settings-prefs-private', | |
| 238 | |
| 239 properties: { | |
| 240 /** | |
| 241 * Object containing all preferences, for use by Polymer controls. | |
| 242 * @type {Object|undefined} | |
| 243 */ | |
| 244 prefs: { | |
| 245 type: Object, | |
| 246 notify: true, | |
| 247 }, | |
| 248 | |
| 249 /** | |
| 139 * Map of pref keys to values representing the state of the Chrome | 250 * Map of pref keys to values representing the state of the Chrome |
| 140 * pref store as of the last update from the API. | 251 * pref store as of the last update from the API. |
| 141 * @type {Object<*>} | 252 * @type {Object<*>} |
| 142 * @private | 253 * @private |
| 143 */ | 254 */ |
| 144 lastPrefValues_: { | 255 lastPrefValues_: { |
| 145 type: Object, | 256 type: Object, |
| 146 value: function() { return {}; }, | 257 value: function() { return {}; }, |
| 147 }, | 258 }, |
| 148 }, | 259 }, |
| 149 | 260 |
| 150 observers: [ | 261 // Listen for the manually fired prefs-changed event. |
| 151 'prefsChanged_(prefs.*)', | 262 listeners: { |
| 152 ], | 263 'prefs-changed': 'prefsChanged_', |
| 264 }, | |
| 153 | 265 |
| 154 settingsApi_: chrome.settingsPrivate, | 266 settingsApi_: chrome.settingsPrivate, |
| 155 | 267 |
| 156 /** @override */ | 268 initialize: function() { |
| 157 ready: function() { | 269 // Only initialize once (or after resetForTesting() is called). |
| 270 if (this.initialized_) | |
| 271 return; | |
| 272 this.initialized_ = true; | |
| 273 | |
| 158 // Set window.mockApi to pass a custom settings API, i.e. for tests. | 274 // Set window.mockApi to pass a custom settings API, i.e. for tests. |
| 159 // TODO(michaelpg): don't use a global. | 275 // TODO(michaelpg): don't use a global. |
| 160 if (window.mockApi) | 276 if (window.mockApi) |
| 161 this.settingsApi_ = window.mockApi; | 277 this.settingsApi_ = window.mockApi; |
| 162 CrSettingsPrefs.isInitialized = false; | |
| 163 | 278 |
| 164 this.settingsApi_.onPrefsChanged.addListener( | 279 this.settingsApi_.onPrefsChanged.addListener( |
| 165 this.onSettingsPrivatePrefsChanged_.bind(this)); | 280 this.onSettingsPrivatePrefsChanged_.bind(this)); |
| 166 this.settingsApi_.getAllPrefs( | 281 this.settingsApi_.getAllPrefs( |
| 167 this.onSettingsPrivatePrefsFetched_.bind(this)); | 282 this.onSettingsPrivatePrefsFetched_.bind(this)); |
| 168 }, | 283 }, |
| 169 | 284 |
| 170 /** | 285 /** |
| 171 * Polymer callback for changes to this.prefs. | 286 * Polymer callback for changes to this.prefs. |
| 172 * @param {!{path: string, value: *}} change | 287 * @param {!CustomEvent} e |
| 288 * @param {!{path: string}} change | |
| 173 * @private | 289 * @private |
| 174 */ | 290 */ |
| 175 prefsChanged_: function(change) { | 291 prefsChanged_: function(e, change) { |
| 176 if (!CrSettingsPrefs.isInitialized) | 292 if (!CrSettingsPrefs.isInitialized) |
| 177 return; | 293 return; |
| 178 | 294 |
| 179 var key = this.getPrefKeyFromPath_(change.path); | 295 var key = this.getPrefKeyFromPath_(change.path); |
| 180 var prefStoreValue = this.lastPrefValues_[key]; | 296 var prefStoreValue = this.lastPrefValues_[key]; |
| 181 | 297 |
| 182 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */( | 298 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */( |
| 183 this.get(key, this.prefs)); | 299 this.get(key, this.prefs)); |
| 184 | 300 |
| 185 // If settingsPrivate already has this value, do nothing. (Otherwise, | 301 // If settingsPrivate already has this value, do nothing. (Otherwise, |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 206 this.updatePrefs_(prefs); | 322 this.updatePrefs_(prefs); |
| 207 }, | 323 }, |
| 208 | 324 |
| 209 /** | 325 /** |
| 210 * Called when prefs are fetched from settingsPrivate. | 326 * Called when prefs are fetched from settingsPrivate. |
| 211 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs | 327 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs |
| 212 * @private | 328 * @private |
| 213 */ | 329 */ |
| 214 onSettingsPrivatePrefsFetched_: function(prefs) { | 330 onSettingsPrivatePrefsFetched_: function(prefs) { |
| 215 this.updatePrefs_(prefs); | 331 this.updatePrefs_(prefs); |
| 216 | 332 // Prefs are now initialized. |
|
Dan Beam
2015/09/22 06:12:31
nit: useless comment
michaelpg
2015/09/22 21:38:37
Done.
| |
| 217 CrSettingsPrefs.isInitialized = true; | 333 CrSettingsPrefs.setInitialized(); |
| 218 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED)); | |
| 219 }, | 334 }, |
| 220 | 335 |
| 221 /** | 336 /** |
| 222 * Checks the result of calling settingsPrivate.setPref. | 337 * Checks the result of calling settingsPrivate.setPref. |
| 223 * @param {string} key The key used in the call to setPref. | 338 * @param {string} key The key used in the call to setPref. |
| 224 * @param {boolean} success True if setting the pref succeeded. | 339 * @param {boolean} success True if setting the pref succeeded. |
| 225 * @private | 340 * @private |
| 226 */ | 341 */ |
| 227 setPrefCallback_: function(key, success) { | 342 setPrefCallback_: function(key, success) { |
| 228 if (success) | 343 if (success) |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 241 * @private | 356 * @private |
| 242 */ | 357 */ |
| 243 updatePrefs_: function(newPrefs) { | 358 updatePrefs_: function(newPrefs) { |
| 244 // Use the existing prefs object or create it. | 359 // Use the existing prefs object or create it. |
| 245 var prefs = this.prefs || {}; | 360 var prefs = this.prefs || {}; |
| 246 newPrefs.forEach(function(newPrefObj) { | 361 newPrefs.forEach(function(newPrefObj) { |
| 247 // Use the PrefObject from settingsPrivate to create a copy in | 362 // Use the PrefObject from settingsPrivate to create a copy in |
| 248 // lastPrefValues_ at the pref's key. | 363 // lastPrefValues_ at the pref's key. |
| 249 this.lastPrefValues_[newPrefObj.key] = deepCopy(newPrefObj.value); | 364 this.lastPrefValues_[newPrefObj.key] = deepCopy(newPrefObj.value); |
| 250 | 365 |
| 251 // Add the pref to |prefs|. | 366 if (!deepEqual(this.get(newPrefObj.key, prefs), newPrefObj)) { |
| 252 cr.exportPath(newPrefObj.key, newPrefObj, prefs); | 367 // Add the pref to |prefs|. |
| 253 // If this.prefs already exists, notify listeners of the change. | 368 cr.exportPath(newPrefObj.key, newPrefObj, prefs); |
| 254 if (prefs == this.prefs) | 369 // If this.prefs already exists, notify listeners of the change. |
| 255 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj); | 370 if (prefs == this.prefs) |
| 371 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj); | |
| 372 } | |
| 256 }, this); | 373 }, this); |
| 257 if (!this.prefs) | 374 if (!this.prefs) |
| 258 this.prefs = prefs; | 375 this.prefs = prefs; |
| 259 }, | 376 }, |
| 260 | 377 |
| 261 /** | 378 /** |
| 262 * Given a 'property-changed' path, returns the key of the preference the | 379 * Given a 'property-changed' path, returns the key of the preference the |
| 263 * path refers to. E.g., if the path of the changed property is | 380 * path refers to. E.g., if the path of the changed property is |
| 264 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is | 381 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is |
| 265 * 'search.suggest_enabled'. | 382 * 'search.suggest_enabled'. |
| 266 * @param {string} path | 383 * @param {string} path |
| 267 * @return {string} | 384 * @return {string} |
| 268 * @private | 385 * @private |
| 269 */ | 386 */ |
| 270 getPrefKeyFromPath_: function(path) { | 387 getPrefKeyFromPath_: function(path) { |
| 271 // Skip the first token, which refers to the member variable (this.prefs). | 388 // Skip the first token, which refers to the member variable (this.prefs). |
| 272 var parts = path.split('.'); | 389 var parts = path.split('.'); |
| 273 assert(parts.shift() == 'prefs'); | 390 assert(parts.shift() == 'prefs'); |
| 274 | 391 |
| 275 for (let i = 1; i <= parts.length; i++) { | 392 for (let i = 1; i <= parts.length; i++) { |
| 276 let key = parts.slice(0, i).join('.'); | 393 let key = parts.slice(0, i).join('.'); |
| 277 // The lastPrefValues_ keys match the pref keys. | 394 // The lastPrefValues_ keys match the pref keys. |
| 278 if (this.lastPrefValues_.hasOwnProperty(key)) | 395 if (this.lastPrefValues_.hasOwnProperty(key)) |
| 279 return key; | 396 return key; |
| 280 } | 397 } |
| 281 return ''; | 398 return ''; |
| 282 }, | 399 }, |
| 400 | |
| 401 /** | |
| 402 * Resets the element so it can be re-initialized with a new prefs state. | |
| 403 */ | |
| 404 resetForTesting: function() { | |
| 405 this.prefs = undefined; | |
| 406 this.lastPrefValues_ = {}; | |
| 407 this.initialized_ = false; | |
| 408 }, | |
| 283 }); | 409 }); |
| 284 })(); | 410 })(); |
| OLD | NEW |