[MacViews] Show comboboxes with a native NSMenu
[chromium-blink-merge.git] / chrome / browser / resources / settings / prefs / prefs.js
blob98c82f78b5c4543a20fc1dd601e5bfabe98b010c
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. */
5 /**
6 * @fileoverview
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
12 * Chrome pref store.
14 * Example:
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
23 /**
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.
28 * @constructor
29 * @param {!chrome.settingsPrivate.PrefObject} prefObj
31 function PrefWrapper(prefObj) {
32 this.key_ = prefObj.key;
33 this.value_ = prefObj.value;
36 /**
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
40 * @return {boolean}
42 PrefWrapper.prototype.equals = function(other) {
43 assert(this.key_ == other.key_);
44 return this.value_ == other.value_;
47 /**
48 * @constructor
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,
61 /**
62 * Tests whether two ListPrefWrapper values contain the same list items.
63 * @override
65 equals: function(other) {
66 'use strict';
67 assert(this.key_ == other.key_);
68 if (this.value_.length != other.value_.length)
69 return false;
70 for (let i = 0; i < this.value_.length; i++) {
71 if (this.value_[i] != other.value_[i])
72 return false;
74 return true;
78 (function() {
79 'use strict';
81 Polymer({
82 is: 'cr-settings-prefs',
84 properties: {
85 /**
86 * Object containing all preferences, for use by Polymer controls.
88 prefs: {
89 type: Object,
90 value: function() { return {}; },
91 notify: true,
94 /**
95 * Map of pref keys to PrefWrapper objects representing the state of the
96 * Chrome pref store.
97 * @type {Object<PrefWrapper>}
98 * @private
100 prefWrappers_: {
101 type: Object,
102 value: function() { return {}; },
106 observers: [
107 'prefsChanged_(prefs.*)',
110 /** @override */
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
123 * @private
125 prefsChanged_: function(change) {
126 if (!CrSettingsPrefs.isInitialized)
127 return;
129 var key = this.getPrefKeyFromPath_(change.path);
130 var prefWrapper = this.prefWrappers_[key];
131 if (!prefWrapper)
132 return;
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)))
141 return;
143 chrome.settingsPrivate.setPref(
144 key,
145 prefObj.value,
146 /* pageId */ '',
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.
154 * @private
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
164 * @private
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.
177 * @private
179 setPrefCallback_: function(key, success) {
180 if (success)
181 return;
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]);
187 }.bind(this));
191 * Updates the prefs model with the given prefs.
192 * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
193 * @private
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);
205 }, this);
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
214 * @return {string}
215 * @private
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)
226 return key;
228 return '';
232 * Sets or updates the pref denoted by newPrefObj.key in the publicy exposed
233 * |prefs| property.
234 * @param {chrome.settingsPrivate.PrefObject} newPrefObj The pref object to
235 * update the pref with.
236 * @private
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);
243 } else {
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
257 * value.
258 * @private
260 createPrefWrapper_: function(prefObj) {
261 return prefObj.type == chrome.settingsPrivate.PrefType.LIST ?
262 new ListPrefWrapper(prefObj) : new PrefWrapper(prefObj);
265 })();