Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / options / browser_options.js
1 // Copyright (c) 2012 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.
4
5 cr.define('options', function() {
6   var OptionsPage = options.OptionsPage;
7   var Page = cr.ui.pageManager.Page;
8   var PageManager = cr.ui.pageManager.PageManager;
9   var ArrayDataModel = cr.ui.ArrayDataModel;
10   var RepeatingButton = cr.ui.RepeatingButton;
11   var HotwordSearchSettingIndicator = options.HotwordSearchSettingIndicator;
12   var NetworkPredictionOptions = {
13     ALWAYS: 0,
14     WIFI_ONLY: 1,
15     NEVER: 2,
16     UNSET: 3,
17     DEFAULT: 1
18   };
19
20   //
21   // BrowserOptions class
22   // Encapsulated handling of browser options page.
23   //
24   function BrowserOptions() {
25     Page.call(this, 'settings', loadTimeData.getString('settingsTitle'),
26               'settings');
27   }
28
29   cr.addSingletonGetter(BrowserOptions);
30
31   /**
32    * @param {HTMLElement} section The section to show or hide.
33    * @return {boolean} Whether the section should be shown.
34    * @private
35    */
36   BrowserOptions.shouldShowSection_ = function(section) {
37     // If the section is hidden or hiding, it should be shown.
38     return section.style.height == '' || section.style.height == '0px';
39   };
40
41   BrowserOptions.prototype = {
42     __proto__: Page.prototype,
43
44     /**
45      * Keeps track of whether the user is signed in or not.
46      * @type {boolean}
47      * @private
48      */
49     signedIn_: false,
50
51     /**
52      * Indicates whether signing out is allowed or whether a complete profile
53      * wipe is required to remove the current enterprise account.
54      * @type {boolean}
55      * @private
56      */
57     signoutAllowed_: true,
58
59     /**
60      * Keeps track of whether |onShowHomeButtonChanged_| has been called. See
61      * |onShowHomeButtonChanged_|.
62      * @type {boolean}
63      * @private
64      */
65     onShowHomeButtonChangedCalled_: false,
66
67     /**
68      * Track if page initialization is complete.  All C++ UI handlers have the
69      * chance to manipulate page content within their InitializePage methods.
70      * This flag is set to true after all initializers have been called.
71      * @type {boolean}
72      * @private
73      */
74     initializationComplete_: false,
75
76     /**
77      * When a section is waiting to change its height, this will be a number.
78      * Otherwise it'll be null.
79      * @type {?number}
80      * @private
81      */
82     sectionHeightChangeTimeout_: null,
83
84     /** @override */
85     initializePage: function() {
86       Page.prototype.initializePage.call(this);
87       var self = this;
88
89       if (window.top != window) {
90         // The options page is not in its own window.
91         document.body.classList.add('uber-frame');
92         PageManager.horizontalOffset = 155;
93       }
94
95       // Ensure that navigation events are unblocked on uber page. A reload of
96       // the settings page while an overlay is open would otherwise leave uber
97       // page in a blocked state, where tab switching is not possible.
98       uber.invokeMethodOnParent('stopInterceptingEvents');
99
100       window.addEventListener('message', this.handleWindowMessage_.bind(this));
101
102       if (loadTimeData.getBoolean('allowAdvancedSettings')) {
103         $('advanced-settings-expander').onclick = function() {
104           var showAdvanced =
105               BrowserOptions.shouldShowSection_($('advanced-settings'));
106           if (showAdvanced) {
107             chrome.send('coreOptionsUserMetricsAction',
108                         ['Options_ShowAdvancedSettings']);
109           }
110           self.toggleSectionWithAnimation_(
111               $('advanced-settings'),
112               $('advanced-settings-container'));
113
114           // If the link was focused (i.e., it was activated using the keyboard)
115           // and it was used to show the section (rather than hiding it), focus
116           // the first element in the container.
117           if (document.activeElement === $('advanced-settings-expander') &&
118               showAdvanced) {
119             var focusElement = $('advanced-settings-container').querySelector(
120                 'button, input, list, select, a[href]');
121             if (focusElement)
122               focusElement.focus();
123           }
124         };
125       } else {
126         $('advanced-settings-expander').hidden = true;
127         $('advanced-settings').hidden = true;
128       }
129
130       $('advanced-settings').addEventListener('webkitTransitionEnd',
131           this.updateAdvancedSettingsExpander_.bind(this));
132
133       if (cr.isChromeOS && loadTimeData.getBoolean('showVersion')) {
134         $('version-button').hidden = false;
135         $('version-button').addEventListener('click', function() {
136           PageManager.showPageByName('help');
137           chrome.send('coreOptionsUserMetricsAction',
138                       ['Options_About']);
139         });
140       }
141
142       if (cr.isChromeOS) {
143         UIAccountTweaks.applyGuestSessionVisibility(document);
144         UIAccountTweaks.applyPublicSessionVisibility(document);
145         if (loadTimeData.getBoolean('secondaryUser'))
146           $('secondary-user-banner').hidden = false;
147       }
148
149       // Sync (Sign in) section.
150       this.updateSyncState_(loadTimeData.getValue('syncData'));
151
152       $('start-stop-sync').onclick = function(event) {
153         if (self.signedIn_) {
154           if (self.signoutAllowed_)
155             SyncSetupOverlay.showStopSyncingUI();
156           else
157             chrome.send('showDisconnectManagedProfileDialog');
158         } else if (cr.isChromeOS) {
159           SyncSetupOverlay.showSetupUI();
160         } else {
161           SyncSetupOverlay.startSignIn();
162         }
163       };
164       $('customize-sync').onclick = function(event) {
165         SyncSetupOverlay.showSetupUI();
166       };
167
168       // Internet connection section (ChromeOS only).
169       if (cr.isChromeOS) {
170         options.network.NetworkList.decorate($('network-list'));
171         // Show that the network settings are shared if this is a secondary user
172         // in a multi-profile session.
173         if (loadTimeData.getBoolean('secondaryUser')) {
174           var networkIndicator = document.querySelector(
175               '#network-section-header > .controlled-setting-indicator');
176           networkIndicator.setAttribute('controlled-by', 'shared');
177           networkIndicator.location = cr.ui.ArrowLocation.TOP_START;
178         }
179         options.network.NetworkList.refreshNetworkData(
180             loadTimeData.getValue('networkData'));
181       }
182
183       // On Startup section.
184       Preferences.getInstance().addEventListener('session.restore_on_startup',
185           this.onRestoreOnStartupChanged_.bind(this));
186       Preferences.getInstance().addEventListener(
187           'session.startup_urls',
188           function(event) {
189             $('startup-set-pages').disabled = event.value.disabled;
190           });
191
192       $('startup-set-pages').onclick = function() {
193         PageManager.showPageByName('startup');
194       };
195
196       // Appearance section.
197       Preferences.getInstance().addEventListener('browser.show_home_button',
198           this.onShowHomeButtonChanged_.bind(this));
199
200       Preferences.getInstance().addEventListener('homepage',
201           this.onHomePageChanged_.bind(this));
202       Preferences.getInstance().addEventListener('homepage_is_newtabpage',
203           this.onHomePageIsNtpChanged_.bind(this));
204
205       $('change-home-page').onclick = function(event) {
206         PageManager.showPageByName('homePageOverlay');
207         chrome.send('coreOptionsUserMetricsAction',
208                     ['Options_Homepage_ShowSettings']);
209       };
210
211       var hotwordIndicator = $('hotword-search-setting-indicator');
212       HotwordSearchSettingIndicator.decorate(hotwordIndicator);
213       hotwordIndicator.disabledOnErrorSection = $('hotword-search-enable');
214       chrome.send('requestHotwordAvailable');
215
216       if ($('set-wallpaper')) {
217         $('set-wallpaper').onclick = function(event) {
218           chrome.send('openWallpaperManager');
219           chrome.send('coreOptionsUserMetricsAction',
220                       ['Options_OpenWallpaperManager']);
221         };
222       }
223
224       $('themes-gallery').onclick = function(event) {
225         window.open(loadTimeData.getString('themesGalleryURL'));
226         chrome.send('coreOptionsUserMetricsAction',
227                     ['Options_ThemesGallery']);
228       };
229       $('themes-reset').onclick = function(event) {
230         chrome.send('themesReset');
231       };
232
233       if (loadTimeData.getBoolean('profileIsSupervised')) {
234         if ($('themes-native-button')) {
235           $('themes-native-button').disabled = true;
236           $('themes-native-button').hidden = true;
237         }
238         // Supervised users have just one default theme, even on Linux. So use
239         // the same button for Linux as for the other platforms.
240         $('themes-reset').textContent = loadTimeData.getString('themesReset');
241       }
242
243       // Device section (ChromeOS only).
244       if (cr.isChromeOS) {
245         $('keyboard-settings-button').onclick = function(evt) {
246           PageManager.showPageByName('keyboard-overlay');
247           chrome.send('coreOptionsUserMetricsAction',
248                       ['Options_ShowKeyboardSettings']);
249         };
250         $('pointer-settings-button').onclick = function(evt) {
251           PageManager.showPageByName('pointer-overlay');
252           chrome.send('coreOptionsUserMetricsAction',
253                       ['Options_ShowTouchpadSettings']);
254         };
255       }
256
257       // Search section.
258       $('manage-default-search-engines').onclick = function(event) {
259         PageManager.showPageByName('searchEngines');
260         chrome.send('coreOptionsUserMetricsAction',
261                     ['Options_ManageSearchEngines']);
262       };
263       $('default-search-engine').addEventListener('change',
264           this.setDefaultSearchEngine_);
265
266       // Users section.
267       if (loadTimeData.valueExists('profilesInfo')) {
268         $('profiles-section').hidden = false;
269         this.maybeShowUserSection_();
270
271         var profilesList = $('profiles-list');
272         options.browser_options.ProfileList.decorate(profilesList);
273         profilesList.autoExpands = true;
274
275         // The profiles info data in |loadTimeData| might be stale.
276         this.setProfilesInfo_(loadTimeData.getValue('profilesInfo'));
277         chrome.send('requestProfilesInfo');
278
279         profilesList.addEventListener('change',
280             this.setProfileViewButtonsStatus_);
281         $('profiles-create').onclick = function(event) {
282           ManageProfileOverlay.showCreateDialog();
283         };
284         if (OptionsPage.isSettingsApp()) {
285           $('profiles-app-list-switch').onclick = function(event) {
286             var selectedProfile = self.getSelectedProfileItem_();
287             chrome.send('switchAppListProfile', [selectedProfile.filePath]);
288           };
289         }
290         $('profiles-manage').onclick = function(event) {
291           ManageProfileOverlay.showManageDialog();
292         };
293         $('profiles-delete').onclick = function(event) {
294           var selectedProfile = self.getSelectedProfileItem_();
295           if (selectedProfile)
296             ManageProfileOverlay.showDeleteDialog(selectedProfile);
297         };
298         if (loadTimeData.getBoolean('profileIsSupervised')) {
299           $('profiles-create').disabled = true;
300           $('profiles-delete').disabled = true;
301           $('profiles-list').canDeleteItems = false;
302         }
303       }
304
305       if (cr.isChromeOS) {
306         // Username (canonical email) of the currently logged in user or
307         // |kGuestUser| if a guest session is active.
308         this.username_ = loadTimeData.getString('username');
309
310         this.updateAccountPicture_();
311
312         $('account-picture').onclick = this.showImagerPickerOverlay_;
313         $('change-picture-caption').onclick = this.showImagerPickerOverlay_;
314
315         $('manage-accounts-button').onclick = function(event) {
316           PageManager.showPageByName('accounts');
317           chrome.send('coreOptionsUserMetricsAction',
318               ['Options_ManageAccounts']);
319         };
320
321         document.querySelector(
322             '#enable-screen-lock + span > .controlled-setting-indicator').
323             setAttribute('textshared',
324                          loadTimeData.getString('screenLockShared'));
325       } else {
326         $('import-data').onclick = function(event) {
327           ImportDataOverlay.show();
328           chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
329         };
330
331         if ($('themes-native-button')) {
332           $('themes-native-button').onclick = function(event) {
333             chrome.send('themesSetNative');
334           };
335         }
336       }
337
338       // Date and time section (CrOS only).
339       if ($('set-time-button'))
340         $('set-time-button').onclick = this.handleSetTime_.bind(this);
341
342       // Default browser section.
343       if (!cr.isChromeOS) {
344         if (!loadTimeData.getBoolean('showSetDefault')) {
345           $('set-default-browser-section').hidden = true;
346         }
347         $('set-as-default-browser').onclick = function(event) {
348           chrome.send('becomeDefaultBrowser');
349         };
350
351         $('auto-launch').onclick = this.handleAutoLaunchChanged_;
352       }
353
354       // Privacy section.
355       $('privacyContentSettingsButton').onclick = function(event) {
356         PageManager.showPageByName('content');
357         OptionsPage.showTab($('cookies-nav-tab'));
358         chrome.send('coreOptionsUserMetricsAction',
359             ['Options_ContentSettings']);
360       };
361       $('privacyClearDataButton').onclick = function(event) {
362         PageManager.showPageByName('clearBrowserData');
363         chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
364       };
365       $('privacyClearDataButton').hidden = OptionsPage.isSettingsApp();
366       // 'metricsReportingEnabled' element is only present on Chrome branded
367       // builds, and the 'metricsReportingCheckboxAction' message is only
368       // handled on ChromeOS.
369       if ($('metricsReportingEnabled') && cr.isChromeOS) {
370         $('metricsReportingEnabled').onclick = function(event) {
371           chrome.send('metricsReportingCheckboxAction',
372               [String(event.currentTarget.checked)]);
373         };
374       }
375       if ($('metricsReportingEnabled') && !cr.isChromeOS) {
376         // The localized string has the | symbol on each side of the text that
377         // needs to be made into a button to restart Chrome. We parse the text
378         // and build the button from that.
379         var restartTextFragments =
380             loadTimeData.getString('metricsReportingResetRestart').split('|');
381         // Assume structure is something like "starting text |link text| ending
382         // text" where both starting text and ending text may or may not be
383         // present, but the split should always be in three pieces.
384         var restartElements =
385             $('metrics-reporting-reset-restart').querySelectorAll('*');
386         for (var i = 0; i < restartTextFragments.length; i++) {
387           restartElements[i].textContent = restartTextFragments[i];
388         }
389         restartElements[1].onclick = function(event) {
390           chrome.send('restartBrowser');
391         };
392         var updateMetricsRestartButton = function() {
393           $('metrics-reporting-reset-restart').hidden =
394               loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
395                   $('metricsReportingEnabled').checked;
396         };
397         Preferences.getInstance().addEventListener(
398             $('metricsReportingEnabled').getAttribute('pref'),
399             updateMetricsRestartButton);
400         updateMetricsRestartButton();
401       }
402       $('networkPredictionOptions').onchange = function(event) {
403         var value = (event.target.checked ?
404             NetworkPredictionOptions.WIFI_ONLY :
405             NetworkPredictionOptions.NEVER);
406         var metric = event.target.metric;
407         Preferences.setIntegerPref(
408             'net.network_prediction_options',
409             value,
410             true,
411             metric);
412       };
413
414       // Bluetooth (CrOS only).
415       if (cr.isChromeOS) {
416         options.system.bluetooth.BluetoothDeviceList.decorate(
417             $('bluetooth-paired-devices-list'));
418
419         $('bluetooth-add-device').onclick =
420             this.handleAddBluetoothDevice_.bind(this);
421
422         $('enable-bluetooth').onchange = function(event) {
423           var state = $('enable-bluetooth').checked;
424           chrome.send('bluetoothEnableChange', [Boolean(state)]);
425         };
426
427         $('bluetooth-reconnect-device').onclick = function(event) {
428           var device = $('bluetooth-paired-devices-list').selectedItem;
429           var address = device.address;
430           chrome.send('updateBluetoothDevice', [address, 'connect']);
431           PageManager.closeOverlay();
432         };
433
434         $('bluetooth-paired-devices-list').addEventListener('change',
435             function() {
436           var item = $('bluetooth-paired-devices-list').selectedItem;
437           var disabled = !item || item.connected || !item.connectable;
438           $('bluetooth-reconnect-device').disabled = disabled;
439         });
440       }
441
442       // Passwords and Forms section.
443       $('autofill-settings').onclick = function(event) {
444         PageManager.showPageByName('autofill');
445         chrome.send('coreOptionsUserMetricsAction',
446             ['Options_ShowAutofillSettings']);
447       };
448       $('manage-passwords').onclick = function(event) {
449         PageManager.showPageByName('passwords');
450         OptionsPage.showTab($('passwords-nav-tab'));
451         chrome.send('coreOptionsUserMetricsAction',
452             ['Options_ShowPasswordManager']);
453       };
454       if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) {
455         // Disable and turn off Autofill in guest mode.
456         var autofillEnabled = $('autofill-enabled');
457         autofillEnabled.disabled = true;
458         autofillEnabled.checked = false;
459         cr.dispatchSimpleEvent(autofillEnabled, 'change');
460         $('autofill-settings').disabled = true;
461
462         // Disable and turn off Password Manager in guest mode.
463         var passwordManagerEnabled = $('password-manager-enabled');
464         passwordManagerEnabled.disabled = true;
465         passwordManagerEnabled.checked = false;
466         cr.dispatchSimpleEvent(passwordManagerEnabled, 'change');
467         $('manage-passwords').disabled = true;
468       }
469
470       if (cr.isMac) {
471         $('mac-passwords-warning').hidden =
472             !loadTimeData.getBoolean('multiple_profiles');
473       }
474
475       // Network section.
476       if (!cr.isChromeOS) {
477         $('proxiesConfigureButton').onclick = function(event) {
478           chrome.send('showNetworkProxySettings');
479         };
480       }
481
482       // Device control section.
483       if (cr.isChromeOS &&
484           UIAccountTweaks.currentUserIsOwner() &&
485           loadTimeData.getBoolean('consumerManagementEnabled')) {
486         $('device-control-section').hidden = false;
487
488         var isEnrolled = loadTimeData.getBoolean('consumerManagementEnrolled');
489         $('consumer-management-enroll').hidden = isEnrolled;
490         $('consumer-management-unenroll').hidden = !isEnrolled;
491
492         $('consumer-management-section').onclick = function(event) {
493           // If either button is clicked.
494           if (event.target.tagName == 'BUTTON')
495             PageManager.showPageByName('consumer-management-overlay');
496         };
497       }
498
499       // Easy Unlock section.
500       if (loadTimeData.getBoolean('easyUnlockAllowed')) {
501         $('easy-unlock-section').hidden = false;
502         $('easy-unlock-setup-button').onclick = function(event) {
503           chrome.send('launchEasyUnlockSetup');
504         };
505         $('easy-unlock-turn-off-button').onclick = function(event) {
506           PageManager.showPageByName('easyUnlockTurnOffOverlay');
507         };
508       }
509
510       // Website Settings section.
511       if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
512         $('website-settings-section').hidden = false;
513         $('website-management-button').onclick = function(event) {
514           PageManager.showPageByName('websiteSettings');
515         };
516       }
517
518       // Web Content section.
519       $('fontSettingsCustomizeFontsButton').onclick = function(event) {
520         PageManager.showPageByName('fonts');
521         chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
522       };
523       $('defaultFontSize').onchange = function(event) {
524         var value = event.target.options[event.target.selectedIndex].value;
525         Preferences.setIntegerPref(
526              'webkit.webprefs.default_fixed_font_size',
527              value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
528         chrome.send('defaultFontSizeAction', [String(value)]);
529       };
530       $('defaultZoomFactor').onchange = function(event) {
531         chrome.send('defaultZoomFactorAction',
532             [String(event.target.options[event.target.selectedIndex].value)]);
533       };
534
535       // Languages section.
536       var showLanguageOptions = function(event) {
537         PageManager.showPageByName('languages');
538         chrome.send('coreOptionsUserMetricsAction',
539             ['Options_LanuageAndSpellCheckSettings']);
540       };
541       $('language-button').onclick = showLanguageOptions;
542       $('manage-languages').onclick = showLanguageOptions;
543
544       // Downloads section.
545       Preferences.getInstance().addEventListener('download.default_directory',
546           this.onDefaultDownloadDirectoryChanged_.bind(this));
547       $('downloadLocationChangeButton').onclick = function(event) {
548         chrome.send('selectDownloadLocation');
549       };
550       if (cr.isChromeOS) {
551         $('disable-drive-row').hidden =
552             UIAccountTweaks.loggedInAsSupervisedUser();
553       }
554       $('autoOpenFileTypesResetToDefault').onclick = function(event) {
555         chrome.send('autoOpenFileTypesAction');
556       };
557
558       // HTTPS/SSL section.
559       if (cr.isWindows || cr.isMac) {
560         $('certificatesManageButton').onclick = function(event) {
561           chrome.send('showManageSSLCertificates');
562         };
563       } else {
564         $('certificatesManageButton').onclick = function(event) {
565           PageManager.showPageByName('certificates');
566           chrome.send('coreOptionsUserMetricsAction',
567                       ['Options_ManageSSLCertificates']);
568         };
569       }
570
571       if (loadTimeData.getBoolean('cloudPrintShowMDnsOptions')) {
572         $('cloudprint-options-mdns').hidden = false;
573         $('cloudPrintDevicesPageButton').onclick = function() {
574           chrome.send('showCloudPrintDevicesPage');
575         };
576       }
577
578       // Accessibility section (CrOS only).
579       if (cr.isChromeOS) {
580         var updateAccessibilitySettingsButton = function() {
581           $('accessibility-settings').hidden =
582               !($('accessibility-spoken-feedback-check').checked);
583         };
584         Preferences.getInstance().addEventListener(
585             'settings.accessibility',
586             updateAccessibilitySettingsButton);
587         $('accessibility-learn-more').onclick = function(unused_event) {
588           window.open(loadTimeData.getString('accessibilityLearnMoreURL'));
589           chrome.send('coreOptionsUserMetricsAction',
590                       ['Options_AccessibilityLearnMore']);
591         };
592         $('accessibility-settings-button').onclick = function(unused_event) {
593           window.open(loadTimeData.getString('accessibilitySettingsURL'));
594         };
595         $('accessibility-spoken-feedback-check').onchange = function(
596             unused_event) {
597           chrome.send('spokenFeedbackChange',
598                       [$('accessibility-spoken-feedback-check').checked]);
599           updateAccessibilitySettingsButton();
600         };
601         updateAccessibilitySettingsButton();
602
603         $('accessibility-high-contrast-check').onchange = function(
604             unused_event) {
605           chrome.send('highContrastChange',
606                       [$('accessibility-high-contrast-check').checked]);
607         };
608
609         var updateDelayDropdown = function() {
610           $('accessibility-autoclick-dropdown').disabled =
611               !$('accessibility-autoclick-check').checked;
612         };
613         Preferences.getInstance().addEventListener(
614             $('accessibility-autoclick-check').getAttribute('pref'),
615             updateDelayDropdown);
616       }
617
618       // Display management section (CrOS only).
619       if (cr.isChromeOS) {
620         $('display-options').onclick = function(event) {
621           PageManager.showPageByName('display');
622           chrome.send('coreOptionsUserMetricsAction',
623                       ['Options_Display']);
624         };
625       }
626
627       // Factory reset section (CrOS only).
628       if (cr.isChromeOS) {
629         $('factory-reset-restart').onclick = function(event) {
630           PageManager.showPageByName('factoryResetData');
631           chrome.send('onPowerwashDialogShow');
632         };
633       }
634
635       // System section.
636       if (!cr.isChromeOS) {
637         var updateGpuRestartButton = function() {
638           $('gpu-mode-reset-restart').hidden =
639               loadTimeData.getBoolean('gpuEnabledAtStart') ==
640               $('gpu-mode-checkbox').checked;
641         };
642         Preferences.getInstance().addEventListener(
643             $('gpu-mode-checkbox').getAttribute('pref'),
644             updateGpuRestartButton);
645         $('gpu-mode-reset-restart-button').onclick = function(event) {
646           chrome.send('restartBrowser');
647         };
648         updateGpuRestartButton();
649       }
650
651       // Reset profile settings section.
652       $('reset-profile-settings').onclick = function(event) {
653         PageManager.showPageByName('resetProfileSettings');
654       };
655
656       // Extension controlled UI.
657       this.addExtensionControlledBox_('search-section-content',
658                                       'search-engine-controlled',
659                                       true);
660       this.addExtensionControlledBox_('extension-controlled-container',
661                                       'homepage-controlled',
662                                       true);
663       this.addExtensionControlledBox_('startup-section-content',
664                                       'startpage-controlled',
665                                       false);
666       this.addExtensionControlledBox_('newtab-section-content',
667                                       'newtab-controlled',
668                                       false);
669       this.addExtensionControlledBox_('proxy-section-content',
670                                       'proxy-controlled',
671                                       true);
672
673       document.body.addEventListener('click', function(e) {
674         var button = findAncestor(e.target, function(el) {
675           return el.tagName == 'BUTTON' &&
676                  el.dataset.extensionId !== undefined &&
677                  el.dataset.extensionId.length;
678         });
679         if (button)
680           chrome.send('disableExtension', [button.dataset.extensionId]);
681       });
682     },
683
684     /** @override */
685     didShowPage: function() {
686       $('search-field').focus();
687     },
688
689    /**
690     * Called after all C++ UI handlers have called InitializePage to notify
691     * that initialization is complete.
692     * @private
693     */
694     notifyInitializationComplete_: function() {
695       this.initializationComplete_ = true;
696       cr.dispatchSimpleEvent(document, 'initializationComplete');
697     },
698
699     /**
700      * Event listener for the 'session.restore_on_startup' pref.
701      * @param {Event} event The preference change event.
702      * @private
703      */
704     onRestoreOnStartupChanged_: function(event) {
705       /** @const */ var showHomePageValue = 0;
706
707       if (event.value.value == showHomePageValue) {
708         // If the user previously selected "Show the homepage", the
709         // preference will already be migrated to "Open a specific page". So
710         // the only way to reach this code is if the 'restore on startup'
711         // preference is managed.
712         assert(event.value.controlledBy);
713
714         // Select "open the following pages" and lock down the list of URLs
715         // to reflect the intention of the policy.
716         $('startup-show-pages').checked = true;
717         StartupOverlay.getInstance().setControlsDisabled(true);
718       } else {
719         // Re-enable the controls in the startup overlay if necessary.
720         StartupOverlay.getInstance().updateControlStates();
721       }
722     },
723
724     /**
725      * Handler for messages sent from the main uber page.
726      * @param {Event} e The 'message' event from the uber page.
727      * @private
728      */
729     handleWindowMessage_: function(e) {
730       if (e.data.method == 'frameSelected')
731         $('search-field').focus();
732     },
733
734     /**
735      * Animatedly changes height |from| a px number |to| a px number.
736      * @param {HTMLElement} section The section to animate.
737      * @param {HTMLElement} container The container of |section|.
738      * @param {boolean} showing Whether to go from 0 -> container height or
739      *     container height -> 0.
740      * @private
741      */
742     animatedSectionHeightChange_: function(section, container, showing) {
743       // If the section is already animating, dispatch a synthetic transition
744       // end event as the upcoming code will cancel the current one.
745       if (section.classList.contains('sliding'))
746         cr.dispatchSimpleEvent(section, 'webkitTransitionEnd');
747
748       this.addTransitionEndListener_(section);
749
750       section.hidden = false;
751       section.style.height = (showing ? 0 : container.offsetHeight) + 'px';
752       section.classList.add('sliding');
753
754       if (this.sectionHeightChangeTimeout_ !== null)
755         clearTimeout(this.sectionHeightChangeTimeout_);
756
757       this.sectionHeightChangeTimeout_ = setTimeout(function() {
758         section.style.height = (showing ? container.offsetHeight : 0) + 'px';
759         this.sectionHeightChangeTimeout_ = null;
760       });
761     },
762
763     /**
764      * Shows the given section.
765      * @param {HTMLElement} section The section to be shown.
766      * @param {HTMLElement} container The container for the section. Must be
767      *     inside of |section|.
768      * @param {boolean} animate Indicate if the expansion should be animated.
769      * @private
770      */
771     showSection_: function(section, container, animate) {
772       // Delay starting the transition if animating so that hidden change will
773       // be processed.
774       if (animate) {
775         this.animatedSectionHeightChange_(section, container, true);
776       } else {
777         section.hidden = false;
778         section.style.height = 'auto';
779       }
780     },
781
782     /**
783      * Shows the given section, with animation.
784      * @param {HTMLElement} section The section to be shown.
785      * @param {HTMLElement} container The container for the section. Must be
786      *     inside of |section|.
787      * @private
788      */
789     showSectionWithAnimation_: function(section, container) {
790       this.showSection_(section, container, /* animate */ true);
791     },
792
793     /**
794      * Hides the given |section| with animation.
795      * @param {HTMLElement} section The section to be hidden.
796      * @param {HTMLElement} container The container for the section. Must be
797      *     inside of |section|.
798      * @private
799      */
800     hideSectionWithAnimation_: function(section, container) {
801       this.animatedSectionHeightChange_(section, container, false);
802     },
803
804     /**
805      * Toggles the visibility of |section| in an animated way.
806      * @param {HTMLElement} section The section to be toggled.
807      * @param {HTMLElement} container The container for the section. Must be
808      *     inside of |section|.
809      * @private
810      */
811     toggleSectionWithAnimation_: function(section, container) {
812       if (BrowserOptions.shouldShowSection_(section))
813         this.showSectionWithAnimation_(section, container);
814       else
815         this.hideSectionWithAnimation_(section, container);
816     },
817
818     /**
819      * Scrolls the settings page to make the section visible auto-expanding
820      * advanced settings if required.  The transition is not animated.  This
821      * method is used to ensure that a section associated with an overlay
822      * is visible when the overlay is closed.
823      * @param {!Element} section  The section to make visible.
824      * @private
825      */
826     scrollToSection_: function(section) {
827       var advancedSettings = $('advanced-settings');
828       var container = $('advanced-settings-container');
829       var expander = $('advanced-settings-expander');
830       if (!expander.hidden &&
831           advancedSettings.hidden &&
832           section.parentNode == container) {
833         this.showSection_($('advanced-settings'),
834                           $('advanced-settings-container'),
835                           /* animate */ false);
836         this.updateAdvancedSettingsExpander_();
837       }
838
839       if (!this.initializationComplete_) {
840         var self = this;
841         var callback = function() {
842            document.removeEventListener('initializationComplete', callback);
843            self.scrollToSection_(section);
844         };
845         document.addEventListener('initializationComplete', callback);
846         return;
847       }
848
849       var pageContainer = $('page-container');
850       // pageContainer.offsetTop is relative to the screen.
851       var pageTop = pageContainer.offsetTop;
852       var sectionBottom = section.offsetTop + section.offsetHeight;
853       // section.offsetTop is relative to the 'page-container'.
854       var sectionTop = section.offsetTop;
855       if (pageTop + sectionBottom > document.body.scrollHeight ||
856           pageTop + sectionTop < 0) {
857         // Currently not all layout updates are guaranteed to precede the
858         // initializationComplete event (for example 'set-as-default-browser'
859         // button) leaving some uncertainty in the optimal scroll position.
860         // The section is placed approximately in the middle of the screen.
861         var top = Math.min(0, document.body.scrollHeight / 2 - sectionBottom);
862         pageContainer.style.top = top + 'px';
863         pageContainer.oldScrollTop = -top;
864       }
865     },
866
867     /**
868      * Adds a |webkitTransitionEnd| listener to the given section so that
869      * it can be animated. The listener will only be added to a given section
870      * once, so this can be called as multiple times.
871      * @param {HTMLElement} section The section to be animated.
872      * @private
873      */
874     addTransitionEndListener_: function(section) {
875       if (section.hasTransitionEndListener_)
876         return;
877
878       section.addEventListener('webkitTransitionEnd',
879           this.onTransitionEnd_.bind(this));
880       section.hasTransitionEndListener_ = true;
881     },
882
883     /**
884      * Called after an animation transition has ended.
885      * @param {Event} The webkitTransitionEnd event. NOTE: May be synthetic.
886      * @private
887      */
888     onTransitionEnd_: function(event) {
889       if (event.propertyName && event.propertyName != 'height') {
890         // If not a synthetic event or a real transition we care about, bail.
891         return;
892       }
893
894       var section = event.target;
895       section.classList.remove('sliding');
896
897       if (!event.propertyName) {
898         // Only real transitions past this point.
899         return;
900       }
901
902       if (section.style.height == '0px') {
903         // Hide the content so it can't get tab focus.
904         section.hidden = true;
905         section.style.height = '';
906       } else {
907         // Set the section height to 'auto' to allow for size changes
908         // (due to font change or dynamic content).
909         section.style.height = 'auto';
910       }
911     },
912
913     /** @private */
914     updateAdvancedSettingsExpander_: function() {
915       var expander = $('advanced-settings-expander');
916       if (BrowserOptions.shouldShowSection_($('advanced-settings')))
917         expander.textContent = loadTimeData.getString('showAdvancedSettings');
918       else
919         expander.textContent = loadTimeData.getString('hideAdvancedSettings');
920     },
921
922     /**
923      * Updates the sync section with the given state.
924      * @param {Object} syncData A bunch of data records that describe the status
925      *     of the sync system.
926      * @private
927      */
928     updateSyncState_: function(syncData) {
929       if (!syncData.signinAllowed &&
930           (!syncData.supervisedUser || !cr.isChromeOS)) {
931         $('sync-section').hidden = true;
932         this.maybeShowUserSection_();
933         return;
934       }
935
936       $('sync-section').hidden = false;
937       this.maybeShowUserSection_();
938
939       if (cr.isChromeOS && syncData.supervisedUser) {
940         var subSection = $('sync-section').firstChild;
941         while (subSection) {
942           if (subSection.nodeType == Node.ELEMENT_NODE)
943             subSection.hidden = true;
944           subSection = subSection.nextSibling;
945         }
946
947         $('account-picture-wrapper').hidden = false;
948         $('sync-general').hidden = false;
949         $('sync-status').hidden = true;
950
951         return;
952       }
953
954       // If the user gets signed out while the advanced sync settings dialog is
955       // visible, say, due to a dashboard clear, close the dialog.
956       // However, if the user gets signed out as a result of abandoning first
957       // time sync setup, do not call closeOverlay as it will redirect the
958       // browser to the main settings page and override any in-progress
959       // user-initiated navigation. See crbug.com/278030.
960       // Note: SyncSetupOverlay.closeOverlay is a no-op if the overlay is
961       // already hidden.
962       if (this.signedIn_ && !syncData.signedIn && !syncData.setupInProgress)
963         SyncSetupOverlay.closeOverlay();
964
965       this.signedIn_ = syncData.signedIn;
966
967       // Display the "advanced settings" button if we're signed in and sync is
968       // not managed/disabled. If the user is signed in, but sync is disabled,
969       // this button is used to re-enable sync.
970       var customizeSyncButton = $('customize-sync');
971       customizeSyncButton.hidden = !this.signedIn_ ||
972                                    syncData.managed ||
973                                    !syncData.syncSystemEnabled;
974
975       // Only modify the customize button's text if the new text is different.
976       // Otherwise, it can affect search-highlighting in the settings page.
977       // See http://crbug.com/268265.
978       var customizeSyncButtonNewText = syncData.setupCompleted ?
979           loadTimeData.getString('customizeSync') :
980           loadTimeData.getString('syncButtonTextStart');
981       if (customizeSyncButton.textContent != customizeSyncButtonNewText)
982         customizeSyncButton.textContent = customizeSyncButtonNewText;
983
984       // Disable the "sign in" button if we're currently signing in, or if we're
985       // already signed in and signout is not allowed.
986       var signInButton = $('start-stop-sync');
987       signInButton.disabled = syncData.setupInProgress;
988       this.signoutAllowed_ = syncData.signoutAllowed;
989       if (!syncData.signoutAllowed)
990         $('start-stop-sync-indicator').setAttribute('controlled-by', 'policy');
991       else
992         $('start-stop-sync-indicator').removeAttribute('controlled-by');
993
994       // Hide the "sign in" button on Chrome OS, and show it on desktop Chrome
995       // (except for supervised users, which can't change their signed-in
996       // status).
997       signInButton.hidden = cr.isChromeOS || syncData.supervisedUser;
998
999       signInButton.textContent =
1000           this.signedIn_ ?
1001               loadTimeData.getString('syncButtonTextStop') :
1002               syncData.setupInProgress ?
1003                   loadTimeData.getString('syncButtonTextInProgress') :
1004                   loadTimeData.getString('syncButtonTextSignIn');
1005       $('start-stop-sync-indicator').hidden = signInButton.hidden;
1006
1007       // TODO(estade): can this just be textContent?
1008       $('sync-status-text').innerHTML = syncData.statusText;
1009       var statusSet = syncData.statusText.length != 0;
1010       $('sync-overview').hidden =
1011           statusSet ||
1012           (cr.isChromeOS && UIAccountTweaks.loggedInAsPublicAccount());
1013       $('sync-status').hidden = !statusSet;
1014
1015       $('sync-action-link').textContent = syncData.actionLinkText;
1016       // Don't show the action link if it is empty or undefined.
1017       $('sync-action-link').hidden = syncData.actionLinkText.length == 0;
1018       $('sync-action-link').disabled = syncData.managed ||
1019                                        !syncData.syncSystemEnabled;
1020
1021       // On Chrome OS, sign out the user and sign in again to get fresh
1022       // credentials on auth errors.
1023       $('sync-action-link').onclick = function(event) {
1024         if (cr.isChromeOS && syncData.hasError)
1025           SyncSetupOverlay.doSignOutOnAuthError();
1026         else
1027           SyncSetupOverlay.showSetupUI();
1028       };
1029
1030       if (syncData.hasError)
1031         $('sync-status').classList.add('sync-error');
1032       else
1033         $('sync-status').classList.remove('sync-error');
1034
1035       // Disable the "customize / set up sync" button if sync has an
1036       // unrecoverable error. Also disable the button if sync has not been set
1037       // up and the user is being presented with a link to re-auth.
1038       // See crbug.com/289791.
1039       customizeSyncButton.disabled =
1040           syncData.hasUnrecoverableError ||
1041           (!syncData.setupCompleted && !$('sync-action-link').hidden);
1042     },
1043
1044     /**
1045      * Update the UI depending on whether the current profile has a pairing for
1046      * Easy Unlock.
1047      * @param {boolean} hasPairing True if the current profile has a pairing.
1048      */
1049     updateEasyUnlock_: function(hasPairing) {
1050       $('easy-unlock-setup').hidden = hasPairing;
1051       $('easy-unlock-enable').hidden = !hasPairing;
1052       if (!hasPairing && EasyUnlockTurnOffOverlay.getInstance().visible) {
1053         EasyUnlockTurnOffOverlay.dismiss();
1054       }
1055     },
1056
1057     /**
1058      * Update the UI depending on whether the current profile manages any
1059      * supervised users.
1060      * @param {boolean} show True if the current profile manages any supervised
1061      *     users.
1062      */
1063     updateManagesSupervisedUsers_: function(show) {
1064       $('profiles-supervised-dashboard-tip').hidden = !show;
1065       this.maybeShowUserSection_();
1066     },
1067
1068     /**
1069      * Get the start/stop sync button DOM element. Used for testing.
1070      * @return {DOMElement} The start/stop sync button.
1071      * @private
1072      */
1073     getStartStopSyncButton_: function() {
1074       return $('start-stop-sync');
1075     },
1076
1077     /**
1078      * Event listener for the 'show home button' preference. Shows/hides the
1079      * UI for changing the home page with animation, unless this is the first
1080      * time this function is called, in which case there is no animation.
1081      * @param {Event} event The preference change event.
1082      */
1083     onShowHomeButtonChanged_: function(event) {
1084       var section = $('change-home-page-section');
1085       if (this.onShowHomeButtonChangedCalled_) {
1086         var container = $('change-home-page-section-container');
1087         if (event.value.value)
1088           this.showSectionWithAnimation_(section, container);
1089         else
1090           this.hideSectionWithAnimation_(section, container);
1091       } else {
1092         section.hidden = !event.value.value;
1093         this.onShowHomeButtonChangedCalled_ = true;
1094       }
1095     },
1096
1097     /**
1098      * Activates the Hotword section from the System settings page.
1099      * @param {boolean} opt_enabled Current preference state for hotwording.
1100      * @param {string} opt_error The error message to display.
1101      * @private
1102      */
1103     showHotwordSection_: function(opt_enabled, opt_error) {
1104       $('hotword-search').hidden = false;
1105       $('hotword-search-setting-indicator').setError(opt_error);
1106       if (opt_enabled && opt_error)
1107         $('hotword-search-setting-indicator').updateBasedOnError();
1108     },
1109
1110     /**
1111      * Activates the Audio History and Always-On Hotword sections from the
1112      * System settings page.
1113      * @private
1114      */
1115     showHotwordAlwaysOnSection_: function() {
1116       $('voice-section-title').hidden = false;
1117       $('hotword-always-on-search').hidden = false;
1118       $('audio-logging').hidden = false;
1119     },
1120
1121     /**
1122      * Event listener for the 'homepage is NTP' preference. Updates the label
1123      * next to the 'Change' button.
1124      * @param {Event} event The preference change event.
1125      */
1126     onHomePageIsNtpChanged_: function(event) {
1127       if (!event.value.uncommitted) {
1128         $('home-page-url').hidden = event.value.value;
1129         $('home-page-ntp').hidden = !event.value.value;
1130       }
1131     },
1132
1133     /**
1134      * Event listener for changes to the homepage preference. Updates the label
1135      * next to the 'Change' button.
1136      * @param {Event} event The preference change event.
1137      */
1138     onHomePageChanged_: function(event) {
1139       if (!event.value.uncommitted)
1140         $('home-page-url').textContent = this.stripHttp_(event.value.value);
1141     },
1142
1143     /**
1144      * Removes the 'http://' from a URL, like the omnibox does. If the string
1145      * doesn't start with 'http://' it is returned unchanged.
1146      * @param {string} url The url to be processed
1147      * @return {string} The url with the 'http://' removed.
1148      */
1149     stripHttp_: function(url) {
1150       return url.replace(/^http:\/\//, '');
1151     },
1152
1153    /**
1154     * Shows the autoLaunch preference and initializes its checkbox value.
1155     * @param {bool} enabled Whether autolaunch is enabled or or not.
1156     * @private
1157     */
1158     updateAutoLaunchState_: function(enabled) {
1159       $('auto-launch-option').hidden = false;
1160       $('auto-launch').checked = enabled;
1161     },
1162
1163     /**
1164      * Called when the value of the download.default_directory preference
1165      * changes.
1166      * @param {Event} event Change event.
1167      * @private
1168      */
1169     onDefaultDownloadDirectoryChanged_: function(event) {
1170       $('downloadLocationPath').value = event.value.value;
1171       if (cr.isChromeOS) {
1172         // On ChromeOS, replace /special/drive-<hash>/root with "Google Drive"
1173         // for remote files, /home/chronos/user/Downloads or
1174         // /home/chronos/u-<hash>/Downloads with "Downloads" for local paths,
1175         // and '/' with ' \u203a ' (angled quote sign) everywhere. The modified
1176         // path is used only for display purpose.
1177         var path = $('downloadLocationPath').value;
1178         path = path.replace(/^\/special\/drive[^\/]*\/root/, 'Google Drive');
1179         path = path.replace(/^\/home\/chronos\/(user|u-[^\/]*)\//, '');
1180         path = path.replace(/\//g, ' \u203a ');
1181         $('downloadLocationPath').value = path;
1182       }
1183       $('download-location-label').classList.toggle('disabled',
1184                                                     event.value.disabled);
1185       $('downloadLocationChangeButton').disabled = event.value.disabled;
1186     },
1187
1188     /**
1189      * Update the Default Browsers section based on the current state.
1190      * @param {string} statusString Description of the current default state.
1191      * @param {boolean} isDefault Whether or not the browser is currently
1192      *     default.
1193      * @param {boolean} canBeDefault Whether or not the browser can be default.
1194      * @private
1195      */
1196     updateDefaultBrowserState_: function(statusString, isDefault,
1197                                          canBeDefault) {
1198       if (!cr.isChromeOS) {
1199         var label = $('default-browser-state');
1200         label.textContent = statusString;
1201
1202         $('set-as-default-browser').hidden = !canBeDefault || isDefault;
1203       }
1204     },
1205
1206     /**
1207      * Clears the search engine popup.
1208      * @private
1209      */
1210     clearSearchEngines_: function() {
1211       $('default-search-engine').textContent = '';
1212     },
1213
1214     /**
1215      * Updates the search engine popup with the given entries.
1216      * @param {Array} engines List of available search engines.
1217      * @param {number} defaultValue The value of the current default engine.
1218      * @param {boolean} defaultManaged Whether the default search provider is
1219      *     managed. If true, the default search provider can't be changed.
1220      * @private
1221      */
1222     updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
1223       this.clearSearchEngines_();
1224       engineSelect = $('default-search-engine');
1225       engineSelect.disabled = defaultManaged;
1226       if (defaultManaged && defaultValue == -1)
1227         return;
1228       engineCount = engines.length;
1229       var defaultIndex = -1;
1230       for (var i = 0; i < engineCount; i++) {
1231         var engine = engines[i];
1232         var option = new Option(engine.name, engine.index);
1233         if (defaultValue == option.value)
1234           defaultIndex = i;
1235         engineSelect.appendChild(option);
1236       }
1237       if (defaultIndex >= 0)
1238         engineSelect.selectedIndex = defaultIndex;
1239     },
1240
1241     /**
1242      * Set the default search engine based on the popup selection.
1243      * @private
1244      */
1245     setDefaultSearchEngine_: function() {
1246       var engineSelect = $('default-search-engine');
1247       var selectedIndex = engineSelect.selectedIndex;
1248       if (selectedIndex >= 0) {
1249         var selection = engineSelect.options[selectedIndex];
1250         chrome.send('setDefaultSearchEngine', [String(selection.value)]);
1251       }
1252     },
1253
1254    /**
1255      * Sets or clear whether Chrome should Auto-launch on computer startup.
1256      * @private
1257      */
1258     handleAutoLaunchChanged_: function() {
1259       chrome.send('toggleAutoLaunch', [$('auto-launch').checked]);
1260     },
1261
1262     /**
1263      * Get the selected profile item from the profile list. This also works
1264      * correctly if the list is not displayed.
1265      * @return {Object} the profile item object, or null if nothing is selected.
1266      * @private
1267      */
1268     getSelectedProfileItem_: function() {
1269       var profilesList = $('profiles-list');
1270       if (profilesList.hidden) {
1271         if (profilesList.dataModel.length > 0)
1272           return profilesList.dataModel.item(0);
1273       } else {
1274         return profilesList.selectedItem;
1275       }
1276       return null;
1277     },
1278
1279     /**
1280      * Helper function to set the status of profile view buttons to disabled or
1281      * enabled, depending on the number of profiles and selection status of the
1282      * profiles list.
1283      * @private
1284      */
1285     setProfileViewButtonsStatus_: function() {
1286       var profilesList = $('profiles-list');
1287       var selectedProfile = profilesList.selectedItem;
1288       var hasSelection = selectedProfile != null;
1289       var hasSingleProfile = profilesList.dataModel.length == 1;
1290       var isSupervised = loadTimeData.getBoolean('profileIsSupervised');
1291       $('profiles-manage').disabled = !hasSelection ||
1292           !selectedProfile.isCurrentProfile;
1293       if (hasSelection && !selectedProfile.isCurrentProfile)
1294         $('profiles-manage').title = loadTimeData.getString('currentUserOnly');
1295       else
1296         $('profiles-manage').title = '';
1297       $('profiles-delete').disabled = isSupervised ||
1298                                       (!hasSelection && !hasSingleProfile);
1299       if (OptionsPage.isSettingsApp()) {
1300         $('profiles-app-list-switch').disabled = !hasSelection ||
1301             selectedProfile.isCurrentProfile;
1302       }
1303       var importData = $('import-data');
1304       if (importData) {
1305         importData.disabled = $('import-data').disabled = hasSelection &&
1306           !selectedProfile.isCurrentProfile;
1307       }
1308     },
1309
1310     /**
1311      * Display the correct dialog layout, depending on how many profiles are
1312      * available.
1313      * @param {number} numProfiles The number of profiles to display.
1314      * @private
1315      */
1316     setProfileViewSingle_: function(numProfiles) {
1317       // Always show the profiles list when using the new Profiles UI.
1318       var usingNewProfilesUI = loadTimeData.getBoolean('usingNewProfilesUI');
1319       var showSingleProfileView = !usingNewProfilesUI && numProfiles == 1;
1320       $('profiles-list').hidden = showSingleProfileView;
1321       $('profiles-single-message').hidden = !showSingleProfileView;
1322       $('profiles-manage').hidden =
1323           showSingleProfileView || OptionsPage.isSettingsApp();
1324       $('profiles-delete').textContent = showSingleProfileView ?
1325           loadTimeData.getString('profilesDeleteSingle') :
1326           loadTimeData.getString('profilesDelete');
1327       if (OptionsPage.isSettingsApp())
1328         $('profiles-app-list-switch').hidden = showSingleProfileView;
1329     },
1330
1331     /**
1332      * Adds all |profiles| to the list.
1333      * @param {Array.<Object>} profiles An array of profile info objects.
1334      *     each object is of the form:
1335      *       profileInfo = {
1336      *         name: "Profile Name",
1337      *         iconURL: "chrome://path/to/icon/image",
1338      *         filePath: "/path/to/profile/data/on/disk",
1339      *         isCurrentProfile: false,
1340      *         isSupervised: false
1341      *       };
1342      * @private
1343      */
1344     setProfilesInfo_: function(profiles) {
1345       this.setProfileViewSingle_(profiles.length);
1346       // add it to the list, even if the list is hidden so we can access it
1347       // later.
1348       $('profiles-list').dataModel = new ArrayDataModel(profiles);
1349
1350       // Received new data. If showing the "manage" overlay, keep it up to
1351       // date. If showing the "delete" overlay, close it.
1352       if (ManageProfileOverlay.getInstance().visible &&
1353           !$('manage-profile-overlay-manage').hidden) {
1354         ManageProfileOverlay.showManageDialog();
1355       } else {
1356         ManageProfileOverlay.getInstance().visible = false;
1357       }
1358
1359       this.setProfileViewButtonsStatus_();
1360     },
1361
1362     /**
1363      * Reports supervised user import errors to the SupervisedUserImportOverlay.
1364      * @param {string} error The error message to display.
1365      * @private
1366      */
1367     showSupervisedUserImportError_: function(error) {
1368       SupervisedUserImportOverlay.onError(error);
1369     },
1370
1371     /**
1372      * Reports successful importing of a supervised user to
1373      * the SupervisedUserImportOverlay.
1374      * @private
1375      */
1376     showSupervisedUserImportSuccess_: function() {
1377       SupervisedUserImportOverlay.onSuccess();
1378     },
1379
1380     /**
1381      * Reports an error to the "create" overlay during profile creation.
1382      * @param {string} error The error message to display.
1383      * @private
1384      */
1385     showCreateProfileError_: function(error) {
1386       CreateProfileOverlay.onError(error);
1387     },
1388
1389     /**
1390     * Sends a warning message to the "create" overlay during profile creation.
1391     * @param {string} warning The warning message to display.
1392     * @private
1393     */
1394     showCreateProfileWarning_: function(warning) {
1395       CreateProfileOverlay.onWarning(warning);
1396     },
1397
1398     /**
1399     * Reports successful profile creation to the "create" overlay.
1400      * @param {Object} profileInfo An object of the form:
1401      *     profileInfo = {
1402      *       name: "Profile Name",
1403      *       filePath: "/path/to/profile/data/on/disk"
1404      *       isSupervised: (true|false),
1405      *     };
1406     * @private
1407     */
1408     showCreateProfileSuccess_: function(profileInfo) {
1409       CreateProfileOverlay.onSuccess(profileInfo);
1410     },
1411
1412     /**
1413      * Returns the currently active profile for this browser window.
1414      * @return {Object} A profile info object.
1415      * @private
1416      */
1417     getCurrentProfile_: function() {
1418       for (var i = 0; i < $('profiles-list').dataModel.length; i++) {
1419         var profile = $('profiles-list').dataModel.item(i);
1420         if (profile.isCurrentProfile)
1421           return profile;
1422       }
1423
1424       assertNotReached('There should always be a current profile.');
1425     },
1426
1427     /**
1428      * Propmpts user to confirm deletion of the profile for this browser
1429      * window.
1430      * @private
1431      */
1432     deleteCurrentProfile_: function() {
1433       ManageProfileOverlay.showDeleteDialog(this.getCurrentProfile_());
1434     },
1435
1436     setNativeThemeButtonEnabled_: function(enabled) {
1437       var button = $('themes-native-button');
1438       if (button)
1439         button.disabled = !enabled;
1440     },
1441
1442     setThemesResetButtonEnabled_: function(enabled) {
1443       $('themes-reset').disabled = !enabled;
1444     },
1445
1446     setAccountPictureManaged_: function(managed) {
1447       var picture = $('account-picture');
1448       if (managed || UIAccountTweaks.loggedInAsGuest()) {
1449         picture.disabled = true;
1450         ChangePictureOptions.closeOverlay();
1451       } else {
1452         picture.disabled = false;
1453       }
1454
1455       // Create a synthetic pref change event decorated as
1456       // CoreOptionsHandler::CreateValueForPref() does.
1457       var event = new Event('account-picture');
1458       if (managed)
1459         event.value = { controlledBy: 'policy' };
1460       else
1461         event.value = {};
1462       $('account-picture-indicator').handlePrefChange(event);
1463     },
1464
1465     /**
1466      * (Re)loads IMG element with current user account picture.
1467      * @private
1468      */
1469     updateAccountPicture_: function() {
1470       var picture = $('account-picture');
1471       if (picture) {
1472         picture.src = 'chrome://userimage/' + this.username_ + '?id=' +
1473             Date.now();
1474       }
1475     },
1476
1477     setWallpaperManaged_: function(managed) {
1478       var button = $('set-wallpaper');
1479       button.disabled = !!managed;
1480
1481       // Create a synthetic pref change event decorated as
1482       // CoreOptionsHandler::CreateValueForPref() does.
1483       var event = new Event('wallpaper');
1484       if (managed)
1485         event.value = { controlledBy: 'policy' };
1486       else
1487         event.value = {};
1488       $('wallpaper-indicator').handlePrefChange(event);
1489     },
1490
1491     /**
1492      * Handle the 'add device' button click.
1493      * @private
1494      */
1495     handleAddBluetoothDevice_: function() {
1496       chrome.send('findBluetoothDevices');
1497       PageManager.showPageByName('bluetooth', false);
1498     },
1499
1500     /**
1501      * Enables or disables the Manage SSL Certificates button.
1502      * @private
1503      */
1504     enableCertificateButton_: function(enabled) {
1505       $('certificatesManageButton').disabled = !enabled;
1506     },
1507
1508     /**
1509      * Enables factory reset section.
1510      * @private
1511      */
1512     enableFactoryResetSection_: function() {
1513       $('factory-reset-section').hidden = false;
1514     },
1515
1516     /**
1517      * Set the checked state of the metrics reporting checkbox.
1518      * @private
1519      */
1520     setMetricsReportingCheckboxState_: function(checked, disabled) {
1521       $('metricsReportingEnabled').checked = checked;
1522       $('metricsReportingEnabled').disabled = disabled;
1523     },
1524
1525     /**
1526      * @private
1527      */
1528     setMetricsReportingSettingVisibility_: function(visible) {
1529       if (visible)
1530         $('metricsReportingSetting').style.display = 'block';
1531       else
1532         $('metricsReportingSetting').style.display = 'none';
1533     },
1534
1535     /**
1536      * Set network prediction checkbox value.
1537      *
1538      * @param {Object} pref Information about network prediction options.
1539      * @param {number} pref.value The value of network prediction options.
1540      * @param {boolean} pref.disabled If the pref is not user modifiable.
1541      * @private
1542      */
1543     setNetworkPredictionValue_: function(pref) {
1544       var checkbox = $('networkPredictionOptions');
1545       checkbox.disabled = pref.disabled;
1546       if (pref.value == NetworkPredictionOptions.UNSET) {
1547         checkbox.checked = (NetworkPredictionOptions.DEFAULT !=
1548             NetworkPredictionOptions.NEVER);
1549       } else {
1550         checkbox.checked = (pref.value != NetworkPredictionOptions.NEVER);
1551       }
1552     },
1553
1554     /**
1555      * Set the font size selected item. This item actually reflects two
1556      * preferences: the default font size and the default fixed font size.
1557      *
1558      * @param {Object} pref Information about the font size preferences.
1559      * @param {number} pref.value The value of the default font size pref.
1560      * @param {boolean} pref.disabled True if either pref not user modifiable.
1561      * @param {string} pref.controlledBy The source of the pref value(s) if
1562      *     either pref is currently not controlled by the user.
1563      * @private
1564      */
1565     setFontSize_: function(pref) {
1566       var selectCtl = $('defaultFontSize');
1567       selectCtl.disabled = pref.disabled;
1568       // Create a synthetic pref change event decorated as
1569       // CoreOptionsHandler::CreateValueForPref() does.
1570       var event = new Event('synthetic-font-size');
1571       event.value = {
1572         value: pref.value,
1573         controlledBy: pref.controlledBy,
1574         disabled: pref.disabled
1575       };
1576       $('font-size-indicator').handlePrefChange(event);
1577
1578       for (var i = 0; i < selectCtl.options.length; i++) {
1579         if (selectCtl.options[i].value == pref.value) {
1580           selectCtl.selectedIndex = i;
1581           if ($('Custom'))
1582             selectCtl.remove($('Custom').index);
1583           return;
1584         }
1585       }
1586
1587       // Add/Select Custom Option in the font size label list.
1588       if (!$('Custom')) {
1589         var option = new Option(loadTimeData.getString('fontSizeLabelCustom'),
1590                                 -1, false, true);
1591         option.setAttribute('id', 'Custom');
1592         selectCtl.add(option);
1593       }
1594       $('Custom').selected = true;
1595     },
1596
1597     /**
1598      * Populate the page zoom selector with values received from the caller.
1599      * @param {Array} items An array of items to populate the selector.
1600      *     each object is an array with three elements as follows:
1601      *       0: The title of the item (string).
1602      *       1: The value of the item (number).
1603      *       2: Whether the item should be selected (boolean).
1604      * @private
1605      */
1606     setupPageZoomSelector_: function(items) {
1607       var element = $('defaultZoomFactor');
1608
1609       // Remove any existing content.
1610       element.textContent = '';
1611
1612       // Insert new child nodes into select element.
1613       var value, title, selected;
1614       for (var i = 0; i < items.length; i++) {
1615         title = items[i][0];
1616         value = items[i][1];
1617         selected = items[i][2];
1618         element.appendChild(new Option(title, value, false, selected));
1619       }
1620     },
1621
1622     /**
1623      * Shows/hides the autoOpenFileTypesResetToDefault button and label, with
1624      * animation.
1625      * @param {boolean} display Whether to show the button and label or not.
1626      * @private
1627      */
1628     setAutoOpenFileTypesDisplayed_: function(display) {
1629       if ($('advanced-settings').hidden) {
1630         // If the Advanced section is hidden, don't animate the transition.
1631         $('auto-open-file-types-section').hidden = !display;
1632       } else {
1633         if (display) {
1634           this.showSectionWithAnimation_(
1635               $('auto-open-file-types-section'),
1636               $('auto-open-file-types-container'));
1637         } else {
1638           this.hideSectionWithAnimation_(
1639               $('auto-open-file-types-section'),
1640               $('auto-open-file-types-container'));
1641         }
1642       }
1643     },
1644
1645     /**
1646      * Set the enabled state for the proxy settings button and its associated
1647      * message when extension controlled.
1648      * @param {boolean} disabled Whether the button should be disabled.
1649      * @param {boolean} extensionControlled Whether the proxy is extension
1650      *     controlled.
1651      * @private
1652      */
1653     setupProxySettingsButton_: function(disabled, extensionControlled) {
1654       if (!cr.isChromeOS) {
1655         $('proxiesConfigureButton').disabled = disabled;
1656         $('proxiesLabel').textContent =
1657             loadTimeData.getString(extensionControlled ?
1658                 'proxiesLabelExtension' : 'proxiesLabelSystem');
1659       }
1660     },
1661
1662     /**
1663      * Set the initial state of the spoken feedback checkbox.
1664      * @private
1665      */
1666     setSpokenFeedbackCheckboxState_: function(checked) {
1667       $('accessibility-spoken-feedback-check').checked = checked;
1668     },
1669
1670     /**
1671      * Set the initial state of the high contrast checkbox.
1672      * @private
1673      */
1674     setHighContrastCheckboxState_: function(checked) {
1675       $('accessibility-high-contrast-check').checked = checked;
1676     },
1677
1678     /**
1679      * Set the initial state of the virtual keyboard checkbox.
1680      * @private
1681      */
1682     setVirtualKeyboardCheckboxState_: function(checked) {
1683       // TODO(zork): Update UI
1684     },
1685
1686     /**
1687      * Show/hide mouse settings slider.
1688      * @private
1689      */
1690     showMouseControls_: function(show) {
1691       $('mouse-settings').hidden = !show;
1692     },
1693
1694     /**
1695      * Adds hidden warning boxes for settings potentially controlled by
1696      * extensions.
1697      * @param {string} parentDiv The div name to append the bubble to.
1698      * @param {string} bubbleId The ID to use for the bubble.
1699      * @param {boolean} first Add as first node if true, otherwise last.
1700      * @private
1701      */
1702     addExtensionControlledBox_: function(parentDiv, bubbleId, first) {
1703       var bubble = $('extension-controlled-warning-template').cloneNode(true);
1704       bubble.id = bubbleId;
1705       var parent = $(parentDiv);
1706       if (first)
1707         parent.insertBefore(bubble, parent.firstChild);
1708       else
1709         parent.appendChild(bubble);
1710     },
1711
1712     /**
1713      * Adds a bubble showing that an extension is controlling a particular
1714      * setting.
1715      * @param {string} parentDiv The div name to append the bubble to.
1716      * @param {string} bubbleId The ID to use for the bubble.
1717      * @param {string} extensionId The ID of the controlling extension.
1718      * @param {string} extensionName The name of the controlling extension.
1719      * @private
1720      */
1721     toggleExtensionControlledBox_: function(
1722         parentDiv, bubbleId, extensionId, extensionName) {
1723       var bubble = $(bubbleId);
1724       assert(bubble);
1725       bubble.hidden = extensionId.length == 0;
1726       if (bubble.hidden)
1727         return;
1728
1729       // Set the extension image.
1730       var div = bubble.firstElementChild;
1731       div.style.backgroundImage =
1732           'url(chrome://extension-icon/' + extensionId + '/24/1)';
1733
1734       // Set the bubble label.
1735       var label = loadTimeData.getStringF('extensionControlled', extensionName);
1736       var docFrag = parseHtmlSubset('<div>' + label + '</div>', ['B', 'DIV']);
1737       div.innerHTML = docFrag.firstChild.innerHTML;
1738
1739       // Wire up the button to disable the right extension.
1740       var button = div.nextElementSibling;
1741       button.dataset.extensionId = extensionId;
1742     },
1743
1744     /**
1745      * Toggles the warning boxes that show which extension is controlling
1746      * various settings of Chrome.
1747      * @param {object} details A dictionary of ID+name pairs for each of the
1748      *     settings controlled by an extension.
1749      * @private
1750      */
1751     toggleExtensionIndicators_: function(details) {
1752       this.toggleExtensionControlledBox_('search-section-content',
1753                                          'search-engine-controlled',
1754                                          details.searchEngine.id,
1755                                          details.searchEngine.name);
1756       this.toggleExtensionControlledBox_('extension-controlled-container',
1757                                          'homepage-controlled',
1758                                          details.homePage.id,
1759                                          details.homePage.name);
1760       this.toggleExtensionControlledBox_('startup-section-content',
1761                                          'startpage-controlled',
1762                                          details.startUpPage.id,
1763                                          details.startUpPage.name);
1764       this.toggleExtensionControlledBox_('newtab-section-content',
1765                                          'newtab-controlled',
1766                                          details.newTabPage.id,
1767                                          details.newTabPage.name);
1768       this.toggleExtensionControlledBox_('proxy-section-content',
1769                                          'proxy-controlled',
1770                                          details.proxy.id,
1771                                          details.proxy.name);
1772
1773       // The proxy section contains just the warning box and nothing else, so
1774       // if we're hiding the proxy warning box, we should also hide its header
1775       // section.
1776       $('proxy-section').hidden = details.proxy.id.length == 0;
1777     },
1778
1779
1780     /**
1781      * Show/hide touchpad-related settings.
1782      * @private
1783      */
1784     showTouchpadControls_: function(show) {
1785       $('touchpad-settings').hidden = !show;
1786       $('accessibility-tap-dragging').hidden = !show;
1787     },
1788
1789     /**
1790      * Activate the Bluetooth settings section on the System settings page.
1791      * @private
1792      */
1793     showBluetoothSettings_: function() {
1794       $('bluetooth-devices').hidden = false;
1795     },
1796
1797     /**
1798      * Dectivates the Bluetooth settings section from the System settings page.
1799      * @private
1800      */
1801     hideBluetoothSettings_: function() {
1802       $('bluetooth-devices').hidden = true;
1803     },
1804
1805     /**
1806      * Sets the state of the checkbox indicating if Bluetooth is turned on. The
1807      * state of the "Find devices" button and the list of discovered devices may
1808      * also be affected by a change to the state.
1809      * @param {boolean} checked Flag Indicating if Bluetooth is turned on.
1810      * @private
1811      */
1812     setBluetoothState_: function(checked) {
1813       $('enable-bluetooth').checked = checked;
1814       $('bluetooth-paired-devices-list').parentNode.hidden = !checked;
1815       $('bluetooth-add-device').hidden = !checked;
1816       $('bluetooth-reconnect-device').hidden = !checked;
1817       // Flush list of previously discovered devices if bluetooth is turned off.
1818       if (!checked) {
1819         $('bluetooth-paired-devices-list').clear();
1820         $('bluetooth-unpaired-devices-list').clear();
1821       } else {
1822         chrome.send('getPairedBluetoothDevices');
1823       }
1824     },
1825
1826     /**
1827      * Adds an element to the list of available Bluetooth devices. If an element
1828      * with a matching address is found, the existing element is updated.
1829      * @param {{name: string,
1830      *          address: string,
1831      *          paired: boolean,
1832      *          connected: boolean}} device
1833      *     Decription of the Bluetooth device.
1834      * @private
1835      */
1836     addBluetoothDevice_: function(device) {
1837       var list = $('bluetooth-unpaired-devices-list');
1838       // Display the "connecting" (already paired or not yet paired) and the
1839       // paired devices in the same list.
1840       if (device.paired || device.connecting) {
1841         // Test to see if the device is currently in the unpaired list, in which
1842         // case it should be removed from that list.
1843         var index = $('bluetooth-unpaired-devices-list').find(device.address);
1844         if (index != undefined)
1845           $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1846         list = $('bluetooth-paired-devices-list');
1847       } else {
1848         // Test to see if the device is currently in the paired list, in which
1849         // case it should be removed from that list.
1850         var index = $('bluetooth-paired-devices-list').find(device.address);
1851         if (index != undefined)
1852           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1853       }
1854       list.appendDevice(device);
1855
1856       // One device can be in the process of pairing.  If found, display
1857       // the Bluetooth pairing overlay.
1858       if (device.pairing)
1859         BluetoothPairing.showDialog(device);
1860     },
1861
1862     /**
1863      * Removes an element from the list of available devices.
1864      * @param {string} address Unique address of the device.
1865      * @private
1866      */
1867     removeBluetoothDevice_: function(address) {
1868       var index = $('bluetooth-unpaired-devices-list').find(address);
1869       if (index != undefined) {
1870         $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index);
1871       } else {
1872         index = $('bluetooth-paired-devices-list').find(address);
1873         if (index != undefined)
1874           $('bluetooth-paired-devices-list').deleteItemAtIndex(index);
1875       }
1876     },
1877
1878     /**
1879      * Shows the overlay dialog for changing the user avatar image.
1880      * @private
1881      */
1882     showImagerPickerOverlay_: function() {
1883       PageManager.showPageByName('changePicture');
1884     },
1885
1886     /**
1887      * Shows (or not) the "User" section of the settings page based on whether
1888      * any of the sub-sections are present (or not).
1889      * @private
1890      */
1891     maybeShowUserSection_: function() {
1892       $('sync-users-section').hidden =
1893           $('profiles-section').hidden &&
1894           $('sync-section').hidden &&
1895           $('profiles-supervised-dashboard-tip').hidden;
1896     },
1897
1898     /**
1899      * Updates the date and time section with time sync information.
1900      * @param {boolean} canSetTime Whether the system time can be set.
1901      * @private
1902      */
1903     setCanSetTime_: function(canSetTime) {
1904       // If the time has been network-synced, it cannot be set manually.
1905       $('time-synced-explanation').hidden = canSetTime;
1906       $('set-time').hidden = !canSetTime;
1907     },
1908
1909     /**
1910      * Handle the 'set date and time' button click.
1911      * @private
1912      */
1913     handleSetTime_: function() {
1914       chrome.send('showSetTime');
1915     },
1916   };
1917
1918   //Forward public APIs to private implementations.
1919   [
1920     'addBluetoothDevice',
1921     'deleteCurrentProfile',
1922     'enableCertificateButton',
1923     'enableFactoryResetSection',
1924     'getCurrentProfile',
1925     'getStartStopSyncButton',
1926     'hideBluetoothSettings',
1927     'notifyInitializationComplete',
1928     'removeBluetoothDevice',
1929     'scrollToSection',
1930     'setAccountPictureManaged',
1931     'setWallpaperManaged',
1932     'setAutoOpenFileTypesDisplayed',
1933     'setBluetoothState',
1934     'setCanSetTime',
1935     'setFontSize',
1936     'setNativeThemeButtonEnabled',
1937     'setNetworkPredictionValue',
1938     'setHighContrastCheckboxState',
1939     'setMetricsReportingCheckboxState',
1940     'setMetricsReportingSettingVisibility',
1941     'setProfilesInfo',
1942     'setSpokenFeedbackCheckboxState',
1943     'setThemesResetButtonEnabled',
1944     'setVirtualKeyboardCheckboxState',
1945     'setupPageZoomSelector',
1946     'setupProxySettingsButton',
1947     'showBluetoothSettings',
1948     'showCreateProfileError',
1949     'showCreateProfileSuccess',
1950     'showCreateProfileWarning',
1951     'showHotwordAlwaysOnSection',
1952     'showHotwordSection',
1953     'showMouseControls',
1954     'showSupervisedUserImportError',
1955     'showSupervisedUserImportSuccess',
1956     'showTouchpadControls',
1957     'toggleExtensionIndicators',
1958     'updateAccountPicture',
1959     'updateAutoLaunchState',
1960     'updateDefaultBrowserState',
1961     'updateEasyUnlock',
1962     'updateManagesSupervisedUsers',
1963     'updateSearchEngines',
1964     'updateStartupPages',
1965     'updateSyncState',
1966   ].forEach(function(name) {
1967     BrowserOptions[name] = function() {
1968       var instance = BrowserOptions.getInstance();
1969       return instance[name + '_'].apply(instance, arguments);
1970     };
1971   });
1972
1973   if (cr.isChromeOS) {
1974     /**
1975      * Returns username (canonical email) of the user logged in (ChromeOS only).
1976      * @return {string} user email.
1977      */
1978     // TODO(jhawkins): Investigate the use case for this method.
1979     BrowserOptions.getLoggedInUsername = function() {
1980       return BrowserOptions.getInstance().username_;
1981     };
1982   }
1983
1984   // Export
1985   return {
1986     BrowserOptions: BrowserOptions
1987   };
1988 });