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

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

Issue 1357183002: MD-Settings: convert cr-settings-prefs to a singleton model (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: clarify singleton/private/model language Created 5 years, 2 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' models Chrome settings and preferences, listening for 7 * 'cr-settings-prefs' exposes a singleton 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 singleton model tries to set those preferences in
11 * settingsPrivate.setPref succeed, 'prefs' is eventually consistent with the 11 * Chrome. 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-checkbox pref="{{prefs.homepage_is_newtabpage}}">
18 * </cr-settings-checkbox>
18 * 19 *
19 * @group Chrome Settings Elements 20 * @group Chrome Settings Elements
20 * @element cr-settings-prefs 21 * @element cr-settings-prefs
21 */ 22 */
22 23
23 (function() { 24 (function() {
24 'use strict'; 25 'use strict';
25 26
26 /** 27 /**
27 * Checks whether two values are recursively equal. Only compares serializable 28 * Checks whether two values are recursively equal. Only compares serializable
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 properties: { 130 properties: {
130 /** 131 /**
131 * Object containing all preferences, for use by Polymer controls. 132 * Object containing all preferences, for use by Polymer controls.
132 */ 133 */
133 prefs: { 134 prefs: {
134 type: Object, 135 type: Object,
135 notify: true, 136 notify: true,
136 }, 137 },
137 138
138 /** 139 /**
140 * Singleton element created at startup which provides the prefs model.
141 * @type {!Element}
142 */
143 singleton_: {
144 type: Object,
145 value: document.createElement('cr-settings-prefs-singleton'),
146 },
147 },
148
149 observers: [
150 'prefsChanged_(prefs.*)',
151 ],
152
153 /** @override */
154 ready: function() {
155 this.singleton_.initialize();
156 this.startListening_();
157 },
158
159 /**
160 * Binds this.prefs to the cr-settings-prefs-singleton's shared prefs once
161 * preferences are initialized.
162 * @private
163 */
164 startListening_: function() {
165 CrSettingsPrefs.initialized.then(function() {
166 // Ignore changes to prevent prefsChanged_ from notifying singleton_.
167 this.runWhileIgnoringChanges_(function() {
168 this.prefs = this.singleton_.prefs;
169 this.stopListening_();
170 this.listen(
171 this.singleton_, 'prefs-changed', 'singletonPrefsChanged_');
172 });
173 }.bind(this));
174 },
175
176 /**
177 * Stops listening for changes to cr-settings-prefs-singleton's shared
178 * prefs.
179 * @private
180 */
181 stopListening_: function() {
182 this.unlisten(
183 this.singleton_, 'prefs-changed', 'singletonPrefsChanged_');
184 },
185
186 /**
187 * Handles changes reported by singleton_ by forwarding them to the host.
188 * @private
189 */
190 singletonPrefsChanged_: function(e) {
191 // Ignore changes because we've defeated Polymer's dirty-checking.
192 this.runWhileIgnoringChanges_(function() {
193 // Forward notification to host.
194 this.fire(e.type, e.detail, {bubbles: false});
195 });
196 },
197
198 /**
199 * Forwards changes to this.prefs to cr-settings-prefs-singleton.
200 * @private
201 */
202 prefsChanged_: function(info) {
203 // Ignore changes that came from singleton_ so we don't re-process
204 // changes made in other instances of this element.
205 if (!this.ignoreChanges_)
206 this.singleton_.fire('prefs-changed', info, {bubbles: false});
207 },
208
209 /**
210 * Sets ignoreChanged_ before calling the function to suppress change
211 * events that are manually handled.
212 * @param {!function()} fn
213 * @private
214 */
215 runWhileIgnoringChanges_: function(fn) {
216 assert(!this.ignoreChanges_,
217 'Nested calls to runWhileIgnoringChanges_ are not supported');
218 this.ignoreChanges_ = true;
219 fn.call(this);
220 // We can unset ignoreChanges_ now because change notifications
221 // are synchronous.
222 this.ignoreChanges_ = false;
223 },
224
225 /**
226 * Uninitializes this element to remove it from tests. Also resets
227 * cr-settings-prefs-singleton, allowing newly created elements to
228 * re-initialize it.
229 */
230 resetForTesting: function() {
231 this.stopListening_();
232 this.singleton_.resetForTesting();
233 },
234 });
235
236 /**
237 * Privately used element that contains, listens to and updates the shared
238 * prefs state.
239 */
240 Polymer({
241 is: 'cr-settings-prefs-singleton',
242
243 properties: {
244 /**
245 * Object containing all preferences, for use by Polymer controls.
246 * @type {Object|undefined}
247 */
248 prefs: {
249 type: Object,
250 notify: true,
251 },
252
253 /**
139 * Map of pref keys to values representing the state of the Chrome 254 * Map of pref keys to values representing the state of the Chrome
140 * pref store as of the last update from the API. 255 * pref store as of the last update from the API.
141 * @type {Object<*>} 256 * @type {Object<*>}
142 * @private 257 * @private
143 */ 258 */
144 lastPrefValues_: { 259 lastPrefValues_: {
145 type: Object, 260 type: Object,
146 value: function() { return {}; }, 261 value: function() { return {}; },
147 }, 262 },
148 }, 263 },
149 264
150 observers: [ 265 // Listen for the manually fired prefs-changed event.
151 'prefsChanged_(prefs.*)', 266 listeners: {
152 ], 267 'prefs-changed': 'prefsChanged_',
268 },
153 269
154 settingsApi_: chrome.settingsPrivate, 270 settingsApi_: chrome.settingsPrivate,
155 271
156 /** @override */ 272 initialize: function() {
157 ready: function() { 273 // Only initialize once (or after resetForTesting() is called).
274 if (this.initialized_)
275 return;
276 this.initialized_ = true;
277
158 // Set window.mockApi to pass a custom settings API, i.e. for tests. 278 // Set window.mockApi to pass a custom settings API, i.e. for tests.
159 // TODO(michaelpg): don't use a global. 279 // TODO(michaelpg): don't use a global.
160 if (window.mockApi) 280 if (window.mockApi)
161 this.settingsApi_ = window.mockApi; 281 this.settingsApi_ = window.mockApi;
162 CrSettingsPrefs.isInitialized = false;
163 282
164 this.settingsApi_.onPrefsChanged.addListener( 283 this.settingsApi_.onPrefsChanged.addListener(
165 this.onSettingsPrivatePrefsChanged_.bind(this)); 284 this.onSettingsPrivatePrefsChanged_.bind(this));
166 this.settingsApi_.getAllPrefs( 285 this.settingsApi_.getAllPrefs(
167 this.onSettingsPrivatePrefsFetched_.bind(this)); 286 this.onSettingsPrivatePrefsFetched_.bind(this));
168 }, 287 },
169 288
170 /** 289 /**
171 * Polymer callback for changes to this.prefs. 290 * Polymer callback for changes to this.prefs.
172 * @param {!{path: string, value: *}} change 291 * @param {!CustomEvent} e
292 * @param {!{path: string}} change
173 * @private 293 * @private
174 */ 294 */
175 prefsChanged_: function(change) { 295 prefsChanged_: function(e, change) {
176 if (!CrSettingsPrefs.isInitialized) 296 if (!CrSettingsPrefs.isInitialized)
177 return; 297 return;
178 298
179 var key = this.getPrefKeyFromPath_(change.path); 299 var key = this.getPrefKeyFromPath_(change.path);
180 var prefStoreValue = this.lastPrefValues_[key]; 300 var prefStoreValue = this.lastPrefValues_[key];
181 301
182 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */( 302 var prefObj = /** @type {chrome.settingsPrivate.PrefObject} */(
183 this.get(key, this.prefs)); 303 this.get(key, this.prefs));
184 304
185 // If settingsPrivate already has this value, do nothing. (Otherwise, 305 // If settingsPrivate already has this value, do nothing. (Otherwise,
(...skipping 20 matching lines...) Expand all
206 this.updatePrefs_(prefs); 326 this.updatePrefs_(prefs);
207 }, 327 },
208 328
209 /** 329 /**
210 * Called when prefs are fetched from settingsPrivate. 330 * Called when prefs are fetched from settingsPrivate.
211 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs 331 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
212 * @private 332 * @private
213 */ 333 */
214 onSettingsPrivatePrefsFetched_: function(prefs) { 334 onSettingsPrivatePrefsFetched_: function(prefs) {
215 this.updatePrefs_(prefs); 335 this.updatePrefs_(prefs);
216 336 CrSettingsPrefs.setInitialized();
217 CrSettingsPrefs.isInitialized = true;
218 document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
219 }, 337 },
220 338
221 /** 339 /**
222 * Checks the result of calling settingsPrivate.setPref. 340 * Checks the result of calling settingsPrivate.setPref.
223 * @param {string} key The key used in the call to setPref. 341 * @param {string} key The key used in the call to setPref.
224 * @param {boolean} success True if setting the pref succeeded. 342 * @param {boolean} success True if setting the pref succeeded.
225 * @private 343 * @private
226 */ 344 */
227 setPrefCallback_: function(key, success) { 345 setPrefCallback_: function(key, success) {
228 if (success) 346 if (success)
(...skipping 12 matching lines...) Expand all
241 * @private 359 * @private
242 */ 360 */
243 updatePrefs_: function(newPrefs) { 361 updatePrefs_: function(newPrefs) {
244 // Use the existing prefs object or create it. 362 // Use the existing prefs object or create it.
245 var prefs = this.prefs || {}; 363 var prefs = this.prefs || {};
246 newPrefs.forEach(function(newPrefObj) { 364 newPrefs.forEach(function(newPrefObj) {
247 // Use the PrefObject from settingsPrivate to create a copy in 365 // Use the PrefObject from settingsPrivate to create a copy in
248 // lastPrefValues_ at the pref's key. 366 // lastPrefValues_ at the pref's key.
249 this.lastPrefValues_[newPrefObj.key] = deepCopy(newPrefObj.value); 367 this.lastPrefValues_[newPrefObj.key] = deepCopy(newPrefObj.value);
250 368
251 // Add the pref to |prefs|. 369 if (!deepEqual(this.get(newPrefObj.key, prefs), newPrefObj)) {
252 cr.exportPath(newPrefObj.key, newPrefObj, prefs); 370 // Add the pref to |prefs|.
253 // If this.prefs already exists, notify listeners of the change. 371 cr.exportPath(newPrefObj.key, newPrefObj, prefs);
254 if (prefs == this.prefs) 372 // If this.prefs already exists, notify listeners of the change.
255 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj); 373 if (prefs == this.prefs)
374 this.notifyPath('prefs.' + newPrefObj.key, newPrefObj);
375 }
256 }, this); 376 }, this);
257 if (!this.prefs) 377 if (!this.prefs)
258 this.prefs = prefs; 378 this.prefs = prefs;
259 }, 379 },
260 380
261 /** 381 /**
262 * Given a 'property-changed' path, returns the key of the preference the 382 * 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 383 * 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 384 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is
265 * 'search.suggest_enabled'. 385 * 'search.suggest_enabled'.
266 * @param {string} path 386 * @param {string} path
267 * @return {string} 387 * @return {string}
268 * @private 388 * @private
269 */ 389 */
270 getPrefKeyFromPath_: function(path) { 390 getPrefKeyFromPath_: function(path) {
271 // Skip the first token, which refers to the member variable (this.prefs). 391 // Skip the first token, which refers to the member variable (this.prefs).
272 var parts = path.split('.'); 392 var parts = path.split('.');
273 assert(parts.shift() == 'prefs'); 393 assert(parts.shift() == 'prefs');
274 394
275 for (let i = 1; i <= parts.length; i++) { 395 for (let i = 1; i <= parts.length; i++) {
276 let key = parts.slice(0, i).join('.'); 396 let key = parts.slice(0, i).join('.');
277 // The lastPrefValues_ keys match the pref keys. 397 // The lastPrefValues_ keys match the pref keys.
278 if (this.lastPrefValues_.hasOwnProperty(key)) 398 if (this.lastPrefValues_.hasOwnProperty(key))
279 return key; 399 return key;
280 } 400 }
281 return ''; 401 return '';
282 }, 402 },
403
404 /**
405 * Resets the element so it can be re-initialized with a new prefs state.
406 */
407 resetForTesting: function() {
408 this.prefs = undefined;
409 this.lastPrefValues_ = {};
410 this.initialized_ = false;
411 },
283 }); 412 });
284 })(); 413 })();
OLDNEW
« no previous file with comments | « chrome/browser/resources/settings/pref_tracker/pref_tracker.js ('k') | chrome/browser/resources/settings/prefs/prefs_types.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698