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

Unified 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: Created 5 years, 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/settings/prefs/prefs.js
diff --git a/chrome/browser/resources/settings/prefs/prefs.js b/chrome/browser/resources/settings/prefs/prefs.js
index b68ccea3c5c9b26ee7608800145cf458189e34af..7f0b2684a560ec9bf663e972f70ecd3ee3512777 100644
--- a/chrome/browser/resources/settings/prefs/prefs.js
+++ b/chrome/browser/resources/settings/prefs/prefs.js
@@ -19,6 +19,12 @@
(function() {
'use strict';
+ // TODO(michaelpg): Remove once prefs.js is complete and well-tested. Used
+ // to catch errors like crbug.com/521884.
+ var verifySetPref = function(result) {
+ assert(result, 'Failed to set preference.');
+ };
+
Polymer({
is: 'cr-settings-prefs',
@@ -36,6 +42,7 @@
/** @override */
created: function() {
CrSettingsPrefs.isInitialized = false;
+ this.committed_ = {};
michaelpg 2015/08/19 02:09:53 Feel free to suggest a better name for "our belief
stevenjb 2015/08/19 17:26:31 It looks like this is a cache of the most recent p
chrome.settingsPrivate.onPrefsChanged.addListener(
this.onPrefsChanged_.bind(this));
@@ -48,7 +55,8 @@
* @private
*/
onPrefsChanged_: function(prefs) {
- this.updatePrefs_(prefs, false);
+ if (CrSettingsPrefs.isInitialized)
+ this.updatePrefs_(prefs);
},
/**
@@ -57,105 +65,167 @@
* @private
*/
onPrefsFetched_: function(prefs) {
- this.updatePrefs_(prefs, true);
+ this.updatePrefs_(prefs);
CrSettingsPrefs.isInitialized = true;
document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
},
-
/**
* Updates the settings model with the given prefs.
* @param {!Array<!PrefObject>} prefs
- * @param {boolean} shouldObserve Whether each of the prefs should be
- * observed.
* @private
*/
- updatePrefs_: function(prefs, shouldObserve) {
+ updatePrefs_: function(prefs) {
prefs.forEach(function(prefObj) {
- let root = this.prefStore;
- let tokens = prefObj.key.split('.');
-
- assert(tokens.length > 0);
-
- for (let i = 0; i < tokens.length; i++) {
- let token = tokens[i];
-
- if (!root.hasOwnProperty(token)) {
- let path = 'prefStore.' + tokens.slice(0, i + 1).join('.');
- this.set(path, {});
- }
- root = root[token];
- }
-
- // NOTE: Do this copy rather than just a re-assignment, so that the
- // observer fires.
- for (let objKey in prefObj) {
- let path = 'prefStore.' + prefObj.key + '.' + objKey;
-
- // Handle lists specially. We don't want to call this.set()
- // unconditionally upon updating a list value, since even its contents
- // are the same as the old list, doing this set() may cause an
- // infinite update cycle (http://crbug.com/498586).
- if (objKey == 'value' &&
- prefObj.type == chrome.settingsPrivate.PrefType.LIST &&
- !this.shouldUpdateListPrefValue_(root, prefObj['value'])) {
- continue;
+ // Cache a copy of the value so we know it came from settingsPrivate.
+ if (prefObj.type == chrome.settingsPrivate.PrefType.LIST)
+ this.committed_[prefObj.key] = prefObj.value.slice();
+ else
+ this.committed_[prefObj.key] = prefObj.value;
Dan Beam 2015/08/19 03:15:38 make a getValue() or getLocalValue() that PrefObje
stevenjb 2015/08/19 17:26:31 Also, this assumes knowledge that prefObj.value is
+
+ // Check if the pref exists already.
+ var pref = /** @type {(!PrefObject|undefined)} */(
+ this.get(prefObj.key, this.prefStore));
+ if (pref) {
+ // Check if the values are different.
Dan Beam 2015/08/19 03:15:38 make a PrefObject#equals() so you can do this rega
+ if (pref.type == chrome.settingsPrivate.PrefType.LIST) {
+ // Two arrays might have the "same" values, so don't just use "==".
+ if (this.arraysEqual_(pref.value, prefObj.value))
+ return;
+ } else if (pref.value == prefObj.value) {
+ return;
}
- this.set(path, prefObj[objKey]);
- }
-
- if (shouldObserve) {
- Object.observe(root, this.propertyChangeCallback_, ['update']);
+ // If we're changing the value to a new array, we need to observe
+ // the array.
+ if (prefObj.value instanceof Array)
+ this.observeListPrefValue_(prefObj);
Dan Beam 2015/08/19 03:15:38 make PrefObject#observe(callback)
+ this.set('prefStore.' + pref.key + '.value', prefObj.value);
+ } else {
+ let node = this.prefStore;
+ let tokens = prefObj.key.split('.').slice(0, -1);
+
+ // Crawl the pref tree, generating objects where necessary.
+ tokens.forEach(function(token) {
Dan Beam 2015/08/19 03:15:38 nit: maybe massage cr.exportPath() take a |target|
stevenjb 2015/08/19 17:26:31 I see that prefs.html includes cr.html, but it doe
Dan Beam 2015/08/19 18:54:11 we could also make a generic merge() or mergeObjec
+ if (!node.hasOwnProperty(token)) {
+ // Don't use Polymer.Base.set because the property update events
+ // won't be useful until the actual pref is set below.
+ node[token] = {};
+ }
+ node = node[token];
+ }, this);
+
+ // Register the observer before this.set(); setting the pref in the
+ // prefStore could trigger a change in prefObj as a side effect.
+ this.observePref_(prefObj);
+ this.set('prefStore.' + prefObj.key, prefObj);
}
}, this);
},
-
/**
- * @param {Object} root The root object for a pref that contains a list
- * value.
- * @param {!Array} newValue The new list value.
- * @return {boolean} Whether the new value is different from the one in
- * root, thus necessitating a pref update.
+ * Registers an observer on the PrefObject using Object.observe.
+ * @param {!PrefObject} prefObj The preference to observe.
*/
- shouldUpdateListPrefValue_: function(root, newValue) {
- if (root.value == null ||
- root.value.length != newValue.length) {
- return true;
- }
-
- for (let i = 0; i < newValue.length; i++) {
- if (root.value != null && root.value[i] != newValue[i]) {
- return true;
- }
- }
+ observePref_: function(prefObj) {
+ Object.observe(prefObj, this.propertyChangeCallback_.bind(this),
+ ['update']);
+ // If the value is an array, observe the array itself as well.
+ if (prefObj.type == chrome.settingsPrivate.PrefType.LIST)
+ this.observeListPrefValue_(prefObj);
+ },
- return false;
+ /**
+ * Observes the PrefObject's value for PrefObject type LIST.
+ * @param {!PrefObject} prefObj The preference whose value to observe.
+ */
+ observeListPrefValue_: function(prefObj) {
Dan Beam 2015/08/19 03:15:38 when is this called without observePref_ being tri
+ assert(prefObj.type == chrome.settingsPrivate.PrefType.LIST);
+ Object.observe(prefObj.value,
+ this.arrayChangeCallback_.bind(this, prefObj.key),
+ ['update', 'splice']);
},
/**
* Called when a property of a pref changes.
- * @param {!Array<!Object>} changes An array of objects describing changes.
- * @see http://www.html5rocks.com/en/tutorials/es7/observe/
+ * @param {!Array<!{object: PrefObject, name: string}>} changes
+ * Array of change records.
* @private
*/
propertyChangeCallback_: function(changes) {
+ // Rarely, multiple changes could refer to the same value (e.g., toggling
+ // a boolean preference twice), causing redundant work. We could ignore
+ // all but the last such change, but that would muddle the data flow.
changes.forEach(function(change) {
- // UI should only be able to change the value of a setting for now, not
- // disabled, etc.
+ // UI should only be able to change the value of a preference for now.
stevenjb 2015/08/19 17:26:31 It's not immediately clear that "value" refers to
assert(change.name == 'value');
+ let newValue = change.object.value;
+ assert(newValue != undefined);
+
+ // If settingsPrivate already has this value, do nothing. (Otherwise,
+ // a change event from settingsPrivate could make us call
+ // settingsPrivate.setPref. This can start an infinite IPC loop if there
+ // are multiple Settings windows and the pref is changed.)
+ var prefValue = this.committed_[change.object.key];
+ if (prefValue instanceof Array) {
+ if (this.arraysEqual_(prefValue, newValue))
+ return;
+ } else if (prefValue == newValue) {
+ return;
+ }
- let newValue = change.object[change.name];
- assert(newValue !== undefined);
+ // Save the value we're going to set, so we don't respond to the
+ // change event sent by settingsPrivate once we call setPref.
chrome.settingsPrivate.setPref(
- change.object['key'],
+ change.object.key,
newValue,
/* pageId */ '',
- /* callback */ function() {});
- });
+ /* callback */ verifySetPref);
+ }, this);
+ },
+
+ /**
+ * Called when an array value of a pref changes.
+ * @param {string} key The name of the pref.
+ * @param {!Array<{object: !Array}>} changes The list of change records.
+ */
+ arrayChangeCallback_: function(key, changes) {
+ // The underlying array is the same for all changes.
+ var value = changes[0].object;
+
+ // If settingsPrivate already has this value, do nothing.
+ if (this.arraysEqual_(this.committed_[key], value))
+ return;
+ chrome.settingsPrivate.setPref(key, value,
+ /* pageId */ '',
+ /* callback */ verifySetPref);
+ },
+
+ /**
+ * Tests whether two arrays contain the same data (true if primitive
+ * elements are equal and array elements contain the same data).
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @return {boolean} True if the arrays contain similar values.
+ */
+ arraysEqual_: function(arr1, arr2) {
+ if (!arr1 || !arr2)
+ return arr1 == arr2;
+ if (arr1.length != arr2.length)
+ return false;
+ for (let i = 0; i < arr1.length; i++) {
+ var val1 = arr1[i];
+ var val2 = arr2[i];
+ assert(typeof val1 != 'object' && typeof val2 != 'object',
+ 'Objects are not supported.');
+ if (val1 instanceof Array && !this.arraysEqual_(val1, val2))
+ return false;
+ else if (val1 != val2)
+ return false;
+ }
+ return true;
},
});
})();
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698