1// Copyright (c) 2011 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
5cr.define('options', function() {
6  const OptionsPage = options.OptionsPage;
7  const ArrayDataModel = cr.ui.ArrayDataModel;
8
9  //
10  // BrowserOptions class
11  // Encapsulated handling of browser options page.
12  //
13  function BrowserOptions() {
14    OptionsPage.call(this, 'browser',
15                     templateData.browserPageTabTitle,
16                     'browserPage');
17  }
18
19  cr.addSingletonGetter(BrowserOptions);
20
21  BrowserOptions.prototype = {
22    // Inherit BrowserOptions from OptionsPage.
23    __proto__: options.OptionsPage.prototype,
24
25    startup_pages_pref_: {
26      'name': 'session.urls_to_restore_on_startup',
27      'managed': false
28    },
29
30    homepage_pref_: {
31      'name': 'homepage',
32      'value': '',
33      'managed': false
34    },
35
36    homepage_is_newtabpage_pref_: {
37      'name': 'homepage_is_newtabpage',
38      'value': true,
39      'managed': false
40    },
41
42    /**
43     * At autocomplete list that can be attached to a text field during editing.
44     * @type {HTMLElement}
45     * @private
46     */
47    autocompleteList_: null,
48
49    // The cached value of the instant.confirm_dialog_shown preference.
50    instantConfirmDialogShown_: false,
51
52    /**
53     * Initialize BrowserOptions page.
54     */
55    initializePage: function() {
56      // Call base class implementation to start preference initialization.
57      OptionsPage.prototype.initializePage.call(this);
58
59      // Wire up controls.
60      $('startupUseCurrentButton').onclick = function(event) {
61        chrome.send('setStartupPagesToCurrentPages');
62      };
63      $('toolbarShowBookmarksBar').onchange = function() {
64        chrome.send('toggleShowBookmarksBar');
65      };
66      $('defaultSearchManageEnginesButton').onclick = function(event) {
67        OptionsPage.navigateToPage('searchEngines');
68        chrome.send('coreOptionsUserMetricsAction',
69            ['Options_ManageSearchEngines']);
70      };
71      $('defaultSearchEngine').onchange = this.setDefaultSearchEngine_;
72
73      var self = this;
74      $('instantEnableCheckbox').onclick = function(event) {
75        if (this.checked && !self.instantConfirmDialogShown_) {
76          // Leave disabled for now. The PrefCheckbox handler already set it to
77          // true so undo that.
78          Preferences.setBooleanPref(this.pref, false, this.metric);
79          OptionsPage.navigateToPage('instantConfirm');
80        }
81      };
82
83      Preferences.getInstance().addEventListener('instant.confirm_dialog_shown',
84          this.onInstantConfirmDialogShownChanged_.bind(this));
85
86      var homepageField = $('homepageURL');
87      $('homepageUseNTPButton').onchange =
88          this.handleHomepageUseNTPButtonChange_.bind(this);
89      $('homepageUseURLButton').onchange =
90          this.handleHomepageUseURLButtonChange_.bind(this);
91      var homepageChangeHandler = this.handleHomepageURLChange_.bind(this);
92      homepageField.addEventListener('change', homepageChangeHandler);
93      homepageField.addEventListener('input', homepageChangeHandler);
94      homepageField.addEventListener('focus', function(event) {
95        self.autocompleteList_.attachToInput(homepageField);
96      });
97      homepageField.addEventListener('blur', function(event) {
98        self.autocompleteList_.detach();
99      });
100      homepageField.addEventListener('keydown', function(event) {
101        // Remove focus when the user hits enter since people expect feedback
102        // indicating that they are done editing.
103        if (event.keyIdentifier == 'Enter')
104          homepageField.blur();
105      });
106
107      // Ensure that changes are committed when closing the page.
108      window.addEventListener('unload', function() {
109        if (document.activeElement == homepageField)
110          homepageField.blur();
111      });
112
113      if (!cr.isChromeOS) {
114        $('defaultBrowserUseAsDefaultButton').onclick = function(event) {
115          chrome.send('becomeDefaultBrowser');
116        };
117      }
118
119      var startupPagesList = $('startupPagesList');
120      options.browser_options.StartupPageList.decorate(startupPagesList);
121      startupPagesList.autoExpands = true;
122
123      // Check if we are in the guest mode.
124      if (cr.commandLine.options['--bwsi']) {
125        // Hide the startup section.
126        $('startupSection').classList.add('hidden');
127      } else {
128        // Initialize control enabled states.
129        Preferences.getInstance().addEventListener('session.restore_on_startup',
130            this.updateCustomStartupPageControlStates_.bind(this));
131        Preferences.getInstance().addEventListener(
132            this.startup_pages_pref_.name,
133            this.handleStartupPageListChange_.bind(this));
134        Preferences.getInstance().addEventListener(
135            this.homepage_pref_.name,
136            this.handleHomepageChange_.bind(this));
137        Preferences.getInstance().addEventListener(
138            this.homepage_is_newtabpage_pref_.name,
139            this.handleHomepageIsNewTabPageChange_.bind(this));
140
141        this.updateCustomStartupPageControlStates_();
142      }
143
144      var suggestionList = new options.AutocompleteList();
145      suggestionList.autoExpands = true;
146      suggestionList.suggestionUpdateRequestCallback =
147          this.requestAutocompleteSuggestions_.bind(this);
148      $('main-content').appendChild(suggestionList);
149      this.autocompleteList_ = suggestionList;
150      startupPagesList.autocompleteList = suggestionList;
151    },
152
153    /**
154     * Called when the value of the instant.confirm_dialog_shown preference
155     * changes. Cache this value.
156     * @param {Event} event Change event.
157     * @private
158     */
159    onInstantConfirmDialogShownChanged_: function(event) {
160      this.instantConfirmDialogShown_ = event.value['value'];
161    },
162
163    /**
164     * Update the Default Browsers section based on the current state.
165     * @param {string} statusString Description of the current default state.
166     * @param {boolean} isDefault Whether or not the browser is currently
167     *     default.
168     * @param {boolean} canBeDefault Whether or not the browser can be default.
169     * @private
170     */
171    updateDefaultBrowserState_: function(statusString, isDefault,
172                                         canBeDefault) {
173      var label = $('defaultBrowserState');
174      label.textContent = statusString;
175
176      $('defaultBrowserUseAsDefaultButton').disabled = !canBeDefault ||
177                                                       isDefault;
178    },
179
180    /**
181     * Clears the search engine popup.
182     * @private
183     */
184    clearSearchEngines_: function() {
185      $('defaultSearchEngine').textContent = '';
186    },
187
188    /**
189     * Updates the search engine popup with the given entries.
190     * @param {Array} engines List of available search engines.
191     * @param {number} defaultValue The value of the current default engine.
192     */
193    updateSearchEngines_: function(engines, defaultValue) {
194      this.clearSearchEngines_();
195      engineSelect = $('defaultSearchEngine');
196      engineCount = engines.length;
197      var defaultIndex = -1;
198      for (var i = 0; i < engineCount; i++) {
199        var engine = engines[i];
200        var option = new Option(engine['name'], engine['index']);
201        if (defaultValue == option.value)
202          defaultIndex = i;
203        engineSelect.appendChild(option);
204      }
205      if (defaultIndex >= 0)
206        engineSelect.selectedIndex = defaultIndex;
207    },
208
209    /**
210     * Returns true if the custom startup page control block should
211     * be enabled.
212     * @returns {boolean} Whether the startup page controls should be
213     *     enabled.
214     */
215    shouldEnableCustomStartupPageControls: function(pages) {
216      return $('startupShowPagesButton').checked &&
217          !this.startup_pages_pref_.managed;
218    },
219
220    /**
221     * Updates the startup pages list with the given entries.
222     * @param {Array} pages List of startup pages.
223     * @private
224     */
225    updateStartupPages_: function(pages) {
226      var model = new ArrayDataModel(pages);
227      // Add a "new page" row.
228      model.push({
229        'modelIndex': '-1'
230      });
231      $('startupPagesList').dataModel = model;
232    },
233
234    /**
235     * Handles change events of the radio button 'homepageUseURLButton'.
236     * @param {event} change event.
237     * @private
238     */
239    handleHomepageUseURLButtonChange_: function(event) {
240      Preferences.setBooleanPref(this.homepage_is_newtabpage_pref_.name, false);
241    },
242
243    /**
244     * Handles change events of the radio button 'homepageUseNTPButton'.
245     * @param {event} change event.
246     * @private
247     */
248    handleHomepageUseNTPButtonChange_: function(event) {
249      Preferences.setBooleanPref(this.homepage_is_newtabpage_pref_.name, true);
250    },
251
252    /**
253     * Handles input and change events of the text field 'homepageURL'.
254     * @param {event} input/change event.
255     * @private
256     */
257    handleHomepageURLChange_: function(event) {
258      var homepageField = $('homepageURL');
259      var doFixup = event.type == 'change' ? '1' : '0';
260      chrome.send('setHomePage', [homepageField.value, doFixup]);
261    },
262
263    /**
264     * Handle change events of the preference 'homepage'.
265     * @param {event} preference changed event.
266     * @private
267     */
268    handleHomepageChange_: function(event) {
269      this.homepage_pref_.value = event.value['value'];
270      this.homepage_pref_.managed = event.value['managed'];
271      if (this.isHomepageURLNewTabPageURL_() && !this.homepage_pref_.managed &&
272          !this.homepage_is_newtabpage_pref_.managed) {
273        var useNewTabPage = this.isHomepageIsNewTabPageChoiceSelected_();
274        Preferences.setStringPref(this.homepage_pref_.name, '')
275        Preferences.setBooleanPref(this.homepage_is_newtabpage_pref_.name,
276                                   useNewTabPage)
277      }
278      this.updateHomepageControlStates_();
279    },
280
281    /**
282     * Handle change events of the preference homepage_is_newtabpage.
283     * @param {event} preference changed event.
284     * @private
285     */
286    handleHomepageIsNewTabPageChange_: function(event) {
287      this.homepage_is_newtabpage_pref_.value = event.value['value'];
288      this.homepage_is_newtabpage_pref_.managed = event.value['managed'];
289      this.updateHomepageControlStates_();
290    },
291
292    /**
293     * Update homepage preference UI controls.  Here's a table describing the
294     * desired characteristics of the homepage choice radio value, its enabled
295     * state and the URL field enabled state. They depend on the values of the
296     * managed bits for homepage (m_hp) and homepageIsNewTabPage (m_ntp)
297     * preferences, as well as the value of the homepageIsNewTabPage preference
298     * (ntp) and whether the homepage preference is equal to the new tab page
299     * URL (hpisntp).
300     *
301     * m_hp m_ntp ntp hpisntp| choice value| choice enabled| URL field enabled
302     * ------------------------------------------------------------------------
303     * 0    0     0   0      | homepage    | 1             | 1
304     * 0    0     0   1      | new tab page| 1             | 0
305     * 0    0     1   0      | new tab page| 1             | 0
306     * 0    0     1   1      | new tab page| 1             | 0
307     * 0    1     0   0      | homepage    | 0             | 1
308     * 0    1     0   1      | homepage    | 0             | 1
309     * 0    1     1   0      | new tab page| 0             | 0
310     * 0    1     1   1      | new tab page| 0             | 0
311     * 1    0     0   0      | homepage    | 1             | 0
312     * 1    0     0   1      | new tab page| 0             | 0
313     * 1    0     1   0      | new tab page| 1             | 0
314     * 1    0     1   1      | new tab page| 0             | 0
315     * 1    1     0   0      | homepage    | 0             | 0
316     * 1    1     0   1      | new tab page| 0             | 0
317     * 1    1     1   0      | new tab page| 0             | 0
318     * 1    1     1   1      | new tab page| 0             | 0
319     *
320     * thus, we have:
321     *
322     *    choice value is new tab page === ntp || (hpisntp && (m_hp || !m_ntp))
323     *    choice enabled === !m_ntp && !(m_hp && hpisntp)
324     *    URL field enabled === !ntp && !mhp && !(hpisntp && !m_ntp)
325     *
326     * which also make sense if you think about them.
327     * @private
328     */
329    updateHomepageControlStates_: function() {
330      var homepageField = $('homepageURL');
331      homepageField.disabled = !this.isHomepageURLFieldEnabled_();
332      if (homepageField.value != this.homepage_pref_.value)
333        homepageField.value = this.homepage_pref_.value;
334      homepageField.style.backgroundImage = url('chrome://favicon/' +
335                                                this.homepage_pref_.value);
336      var disableChoice = !this.isHomepageChoiceEnabled_();
337      $('homepageUseURLButton').disabled = disableChoice;
338      $('homepageUseNTPButton').disabled = disableChoice;
339      var useNewTabPage = this.isHomepageIsNewTabPageChoiceSelected_();
340      $('homepageUseNTPButton').checked = useNewTabPage;
341      $('homepageUseURLButton').checked = !useNewTabPage;
342    },
343
344    /**
345     * Tests whether the value of the 'homepage' preference equls the new tab
346     * page url (chrome://newtab).
347     * @returns {boolean} True if the 'homepage' value equals the new tab page
348     *     url.
349     * @private
350     */
351    isHomepageURLNewTabPageURL_ : function() {
352      return (this.homepage_pref_.value.toLowerCase() == 'chrome://newtab');
353    },
354
355    /**
356     * Tests whether the Homepage choice "Use New Tab Page" is selected.
357     * @returns {boolean} True if "Use New Tab Page" is selected.
358     * @private
359     */
360    isHomepageIsNewTabPageChoiceSelected_: function() {
361      return (this.homepage_is_newtabpage_pref_.value ||
362              (this.isHomepageURLNewTabPageURL_() &&
363               (this.homepage_pref_.managed ||
364                !this.homepage_is_newtabpage_pref_.managed)));
365    },
366
367    /**
368     * Tests whether the home page choice controls are enabled.
369     * @returns {boolean} True if the home page choice controls are enabled.
370     * @private
371     */
372    isHomepageChoiceEnabled_: function() {
373      return (!this.homepage_is_newtabpage_pref_.managed &&
374              !(this.homepage_pref_.managed &&
375                this.isHomepageURLNewTabPageURL_()));
376    },
377
378    /**
379     * Checks whether the home page field should be enabled.
380     * @returns {boolean} True if the home page field should be enabled.
381     * @private
382     */
383    isHomepageURLFieldEnabled_: function() {
384      return (!this.homepage_is_newtabpage_pref_.value &&
385              !this.homepage_pref_.managed &&
386              !(this.isHomepageURLNewTabPageURL_() &&
387                !this.homepage_is_newtabpage_pref_.managed));
388    },
389
390    /**
391     * Sets the enabled state of the custom startup page list controls
392     * based on the current startup radio button selection.
393     * @private
394     */
395    updateCustomStartupPageControlStates_: function() {
396      var disable = !this.shouldEnableCustomStartupPageControls();
397      $('startupPagesList').disabled = disable;
398      $('startupUseCurrentButton').disabled = disable;
399    },
400
401    /**
402     * Handle change events of the preference
403     * 'session.urls_to_restore_on_startup'.
404     * @param {event} preference changed event.
405     * @private
406     */
407    handleStartupPageListChange_: function(event) {
408      this.startup_pages_pref_.managed = event.value['managed'];
409      this.updateCustomStartupPageControlStates_();
410    },
411
412    /**
413     * Set the default search engine based on the popup selection.
414     */
415    setDefaultSearchEngine_: function() {
416      var engineSelect = $('defaultSearchEngine');
417      var selectedIndex = engineSelect.selectedIndex;
418      if (selectedIndex >= 0) {
419        var selection = engineSelect.options[selectedIndex];
420        chrome.send('setDefaultSearchEngine', [String(selection.value)]);
421      }
422    },
423
424    /**
425     * Sends an asynchronous request for new autocompletion suggestions for the
426     * the given query. When new suggestions are available, the C++ handler will
427     * call updateAutocompleteSuggestions_.
428     * @param {string} query List of autocomplete suggestions.
429     * @private
430     */
431    requestAutocompleteSuggestions_: function(query) {
432      chrome.send('requestAutocompleteSuggestions', [query]);
433    },
434
435    /**
436     * Updates the autocomplete suggestion list with the given entries.
437     * @param {Array} pages List of autocomplete suggestions.
438     * @private
439     */
440    updateAutocompleteSuggestions_: function(suggestions) {
441      var list = this.autocompleteList_;
442      // If the trigger for this update was a value being selected from the
443      // current list, do nothing.
444      if (list.targetInput && list.selectedItem &&
445          list.selectedItem['url'] == list.targetInput.value)
446        return;
447      list.suggestions = suggestions;
448    },
449  };
450
451  BrowserOptions.updateDefaultBrowserState = function(statusString, isDefault,
452                                                      canBeDefault) {
453    if (!cr.isChromeOS) {
454      BrowserOptions.getInstance().updateDefaultBrowserState_(statusString,
455                                                              isDefault,
456                                                              canBeDefault);
457    }
458  };
459
460  BrowserOptions.updateSearchEngines = function(engines, defaultValue) {
461    BrowserOptions.getInstance().updateSearchEngines_(engines, defaultValue);
462  };
463
464  BrowserOptions.updateStartupPages = function(pages) {
465    BrowserOptions.getInstance().updateStartupPages_(pages);
466  };
467
468  BrowserOptions.updateAutocompleteSuggestions = function(suggestions) {
469    BrowserOptions.getInstance().updateAutocompleteSuggestions_(suggestions);
470  };
471
472  // Export
473  return {
474    BrowserOptions: BrowserOptions
475  };
476
477});
478