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' models Chrome settings and preferences, listening for |
| 8 * changes to Chrome prefs whitelisted in chrome.settingsPrivate. | 8 * changes to Chrome prefs whitelisted in chrome.settingsPrivate. |
| 9 * When changing prefs in this element's 'prefs' property via the UI, this | 9 * When changing prefs in this element's 'prefs' property via the UI, this |
| 10 * element tries to set those preferences in Chrome. Whether or not the calls to | 10 * element tries to set those preferences in Chrome. Whether or not the calls to |
| 11 * settingsPrivate.setPref succeed, 'prefs' is eventually consistent with the | 11 * settingsPrivate.setPref succeed, 'prefs' is eventually consistent with the |
| 12 * Chrome pref store. | 12 * 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 |
| 23 /** | |
| 24 * Pref state object. Copies values of PrefObjects received from | |
| 25 * settingsPrivate to determine when pref values have changed. | |
| 26 * This prototype works for primitive types, but more complex types should | |
| 27 * override these functions. | |
| 28 * @constructor | |
| 29 * @param {!chrome.settingsPrivate.PrefObject} prefObj | |
| 30 */ | |
| 31 function PrefWrapper(prefObj) { | |
| 32 this.key_ = prefObj.key; | |
| 33 this.value_ = prefObj.value; | |
| 34 } | |
| 35 | |
| 36 /** | |
| 37 * Checks if other's value equals this.value_. Both prefs wrapped by the | |
| 38 * PrefWrappers should have the same keys. | |
| 39 * @param {PrefWrapper} other | |
| 40 * @return {boolean} | |
| 41 */ | |
| 42 PrefWrapper.prototype.equals = function(other) { | |
| 43 assert(this.key_ == other.key_); | |
| 44 return this.value_ == other.value_; | |
| 45 }; | |
| 46 | |
| 47 /** | |
| 48 * @constructor | |
| 49 * @extends {PrefWrapper} | |
| 50 * @param {!chrome.settingsPrivate.PrefObject} prefObj | |
| 51 */ | |
| 52 function ListPrefWrapper(prefObj) { | |
| 53 // Copy the array so changes to prefObj aren't reflected in this.value_. | |
| 54 // TODO(michaelpg): Do a deep copy to support nested lists and objects. | |
| 55 this.value_ = prefObj.value.slice(); | |
| 56 } | |
| 57 | |
| 58 ListPrefWrapper.prototype = { | |
| 59 __proto__: PrefWrapper.prototype, | |
| 60 | |
| 61 /** | |
| 62 * Tests whether two ListPrefWrapper values contain the same list items. | |
| 63 * @override | |
| 64 */ | |
| 65 equals: function(other) { | |
| 66 'use strict'; | |
| 67 assert(this.key_ == other.key_); | |
| 68 if (this.value_.length != other.value_.length) | |
| 69 return false; | |
| 70 for (let i = 0; i < this.value_.length; i++) { | |
| 71 if (this.value_[i] != other.value_[i]) | |
| 72 return false; | |
| 73 } | |
| 74 return true; | |
| 75 }, | |
| 76 }; | |
| 77 | |
| 78 (function() { | 23 (function() { |
| 79 'use strict'; | 24 'use strict'; |
| 80 | 25 |
| 26 /** | |
| 27 * Checks whether two values are recursively equal. Only compares serializable | |
| 28 * data (primitives, serializable arrays and serializable objects). | |
| 29 * @param {*} val1 Value to compare. | |
| 30 * @param {*} val2 Value to compare with val1. | |
| 31 * @return {boolean} True if the values are recursively equal. | |
| 32 */ | |
| 33 function deepEqual(val1, val2) { | |
| 34 if (val1 === val2) | |
| 35 return true; | |
| 36 | |
| 37 if (val1 == null || val2 == null) | |
|
Dan Beam
2015/09/18 01:54:16
you should probably use === just to make sure
michaelpg
2015/09/18 02:03:22
actually this line is superfluous because null and
Dan Beam
2015/09/18 02:05:18
super
| |
| 38 return false; | |
| 39 | |
| 40 if (Array.isArray(val1) || Array.isArray(val2)) { | |
| 41 if (!Array.isArray(val1) || !Array.isArray(val2)) | |
| 42 return false; | |
| 43 return arraysEqual(/** @type {!Array} */(val1), | |
| 44 /** @type {!Array} */(val2)); | |
| 45 } | |
| 46 | |
| 47 if (val1 instanceof Object && val2 instanceof Object) | |
| 48 return objectsEqual(val1, val2); | |
| 49 | |
| 50 return false; | |
| 51 } | |
| 52 | |
| 53 /** | |
| 54 * @param {!Array} arr1 | |
| 55 * @param {!Array} arr2 | |
| 56 * @return {boolean} True if the arrays are recursively equal. | |
| 57 */ | |
| 58 function arraysEqual(arr1, arr2) { | |
| 59 if (arr1.length != arr2.length) | |
| 60 return false; | |
| 61 | |
| 62 for (var i = 0; i < arr1.length; i++) { | |
| 63 if (!deepEqual(arr1[i], arr2[i])) | |
| 64 return false; | |
| 65 } | |
| 66 | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * @param {!Object} obj1 | |
| 72 * @param {!Object} obj2 | |
| 73 * @return {boolean} True if the objects are recursively equal. | |
| 74 */ | |
| 75 function objectsEqual(obj1, obj2) { | |
| 76 var keys1 = Object.keys(obj1); | |
| 77 var keys2 = Object.keys(obj2); | |
| 78 if (keys1.length != keys2.length) | |
| 79 return false; | |
| 80 | |
| 81 for (var i = 0; i < keys1.length; i++) { | |
| 82 var key = keys1[i]; | |
| 83 if (!deepEqual(obj1[key], obj2[key])) | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 /** | |
| 91 * Returns a recursive copy of the value. | |
| 92 * @param {*} val Value to copy. Should be a primitive or only contain | |
| 93 * serializable data (primitives, serializable arrays and | |
| 94 * serializable objects). | |
| 95 * @return {*} A deep copy of the value. | |
| 96 */ | |
| 97 function deepCopy(val) { | |
| 98 if (!(val instanceof Object)) | |
| 99 return val; | |
| 100 return Array.isArray(val) ? deepCopyArray(/** @type {!Array} */(val)) : | |
| 101 deepCopyObject(val); | |
| 102 }; | |
| 103 | |
| 104 /** | |
| 105 * @param {!Array} arr | |
| 106 * @return {!Array} Deep copy of the array. | |
| 107 */ | |
| 108 function deepCopyArray(arr) { | |
| 109 var copy = []; | |
| 110 for (var i = 0; i < arr.length; i++) | |
| 111 copy.push(deepCopy(arr[i])); | |
| 112 return copy; | |
| 113 } | |
| 114 | |
| 115 /** | |
| 116 * @param {!Object} obj | |
| 117 * @return {!Object} Deep copy of the object. | |
| 118 */ | |
| 119 function deepCopyObject(obj) { | |
| 120 var copy = {}; | |
| 121 var keys = Object.keys(obj); | |
| 122 for (var i = 0; i < keys.length; i++) { | |
| 123 var key = keys[i]; | |
| 124 copy[key] = deepCopy(obj[key]); | |
| 125 } | |
| 126 return copy; | |
| 127 } | |
| 128 | |
| 81 Polymer({ | 129 Polymer({ |
| 82 is: 'cr-settings-prefs', | 130 is: 'cr-settings-prefs', |
| 83 | 131 |
| 84 properties: { | 132 properties: { |
| 85 /** | 133 /** |
| 86 * Object containing all preferences, for use by Polymer controls. | 134 * Object containing all preferences, for use by Polymer controls. |
| 87 */ | 135 */ |
| 88 prefs: { | 136 prefs: { |
| 89 type: Object, | 137 type: Object, |
| 90 notify: true, | 138 notify: true, |
| 91 }, | 139 }, |
| 92 | 140 |
| 93 /** | 141 /** |
| 94 * Map of pref keys to PrefWrapper objects representing the state of the | 142 * Map of pref keys to values representing the state of the Chrome |
| 95 * Chrome pref store. | 143 * pref store as of the last update from the API. |
| 96 * @type {Object<PrefWrapper>} | 144 * @type {Object<*>} |
| 97 * @private | 145 * @private |
| 98 */ | 146 */ |
| 99 prefWrappers_: { | 147 lastPrefValues_: { |
| 100 type: Object, | 148 type: Object, |
| 101 value: function() { return {}; }, | 149 value: function() { return {}; }, |
| 102 }, | 150 }, |
| 103 }, | 151 }, |
| 104 | 152 |
| 105 observers: [ | 153 observers: [ |
| 106 'prefsChanged_(prefs.*)', | 154 'prefsChanged_(prefs.*)', |
| 107 ], | 155 ], |
| 108 | 156 |
| 109 settingsApi_: chrome.settingsPrivate, | 157 settingsApi_: chrome.settingsPrivate, |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 125 /** | 173 /** |
| 126 * Polymer callback for changes to this.prefs. | 174 * Polymer callback for changes to this.prefs. |
| 127 * @param {!{path: string, value: *}} change | 175 * @param {!{path: string, value: *}} change |
| 128 * @private | 176 * @private |
| 129 */ | 177 */ |
| 130 prefsChanged_: function(change) { | 178 prefsChanged_: function(change) { |
| 131 if (!CrSettingsPrefs.isInitialized) | 179 if (!CrSettingsPrefs.isInitialized) |
| 132 return; | 180 return; |
| 133 | 181 |
| 134 var key = this.getPrefKeyFromPath_(change.path); | 182 var key = this.getPrefKeyFromPath_(change.path); |
| 135 var prefWrapper = this.prefWrappers_[key]; | 183 var prefStoreValue = this.lastPrefValues_[key]; |
| 136 if (!prefWrapper) | |
| 137 return; | |
| 138 | 184 |
| 139 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */( | 185 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */( |
| 140 this.get(key, this.prefs)); | 186 this.get(key, this.prefs)); |
| 141 | 187 |
| 142 // If settingsPrivate already has this value, do nothing. (Otherwise, | 188 // If settingsPrivate already has this value, do nothing. (Otherwise, |
| 143 // a change event from settingsPrivate could make us call | 189 // a change event from settingsPrivate could make us call |
| 144 // settingsPrivate.setPref and potentially trigger an IPC loop.) | 190 // settingsPrivate.setPref and potentially trigger an IPC loop.) |
| 145 if (prefWrapper.equals(this.createPrefWrapper_(prefObj))) | 191 if (deepEqual(prefStoreValue, prefObj.value)) |
| 146 return; | 192 return; |
| 147 | 193 |
| 148 this.settingsApi_.setPref( | 194 this.settingsApi_.setPref( |
| 149 key, | 195 key, |
| 150 prefObj.value, | 196 prefObj.value, |
| 151 /* pageId */ '', | 197 /* pageId */ '', |
| 152 /* callback */ this.setPrefCallback_.bind(this, key)); | 198 /* callback */ this.setPrefCallback_.bind(this, key)); |
| 153 }, | 199 }, |
| 154 | 200 |
| 155 /** | 201 /** |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 194 | 240 |
| 195 /** | 241 /** |
| 196 * Updates the prefs model with the given prefs. | 242 * Updates the prefs model with the given prefs. |
| 197 * @param {!Array<!chrome.settingsPrivate.PrefObject>} newPrefs | 243 * @param {!Array<!chrome.settingsPrivate.PrefObject>} newPrefs |
| 198 * @private | 244 * @private |
| 199 */ | 245 */ |
| 200 updatePrefs_: function(newPrefs) { | 246 updatePrefs_: function(newPrefs) { |
| 201 // Use the existing prefs object or create it. | 247 // Use the existing prefs object or create it. |
| 202 var prefs = this.prefs || {}; | 248 var prefs = this.prefs || {}; |
| 203 newPrefs.forEach(function(newPrefObj) { | 249 newPrefs.forEach(function(newPrefObj) { |
| 204 // Use the PrefObject from settingsPrivate to create a PrefWrapper in | 250 // Use the PrefObject from settingsPrivate to create a copy in |
| 205 // prefWrappers_ at the pref's key. | 251 // lastPrefValues_ at the pref's key. |
| 206 this.prefWrappers_[newPrefObj.key] = | 252 this.lastPrefValues_[newPrefObj.key] = deepCopy(newPrefObj.value); |
| 207 this.createPrefWrapper_(newPrefObj); | |
| 208 | 253 |
| 209 // Add the pref to |prefs|. | 254 // Add the pref to |prefs|. |
| 210 cr.exportPath(newPrefObj.key, newPrefObj, prefs); | 255 cr.exportPath(newPrefObj.key, newPrefObj, prefs); |
| 211 // If this.prefs already exists, notify listeners of the change. | 256 // If this.prefs already exists, notify listeners of the change. |
| 212 if (prefs == this.prefs) | 257 if (prefs == this.prefs) |
| 213 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj); | 258 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj); |
| 214 }, this); | 259 }, this); |
| 215 if (!this.prefs) | 260 if (!this.prefs) |
| 216 this.prefs = prefs; | 261 this.prefs = prefs; |
| 217 }, | 262 }, |
| 218 | 263 |
| 219 /** | 264 /** |
| 220 * Given a 'property-changed' path, returns the key of the preference the | 265 * Given a 'property-changed' path, returns the key of the preference the |
| 221 * path refers to. E.g., if the path of the changed property is | 266 * path refers to. E.g., if the path of the changed property is |
| 222 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is | 267 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is |
| 223 * 'search.suggest_enabled'. | 268 * 'search.suggest_enabled'. |
| 224 * @param {string} path | 269 * @param {string} path |
| 225 * @return {string} | 270 * @return {string} |
| 226 * @private | 271 * @private |
| 227 */ | 272 */ |
| 228 getPrefKeyFromPath_: function(path) { | 273 getPrefKeyFromPath_: function(path) { |
| 229 // Skip the first token, which refers to the member variable (this.prefs). | 274 // Skip the first token, which refers to the member variable (this.prefs). |
| 230 var parts = path.split('.'); | 275 var parts = path.split('.'); |
| 231 assert(parts.shift() == 'prefs'); | 276 assert(parts.shift() == 'prefs'); |
| 232 | 277 |
| 233 for (let i = 1; i <= parts.length; i++) { | 278 for (let i = 1; i <= parts.length; i++) { |
| 234 let key = parts.slice(0, i).join('.'); | 279 let key = parts.slice(0, i).join('.'); |
| 235 // The prefWrappers_ keys match the pref keys. | 280 // The lastPrefValues_ keys match the pref keys. |
| 236 if (this.prefWrappers_[key] != undefined) | 281 if (this.lastPrefValues_.hasOwnProperty(key)) |
| 237 return key; | 282 return key; |
| 238 } | 283 } |
| 239 return ''; | 284 return ''; |
| 240 }, | 285 }, |
| 241 | |
| 242 /** | |
| 243 * Creates a PrefWrapper object from a chrome.settingsPrivate pref. | |
| 244 * @param {!chrome.settingsPrivate.PrefObject} prefObj | |
| 245 * PrefObject received from chrome.settingsPrivate. | |
| 246 * @return {PrefWrapper} An object containing a copy of the PrefObject's | |
| 247 * value. | |
| 248 * @private | |
| 249 */ | |
| 250 createPrefWrapper_: function(prefObj) { | |
| 251 return prefObj.type == chrome.settingsPrivate.PrefType.LIST ? | |
| 252 new ListPrefWrapper(prefObj) : new PrefWrapper(prefObj); | |
| 253 }, | |
| 254 }); | 286 }); |
| 255 })(); | 287 })(); |
| OLD | NEW |