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
3 * found in the LICENSE file. */
7 * 'cr-settings-prefs' models Chrome settings and preferences, listening for
8 * changes to Chrome prefs whitelisted in chrome.settingsPrivate.
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
16 * <cr-settings-prefs prefs="{{prefs}}"></cr-settings-prefs>
17 * <cr-settings-a11y-page prefs="{{prefs}}"></cr-settings-a11y-page>
19 * @group Chrome Settings Elements
20 * @element cr-settings-prefs
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.
29 * @param {!chrome.settingsPrivate.PrefObject} prefObj
31 function PrefWrapper(prefObj
) {
32 this.key_
= prefObj
.key
;
33 this.value_
= prefObj
.value
;
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
42 PrefWrapper
.prototype.equals = function(other
) {
43 assert(this.key_
== other
.key_
);
44 return this.value_
== other
.value_
;
49 * @extends {PrefWrapper}
50 * @param {!chrome.settingsPrivate.PrefObject} prefObj
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();
58 ListPrefWrapper
.prototype = {
59 __proto__
: PrefWrapper
.prototype,
62 * Tests whether two ListPrefWrapper values contain the same list items.
65 equals: function(other
) {
67 assert(this.key_
== other
.key_
);
68 if (this.value_
.length
!= other
.value_
.length
)
70 for (let i
= 0; i
< this.value_
.length
; i
++) {
71 if (this.value_
[i
] != other
.value_
[i
])
82 is
: 'cr-settings-prefs',
86 * Object containing all preferences, for use by Polymer controls.
90 value: function() { return {}; },
95 * Map of pref keys to PrefWrapper objects representing the state of the
97 * @type {Object<PrefWrapper>}
102 value: function() { return {}; },
107 'prefsChanged_(prefs.*)',
111 created: function() {
112 CrSettingsPrefs
.isInitialized
= false;
114 chrome
.settingsPrivate
.onPrefsChanged
.addListener(
115 this.onSettingsPrivatePrefsChanged_
.bind(this));
116 chrome
.settingsPrivate
.getAllPrefs(
117 this.onSettingsPrivatePrefsFetched_
.bind(this));
121 * Polymer callback for changes to this.prefs.
122 * @param {!{path: string, value: *}} change
125 prefsChanged_: function(change
) {
126 if (!CrSettingsPrefs
.isInitialized
)
129 var key
= this.getPrefKeyFromPath_(change
.path
);
130 var prefWrapper
= this.prefWrappers_
[key
];
134 var prefObj
= /** @type {chrome.settingsPrivate.PrefObject} */(
135 this.get(key
, this.prefs
));
137 // If settingsPrivate already has this value, do nothing. (Otherwise,
138 // a change event from settingsPrivate could make us call
139 // settingsPrivate.setPref and potentially trigger an IPC loop.)
140 if (prefWrapper
.equals(this.createPrefWrapper_(prefObj
)))
143 chrome
.settingsPrivate
.setPref(
147 /* callback */ this.setPrefCallback_
.bind(this, key
));
151 * Called when prefs in the underlying Chrome pref store are changed.
152 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
153 * The prefs that changed.
156 onSettingsPrivatePrefsChanged_: function(prefs
) {
157 if (CrSettingsPrefs
.isInitialized
)
158 this.updatePrefs_(prefs
);
162 * Called when prefs are fetched from settingsPrivate.
163 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
166 onSettingsPrivatePrefsFetched_: function(prefs
) {
167 this.updatePrefs_(prefs
);
169 CrSettingsPrefs
.isInitialized
= true;
170 document
.dispatchEvent(new Event(CrSettingsPrefs
.INITIALIZED
));
174 * Checks the result of calling settingsPrivate.setPref.
175 * @param {string} key The key used in the call to setPref.
176 * @param {boolean} success True if setting the pref succeeded.
179 setPrefCallback_: function(key
, success
) {
183 // Get the current pref value from chrome.settingsPrivate to ensure the
184 // UI stays up to date.
185 chrome
.settingsPrivate
.getPref(key
, function(pref
) {
186 this.updatePrefs_([pref
]);
191 * Updates the prefs model with the given prefs.
192 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
195 updatePrefs_: function(prefs
) {
196 prefs
.forEach(function(newPrefObj
) {
197 // Use the PrefObject from settingsPrivate to create a PrefWrapper in
198 // prefWrappers_ at the pref's key.
199 this.prefWrappers_
[newPrefObj
.key
] =
200 this.createPrefWrapper_(newPrefObj
);
202 // Set or update the pref in |prefs|. This triggers observers in the UI,
203 // which update controls associated with the pref.
204 this.setPref_(newPrefObj
);
209 * Given a 'property-changed' path, returns the key of the preference the
210 * path refers to. E.g., if the path of the changed property is
211 * 'prefs.search.suggest_enabled.value', the key of the pref that changed is
212 * 'search.suggest_enabled'.
213 * @param {string} path
217 getPrefKeyFromPath_: function(path
) {
218 // Skip the first token, which refers to the member variable (this.prefs).
219 var parts
= path
.split('.');
220 assert(parts
.shift() == 'prefs');
222 for (let i
= 1; i
<= parts
.length
; i
++) {
223 let key
= parts
.slice(0, i
).join('.');
224 // The prefWrappers_ keys match the pref keys.
225 if (this.prefWrappers_
[key
] != undefined)
232 * Sets or updates the pref denoted by newPrefObj.key in the publicy exposed
234 * @param {chrome.settingsPrivate.PrefObject} newPrefObj The pref object to
235 * update the pref with.
238 setPref_: function(newPrefObj
) {
239 // Check if the pref exists already in the Polymer |prefs| object.
240 if (this.get(newPrefObj
.key
, this.prefs
)) {
241 // Update just the value, notifying listeners of the change.
242 this.set('prefs.' + newPrefObj
.key
+ '.value', newPrefObj
.value
);
244 // Add the pref to |prefs|. cr.exportPath doesn't use Polymer.Base.set,
245 // which is OK because the nested property update events aren't useful.
246 cr
.exportPath(newPrefObj
.key
, newPrefObj
, this.prefs
);
247 // Notify listeners of the change at the preference key.
248 this.notifyPath('prefs.' + newPrefObj
.key
, newPrefObj
);
253 * Creates a PrefWrapper object from a chrome.settingsPrivate pref.
254 * @param {!chrome.settingsPrivate.PrefObject} prefObj
255 * PrefObject received from chrome.settingsPrivate.
256 * @return {PrefWrapper} An object containing a copy of the PrefObject's
260 createPrefWrapper_: function(prefObj
) {
261 return prefObj
.type
== chrome
.settingsPrivate
.PrefType
.LIST
?
262 new ListPrefWrapper(prefObj
) : new PrefWrapper(prefObj
);