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

Side by Side Diff: chrome/browser/resources/settings/prefs/prefs.js

Issue 1287913005: Refactor prefs.js for MD-Settings (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Feedback Created 5 years, 3 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
OLDNEW
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' is an element which serves as a model for 7 * 'cr-settings-prefs' models Chrome settings and preferences, listening for
8 * interaction with settings which are stored in Chrome's 8 * changes to Chrome prefs whitelisted in chrome.settingsPrivate.
9 * Preferences. 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
11 * settingsPrivate.setPref succeed, 'prefs' is eventually consistent with the
12 * Chrome pref store.
10 * 13 *
11 * Example: 14 * Example:
12 * 15 *
13 * <cr-settings-prefs id="prefs"></cr-settings-prefs> 16 * <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs>
14 * <cr-settings-a11y-page prefs="{{this.$.prefs}}"></cr-settings-a11y-page> 17 * <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page>
15 * 18 *
16 * @group Chrome Settings Elements 19 * @group Chrome Settings Elements
17 * @element cr-settings-a11y-page 20 * @element cr-settings-prefs
18 */ 21 */
19 (function() { 22 (function() {
20 'use strict'; 23 'use strict';
21 24
25 /**
26 * Pref state object. Copies values of PrefObjects received from
27 * settingsPrivate to determine when pref values have changed.
28 * This prototype works for primitive types, but more complex types should
29 * override these functions.
30 * @constructor
31 * @param {!PrefObject} prefObj
32 */
33 function PrefWrapper(prefObj) {
34 this.value = prefObj.value;
35 }
36
37 /**
38 * Checks if the value matches this pref's value.
39 * @param {*} value
40 * @return {boolean}
41 */
42 PrefWrapper.prototype.equalsValue = function(value) {
43 return this.value == value;
44 };
45
46 /**
47 * @constructor
48 * @extends {PrefWrapper}
49 * @param {!PrefObject} prefObj
50 */
51 function ListPrefWrapper(prefObj) {
52 // Copy the array so changes to prefObj aren't reflected in this.value.
53 // TODO(michaelpg): Do a deep copy to support nested lists and objects.
54 this.value = prefObj.value.slice();
55 }
56
57 ListPrefWrapper.prototype = {
58 __proto__: PrefWrapper.prototype,
59
60 /** @override */
61 equalsValue: function(value) {
62 // Two arrays might have the same values, so don't just use "==".
63 return this.arraysEqual_(this.value, value);
64 },
65
66 /**
67 * Tests whether two arrays contain the same data (true if primitive
68 * elements are equal and array elements contain the same data).
69 * @param {Array} arr1
70 * @param {Array} arr2
71 * @return {boolean} True if the arrays contain similar values.
72 * @private
73 */
74 arraysEqual_: function(arr1, arr2) {
75 if (!arr1 || !arr2)
76 return arr1 == arr2;
77 if (arr1.length != arr2.length)
78 return false;
79 for (let i = 0; i < arr1.length; i++) {
80 var val1 = arr1[i];
81 var val2 = arr2[i];
82 assert(typeof val1 != 'object' && typeof val2 != 'object',
83 'Objects are not supported.');
84 if (Array.isArray(val1) && !this.arraysEqual_(val1, val2))
85 return false;
86 else if (val1 != val2)
87 return false;
88 }
89 return true;
90 },
91 };
92
22 Polymer({ 93 Polymer({
23 is: 'cr-settings-prefs', 94 is: 'cr-settings-prefs',
24 95
25 properties: { 96 properties: {
26 /** 97 /**
27 * Object containing all preferences. 98 * Object containing all preferences, for use by Polymer controls.
28 */ 99 */
29 prefStore: { 100 prefs: {
30 type: Object, 101 type: Object,
31 value: function() { return {}; }, 102 value: function() { return {}; },
32 notify: true, 103 notify: true,
33 }, 104 },
105
106 /**
107 * Map of pref keys to PrefWrapper objects representing the state of the
108 * Chrome pref store.
109 * @type {Object<PrefWrapper>}
110 * @private
111 */
112 settingsPrivateState_: {
113 type: Object,
114 value: function() { return {}; },
Dan Beam 2015/08/28 00:08:41 value: {}, // does this have issues across protot
michaelpg 2015/08/28 01:52:48 it has issues across objects using this prototype,
115 },
34 }, 116 },
35 117
118 observers: [
119 'prefsChanged_(prefs.*)',
120 ],
121
36 /** @override */ 122 /** @override */
37 created: function() { 123 created: function() {
38 CrSettingsPrefs.isInitialized = false; 124 CrSettingsPrefs.isInitialized = false;
39 125
40 chrome.settingsPrivate.onPrefsChanged.addListener( 126 chrome.settingsPrivate.onPrefsChanged.addListener(
41 this.onPrefsChanged_.bind(this)); 127 this.onSettingsPrivatePrefsChanged_.bind(this));
42 chrome.settingsPrivate.getAllPrefs(this.onPrefsFetched_.bind(this)); 128 chrome.settingsPrivate.getAllPrefs(
129 this.onSettingsPrivatePrefsFetched_.bind(this));
43 }, 130 },
44 131
45 /** 132 /**
133 * Polymer callback for changes to this.prefs.
134 * @param {!{path: string, value: *}} change
135 * @private
136 */
137 prefsChanged_: function(change) {
138 if (!CrSettingsPrefs.isInitialized)
139 return;
140
141 var key = this.getPrefKeyFromPath_(change.path);
142 var prefState = this.settingsPrivateState_[key];
143 if (!prefState)
144 return;
Dan Beam 2015/08/28 00:08:41 \n
michaelpg 2015/08/28 01:52:48 Done.
145 var prefObj = this.get(key, this.prefs);
146
147 // If settingsPrivate already has this value, do nothing. (Otherwise,
148 // a change event from settingsPrivate could make us call
149 // settingsPrivate.setPref and potentially trigger an IPC loop.)
150 if (prefState.equalsValue(prefObj.value))
151 return;
Dan Beam 2015/08/28 00:08:41 \n
michaelpg 2015/08/28 01:52:48 Done.
152 chrome.settingsPrivate.setPref(
153 key,
154 prefObj.value,
155 /* pageId */ '',
156 /* callback */ this.setPrefCallback_.bind(this, key));
157 },
158
159 /**
46 * Called when prefs in the underlying Chrome pref store are changed. 160 * Called when prefs in the underlying Chrome pref store are changed.
47 * @param {!Array<!PrefObject>} prefs The prefs that changed. 161 * @param {!Array<!PrefObject>} prefs The prefs that changed.
48 * @private 162 * @private
49 */ 163 */
50 onPrefsChanged_: function(prefs) { 164 onSettingsPrivatePrefsChanged_: function(prefs) {
51 this.updatePrefs_(prefs, false); 165 if (CrSettingsPrefs.isInitialized)
166 this.updatePrefs_(prefs);
52 }, 167 },
53 168
54 /** 169 /**
55 * Called when prefs are fetched from settingsPrivate. 170 * Called when prefs are fetched from settingsPrivate.
56 * @param {!Array<!PrefObject>} prefs 171 * @param {!Array<!PrefObject>} prefs
57 * @private 172 * @private
58 */ 173 */
59 onPrefsFetched_: function(prefs) { 174 onSettingsPrivatePrefsFetched_: function(prefs) {
60 this.updatePrefs_(prefs, true); 175 this.updatePrefs_(prefs);
61 176
62 CrSettingsPrefs.isInitialized = true; 177 CrSettingsPrefs.isInitialized = true;
63 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED)); 178 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
64 }, 179 },
65 180
181 /**
182 * Checks the result of calling settingsPrivate.setPref.
183 * @param {string} key The key used in the call to setPref.
184 * @param {boolean} success True if setting the pref succeeded.
185 * @private
186 */
187 setPrefCallback_: function(key, success) {
188 if (success)
189 return;
Dan Beam 2015/08/28 00:08:41 \n
michaelpg 2015/08/28 01:52:48 Done.
190 // Get the current pref value from chrome.settingsPrivate to ensure the
191 // UI stays up to date.
192 chrome.settingsPrivate.getPref(key, function(pref) {
193 this.updatePrefs_([pref]);
194 }.bind(this));
195 },
66 196
67 /** 197 /**
68 * Updates the settings model with the given prefs. 198 * Updates the prefs model with the given prefs.
69 * @param {!Array<!PrefObject>} prefs 199 * @param {!Array<!PrefObject>} prefs
70 * @param {boolean} shouldObserve Whether each of the prefs should be
71 * observed.
72 * @private 200 * @private
73 */ 201 */
74 updatePrefs_: function(prefs, shouldObserve) { 202 updatePrefs_: function(prefs) {
75 prefs.forEach(function(prefObj) { 203 prefs.forEach(function(newPrefObj) {
76 let root = this.prefStore; 204 // Set or update the PrefWrapper in settingsPrivateState_ with the
77 let tokens = prefObj.key.split('.'); 205 // PrefObject from settingsPrivate.
206 this.setPrefWrapper_(newPrefObj);
Dan Beam 2015/08/28 00:08:41 change the name of setPrefWrapper_ if... it's not
michaelpg 2015/08/28 01:52:48 Done.
78 207
79 assert(tokens.length > 0); 208 // Set or update the pref in |prefs|. This triggers observers in the UI,
80 209 // which update controls associated with the pref.
81 for (let i = 0; i < tokens.length; i++) { 210 this.setPref_(newPrefObj);
82 let token = tokens[i];
83
84 if (!root.hasOwnProperty(token)) {
85 let path = 'prefStore.' + tokens.slice(0, i + 1).join('.');
86 this.set(path, {});
87 }
88 root = root[token];
89 }
90
91 // NOTE: Do this copy rather than just a re-assignment, so that the
92 // observer fires.
93 for (let objKey in prefObj) {
94 let path = 'prefStore.' + prefObj.key + '.' + objKey;
95
96 // Handle lists specially. We don't want to call this.set()
97 // unconditionally upon updating a list value, since even its contents
98 // are the same as the old list, doing this set() may cause an
99 // infinite update cycle (http://crbug.com/498586).
100 if (objKey == 'value' &&
101 prefObj.type == chrome.settingsPrivate.PrefType.LIST &&
102 !this.shouldUpdateListPrefValue_(root, prefObj['value'])) {
103 continue;
104 }
105
106 this.set(path, prefObj[objKey]);
107 }
108
109 if (shouldObserve) {
110 Object.observe(root, this.propertyChangeCallback_, ['update']);
111 }
112 }, this); 211 }, this);
113 }, 212 },
114 213
214 /**
215 * Given a 'property-changed' path, returns the key of the preference the
216 * path refers to. E.g., if the path of the changed property is
217 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is
218 * 'search.suggest_enabled'.
219 * @param {string} path
220 * @return {string}
221 * @private
222 */
223 getPrefKeyFromPath_: function(path) {
224 // Skip the first token, which refers to the member variable (this.prefs).
225 var tokens = path.split('.').slice(1);
226 var key = '';
115 227
116 /** 228 for (let token of tokens) {
Dan Beam 2015/08/28 00:08:41 nit: for (let i = 0; i < tokens.length; ++i) {
michaelpg 2015/08/28 01:52:48 Done, but for i from [1, tokens.length].
117 * @param {Object} root The root object for a pref that contains a list 229 if (key)
118 * value. 230 key += '.';
119 * @param {!Array} newValue The new list value. 231 key += token;
120 * @return {boolean} Whether the new value is different from the one in 232 // The settingsPrivateState_ keys match the pref keys.
121 * root, thus necessitating a pref update. 233 if (this.settingsPrivateState_[key] != undefined)
122 */ 234 return key;
123 shouldUpdateListPrefValue_: function(root, newValue) {
124 if (root.value == null ||
125 root.value.length != newValue.length) {
126 return true;
127 } 235 }
128 236 return '';
129 for (let i = 0; i < newValue.length; i++) {
130 if (root.value != null && root.value[i] != newValue[i]) {
131 return true;
132 }
133 }
134
135 return false;
136 }, 237 },
137 238
138 /** 239 /**
139 * Called when a property of a pref changes. 240 * Sets or updates the pref denoted by newPrefObj.key in the publicy exposed
140 * @param {!Array<!Object>} changes An array of objects describing changes. 241 * |prefs| property.
141 * @see http://www.html5rocks.com/en/tutorials/es7/observe/ 242 * @param {PrefObject} newPrefObj The pref object to update the pref with.
142 * @private 243 * @private
143 */ 244 */
144 propertyChangeCallback_: function(changes) { 245 setPref_: function(newPrefObj) {
145 changes.forEach(function(change) { 246 // Check if the pref exists already in the Polymer |prefs| object.
146 // UI should only be able to change the value of a setting for now, not 247 if (this.get(newPrefObj.key, this.prefs)) {
147 // disabled, etc. 248 // Update just the value, notifying listeners of the change.
148 assert(change.name == 'value'); 249 this.set('prefs.' + newPrefObj.key + '.value', newPrefObj.value);
250 } else {
251 // Add the pref to |prefs|.
252 let node = this.prefs;
253 let tokens = newPrefObj.key.split('.').slice(0, -1);
Dan Beam 2015/08/28 00:08:41 tokens -> keyParts
michaelpg 2015/08/28 01:52:48 Done.
149 254
150 let newValue = change.object[change.name]; 255 // Crawl the pref tree, generating objects where necessary.
151 assert(newValue !== undefined); 256 tokens.forEach(function(token) {
257 if (!node.hasOwnProperty(token)) {
258 // Don't use Polymer.Base.set because the property update events
259 // won't be useful until the actual pref is set below.
260 node[token] = {};
261 }
262 node = node[token];
263 }, this);
Dan Beam 2015/08/28 00:08:41 why not use? https://code.google.com/p/chromium/co
michaelpg 2015/08/28 01:52:48 Done, thanks.
152 264
153 chrome.settingsPrivate.setPref( 265 // Set the actual preference, notifying listeners of the change.
154 change.object['key'], 266 this.set('prefs.' + newPrefObj.key, newPrefObj);
155 newValue, 267 }
156 /* pageId */ '', 268 },
157 /* callback */ function() {}); 269
158 }); 270 /**
271 * Creates a PrefWrapper object from a chrome.settingsPrivate pref and adds
Dan Beam 2015/08/28 00:08:41 {create,make}PrefWrapper_? general nit: if the do
michaelpg 2015/08/28 01:52:48 Done.
272 * the PrefWrapper to settingsPrivateState_.
273 * @param {!PrefObject} PrefObject received from chrome.settingsPrivate.
274 * @private
275 */
276 setPrefWrapper_: function(prefObj) {
277 var prefState;
278 if (prefObj.type == chrome.settingsPrivate.PrefType.LIST)
279 prefState = new ListPrefWrapper(prefObj);
280 else
281 prefState = new PrefWrapper(prefObj);
282 this.settingsPrivateState_[prefObj.key] = prefState;
159 }, 283 },
160 }); 284 });
161 })(); 285 })();
OLDNEW
« no previous file with comments | « chrome/browser/resources/settings/prefs/prefs.html ('k') | chrome/browser/resources/settings/settings.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698