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

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: fix 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 {!chrome.settingsPrivate.PrefObject} prefObj
32 */
33 function PrefWrapper(prefObj) {
34 this.key_ = prefObj.key;
35 this.value_ = prefObj.value;
36 }
37
38 /**
39 * Checks if other's value equals this.value_. Both prefs wrapped by the
40 * PrefWrappers should have the same keys.
41 * @param {PrefWrapper} other
42 * @return {boolean}
43 */
44 PrefWrapper.prototype.equals = function(other) {
45 assert(this.key_ == other.key_);
46 return this.value_ == other.value_;
47 };
48
49 /**
50 * @constructor
51 * @extends {PrefWrapper}
52 * @param {!chrome.settingsPrivate.PrefObject} prefObj
53 */
54 function ListPrefWrapper(prefObj) {
55 // Copy the array so changes to prefObj aren't reflected in this.value_.
56 // TODO(michaelpg): Do a deep copy to support nested lists and objects.
57 this.value_ = prefObj.value.slice();
58 }
59
60 ListPrefWrapper.prototype = {
61 __proto__: PrefWrapper.prototype,
62
63 /**
64 * Tests whether two ListPrefWrapper values contain the same list items.
65 * @override
66 */
67 equals: function(other) {
68 assert(this.key_ == other.key_);
69 if (this.value_.length != other.value_.length)
70 return false;
71 for (let i = 0; i < this.value_.length; i++) {
72 if (this.value_[i] != other.value_[i])
73 return false;
74 }
75 return true;
76 },
77 };
78
22 Polymer({ 79 Polymer({
23 is: 'cr-settings-prefs', 80 is: 'cr-settings-prefs',
24 81
25 properties: { 82 properties: {
26 /** 83 /**
27 * Object containing all preferences. 84 * Object containing all preferences, for use by Polymer controls.
28 */ 85 */
29 prefStore: { 86 prefs: {
30 type: Object, 87 type: Object,
31 value: function() { return {}; }, 88 value: function() { return {}; },
32 notify: true, 89 notify: true,
33 }, 90 },
34 }, 91
92 /**
93 * Map of pref keys to PrefWrapper objects representing the state of the
94 * Chrome pref store.
95 * @type {Object<PrefWrapper>}
michaelpg 2015/08/28 20:43:05 "ERROR - Bad type annotation. Unknown type PrefWra
michaelpg 2015/08/29 01:26:00 Done.
96 * @private
97 */
98 prefWrappers_: {
99 type: Object,
100 value: function() { return {}; },
101 },
102 },
103
104 observers: [
105 'prefsChanged_(prefs.*)',
106 ],
35 107
36 /** @override */ 108 /** @override */
37 created: function() { 109 created: function() {
38 CrSettingsPrefs.isInitialized = false; 110 CrSettingsPrefs.isInitialized = false;
39 111
40 chrome.settingsPrivate.onPrefsChanged.addListener( 112 chrome.settingsPrivate.onPrefsChanged.addListener(
41 this.onPrefsChanged_.bind(this)); 113 this.onSettingsPrivatePrefsChanged_.bind(this));
42 chrome.settingsPrivate.getAllPrefs(this.onPrefsFetched_.bind(this)); 114 chrome.settingsPrivate.getAllPrefs(
115 this.onSettingsPrivatePrefsFetched_.bind(this));
116 },
117
118 /**
119 * Polymer callback for changes to this.prefs.
120 * @param {!{path: string, value: *}} change
121 * @private
122 */
123 prefsChanged_: function(change) {
124 if (!CrSettingsPrefs.isInitialized)
125 return;
126
127 var key = this.getPrefKeyFromPath_(change.path);
128 var prefWrapper = this.prefWrappers_[key];
129 if (!prefWrapper)
130 return;
131
132 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */(
133 this.get(key, this.prefs));
134
135 // If settingsPrivate already has this value, do nothing. (Otherwise,
136 // a change event from settingsPrivate could make us call
137 // settingsPrivate.setPref and potentially trigger an IPC loop.)
138 if (prefWrapper.equals(this.createPrefWrapper_(prefObj)))
139 return;
140
141 chrome.settingsPrivate.setPref(
142 key,
143 prefObj.value,
144 /* pageId */ '',
145 /* callback */ this.setPrefCallback_.bind(this, key));
43 }, 146 },
44 147
45 /** 148 /**
46 * Called when prefs in the underlying Chrome pref store are changed. 149 * Called when prefs in the underlying Chrome pref store are changed.
47 * @param {!Array<!PrefObject>} prefs The prefs that changed. 150 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
48 * @private 151 * The prefs that changed.
49 */ 152 * @private
50 onPrefsChanged_: function(prefs) { 153 */
51 this.updatePrefs_(prefs, false); 154 onSettingsPrivatePrefsChanged_: function(prefs) {
155 if (CrSettingsPrefs.isInitialized)
156 this.updatePrefs_(prefs);
52 }, 157 },
53 158
54 /** 159 /**
55 * Called when prefs are fetched from settingsPrivate. 160 * Called when prefs are fetched from settingsPrivate.
56 * @param {!Array<!PrefObject>} prefs 161 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
57 * @private 162 * @private
58 */ 163 */
59 onPrefsFetched_: function(prefs) { 164 onSettingsPrivatePrefsFetched_: function(prefs) {
60 this.updatePrefs_(prefs, true); 165 this.updatePrefs_(prefs);
61 166
62 CrSettingsPrefs.isInitialized = true; 167 CrSettingsPrefs.isInitialized = true;
63 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED)); 168 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
64 }, 169 },
65 170
66 171 /**
67 /** 172 * Checks the result of calling settingsPrivate.setPref.
68 * Updates the settings model with the given prefs. 173 * @param {string} key The key used in the call to setPref.
69 * @param {!Array<!PrefObject>} prefs 174 * @param {boolean} success True if setting the pref succeeded.
70 * @param {boolean} shouldObserve Whether each of the prefs should be 175 * @private
71 * observed. 176 */
72 * @private 177 setPrefCallback_: function(key, success) {
73 */ 178 if (success)
74 updatePrefs_: function(prefs, shouldObserve) { 179 return;
75 prefs.forEach(function(prefObj) { 180
76 let root = this.prefStore; 181 // Get the current pref value from chrome.settingsPrivate to ensure the
77 let tokens = prefObj.key.split('.'); 182 // UI stays up to date.
78 183 chrome.settingsPrivate.getPref(key, function(pref) {
79 assert(tokens.length > 0); 184 this.updatePrefs_([pref]);
80 185 }.bind(this));
81 for (let i = 0; i < tokens.length; i++) { 186 },
82 let token = tokens[i]; 187
83 188 /**
84 if (!root.hasOwnProperty(token)) { 189 * Updates the prefs model with the given prefs.
85 let path = 'prefStore.' + tokens.slice(0, i + 1).join('.'); 190 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
86 this.set(path, {}); 191 * @private
87 } 192 */
88 root = root[token]; 193 updatePrefs_: function(prefs) {
89 } 194 prefs.forEach(function(newPrefObj) {
90 195 // Use the PrefObject from settingsPrivate to create a PrefWrapper in
91 // NOTE: Do this copy rather than just a re-assignment, so that the 196 // prefWrappers_ at the pref's key.
92 // observer fires. 197 this.prefWrappers_[newPrefObj.key] =
93 for (let objKey in prefObj) { 198 this.createPrefWrapper_(newPrefObj);
94 let path = 'prefStore.' + prefObj.key + '.' + objKey; 199
95 200 // Set or update the pref in |prefs|. This triggers observers in the UI,
96 // Handle lists specially. We don't want to call this.set() 201 // which update controls associated with the pref.
97 // unconditionally upon updating a list value, since even its contents 202 this.setPref_(newPrefObj);
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); 203 }, this);
113 }, 204 },
114 205
115 206 /**
116 /** 207 * Given a 'property-changed' path, returns the key of the preference the
117 * @param {Object} root The root object for a pref that contains a list 208 * path refers to. E.g., if the path of the changed property is
209 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is
210 * 'search.suggest_enabled'.
211 * @param {string} path
212 * @return {string}
213 * @private
214 */
215 getPrefKeyFromPath_: function(path) {
216 // Skip the first token, which refers to the member variable (this.prefs).
217 var parts = path.split('.');
218 assert(parts.shift() == 'prefs');
219
220 for (let i = 1; i <= parts.length; i++) {
221 let key = parts.slice(0, i).join('.');
222 // The prefWrappers_ keys match the pref keys.
223 if (this.prefWrappers_[key] != undefined)
224 return key;
225 }
226 return '';
227 },
228
229 /**
230 * Sets or updates the pref denoted by newPrefObj.key in the publicy exposed
231 * |prefs| property.
232 * @param {chrome.settingsPrivate.PrefObject} newPrefObj The pref object to
233 * update the pref with.
234 * @private
235 */
236 setPref_: function(newPrefObj) {
237 // Check if the pref exists already in the Polymer |prefs| object.
238 if (this.get(newPrefObj.key, this.prefs)) {
239 // Update just the value, notifying listeners of the change.
240 this.set('prefs.' + newPrefObj.key + '.value', newPrefObj.value);
241 } else {
242 // Add the pref to |prefs|. cr.exportPath doesn't use Polymer.Base.set,
243 // which is OK because the nested property update events aren't useful.
244 cr.exportPath(newPrefObj.key, newPrefObj, this.prefs);
michaelpg 2015/08/28 20:43:05 "ERROR - cr.exportPath() should have exactly 1 arg
Dan Beam 2015/08/28 20:50:05 this is wrong: https://code.google.com/p/chromium/
michaelpg 2015/08/28 20:58:10 should we remove that check? or not use exportPath
Dan Beam 2015/08/28 21:06:56 we should fix it
michaelpg 2015/08/29 01:26:00 Done.
245 // Notify listeners of the change at the preference key.
246 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj);
247 }
248 },
249
250 /**
251 * Creates a PrefWrapper object from a chrome.settingsPrivate pref.
252 * @param {!chrome.settingsPrivate.PrefObject} prefObj
253 * PrefObject received from chrome.settingsPrivate.
254 * @return {PrefWrapper} An object containing a copy of the PrefObject's
118 * value. 255 * value.
119 * @param {!Array} newValue The new list value. 256 * @private
120 * @return {boolean} Whether the new value is different from the one in 257 */
121 * root, thus necessitating a pref update. 258 createPrefWrapper_: function(prefObj) {
122 */ 259 return prefObj.type == chrome.settingsPrivate.PrefType.LIST ?
123 shouldUpdateListPrefValue_: function(root, newValue) { 260 new ListPrefWrapper(prefObj) : new PrefWrapper(prefObj);
124 if (root.value == null ||
125 root.value.length != newValue.length) {
126 return true;
127 }
128
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 },
137
138 /**
139 * Called when a property of a pref changes.
140 * @param {!Array<!Object>} changes An array of objects describing changes.
141 * @see http://www.html5rocks.com/en/tutorials/es7/observe/
142 * @private
143 */
144 propertyChangeCallback_: function(changes) {
145 changes.forEach(function(change) {
146 // UI should only be able to change the value of a setting for now, not
147 // disabled, etc.
148 assert(change.name == 'value');
149
150 let newValue = change.object[change.name];
151 assert(newValue !== undefined);
152
153 chrome.settingsPrivate.setPref(
154 change.object['key'],
155 newValue,
156 /* pageId */ '',
157 /* callback */ function() {});
158 });
159 }, 261 },
160 }); 262 });
161 })(); 263 })();
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