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
5cr.define('options', function() {
6  /** @const */ var Page = cr.ui.pageManager.Page;
7  /** @const */ var PageManager = cr.ui.pageManager.PageManager;
8
9  // Lookup table to generate the i18n strings.
10  /** @const */ var permissionsLookup = {
11    'location': 'location',
12    'notifications': 'notifications',
13    'media-stream': 'mediaStream',
14    'cookies': 'cookies',
15    'multiple-automatic-downloads': 'multipleAutomaticDownloads',
16    'images': 'images',
17    'plugins': 'plugins',
18    'popups': 'popups',
19    'javascript': 'javascript'
20  };
21
22  //////////////////////////////////////////////////////////////////////////////
23  // ContentSettings class:
24
25  /**
26   * Encapsulated handling of content settings page.
27   * @constructor
28   * @extends {cr.ui.pageManager.Page}
29   */
30  function ContentSettings() {
31    this.activeNavTab = null;
32    Page.call(this, 'content',
33              loadTimeData.getString('contentSettingsPageTabTitle'),
34              'content-settings-page');
35  }
36
37  cr.addSingletonGetter(ContentSettings);
38
39  ContentSettings.prototype = {
40    __proto__: Page.prototype,
41
42    /** @override */
43    initializePage: function() {
44      Page.prototype.initializePage.call(this);
45
46      var exceptionsButtons =
47          this.pageDiv.querySelectorAll('.exceptions-list-button');
48      for (var i = 0; i < exceptionsButtons.length; i++) {
49        exceptionsButtons[i].onclick = function(event) {
50          var hash = event.currentTarget.getAttribute('contentType');
51          PageManager.showPageByName('contentExceptions', true,
52                                     {hash: '#' + hash});
53        };
54      }
55
56      var experimentalExceptionsButtons =
57          this.pageDiv.querySelectorAll('.website-settings-permission-button');
58      for (var i = 0; i < experimentalExceptionsButtons.length; i++) {
59        experimentalExceptionsButtons[i].onclick = function(event) {
60          var hash = event.currentTarget.getAttribute('contentType');
61          WebsiteSettingsManager.showWebsiteSettings(hash);
62        };
63      }
64
65      var manageHandlersButton = $('manage-handlers-button');
66      if (manageHandlersButton) {
67        manageHandlersButton.onclick = function(event) {
68          PageManager.showPageByName('handlers');
69        };
70      }
71
72      if (cr.isChromeOS) {
73        // Disable some controls for Guest in Chrome OS.
74        UIAccountTweaks.applyGuestSessionVisibility(document);
75
76        // Disable some controls for Public session in Chrome OS.
77        UIAccountTweaks.applyPublicSessionVisibility(document);
78      }
79
80      // Cookies filter page ---------------------------------------------------
81      $('show-cookies-button').onclick = function(event) {
82        chrome.send('coreOptionsUserMetricsAction', ['Options_ShowCookies']);
83        PageManager.showPageByName('cookies');
84      };
85
86      $('content-settings-overlay-confirm').onclick =
87          PageManager.closeOverlay.bind(PageManager);
88
89      $('media-pepper-flash-default').hidden = true;
90      $('media-pepper-flash-exceptions').hidden = true;
91
92      $('media-select-mic').addEventListener('change',
93          ContentSettings.setDefaultMicrophone_);
94      $('media-select-camera').addEventListener('change',
95          ContentSettings.setDefaultCamera_);
96
97      if (loadTimeData.getBoolean('websiteSettingsManagerEnabled')) {
98        var oldUI =
99            this.pageDiv.querySelectorAll('.replace-with-website-settings');
100        for (var i = 0; i < oldUI.length; i++) {
101          oldUI[i].hidden = true;
102        }
103
104        var newUI =
105            this.pageDiv.querySelectorAll('.experimental-website-settings');
106        for (var i = 0; i < newUI.length; i++) {
107          newUI[i].hidden = false;
108        }
109      }
110    },
111  };
112
113  ContentSettings.updateHandlersEnabledRadios = function(enabled) {
114    var selector = '#content-settings-page input[type=radio][value=' +
115        (enabled ? 'allow' : 'block') + '].handler-radio';
116    document.querySelector(selector).checked = true;
117  };
118
119  /**
120   * Sets the values for all the content settings radios and labels.
121   * @param {Object} dict A mapping from radio groups to the checked value for
122   *     that group.
123   */
124  ContentSettings.setContentFilterSettingsValue = function(dict) {
125    for (var group in dict) {
126      var settingLabel = $(group + '-default-string');
127      if (settingLabel) {
128        var value = dict[group].value;
129        var valueId =
130            permissionsLookup[group] + value[0].toUpperCase() + value.slice(1);
131        settingLabel.textContent = loadTimeData.getString(valueId);
132      }
133
134      var managedBy = dict[group].managedBy;
135      var controlledBy = managedBy == 'policy' || managedBy == 'extension' ?
136          managedBy : null;
137      document.querySelector('input[type=radio][name=' + group + '][value=' +
138                             dict[group].value + ']').checked = true;
139      var radios = document.querySelectorAll('input[type=radio][name=' +
140                                             group + ']');
141      for (var i = 0, len = radios.length; i < len; i++) {
142        radios[i].disabled = (managedBy != 'default');
143        radios[i].controlledBy = controlledBy;
144      }
145      var indicators = document.querySelectorAll(
146          'span.controlled-setting-indicator[content-setting=' + group + ']');
147      if (indicators.length == 0)
148        continue;
149      // Create a synthetic pref change event decorated as
150      // CoreOptionsHandler::CreateValueForPref() does.
151      var event = new Event(group);
152      event.value = {
153        value: dict[group].value,
154        controlledBy: controlledBy,
155      };
156      for (var i = 0; i < indicators.length; i++) {
157        indicators[i].handlePrefChange(event);
158      }
159    }
160  };
161
162  /**
163   * Updates the labels and indicators for the Media settings. Those require
164   * special handling because they are backed by multiple prefs and can change
165   * their scope based on the managed state of the backing prefs.
166   * @param {{askText: string, blockText: string, cameraDisabled: boolean,
167   *          micDisabled: boolean, showBubble: boolean, bubbleText: string}}
168   *     mediaSettings A dictionary containing the following fields:
169   *     askText The label for the ask radio button.
170   *     blockText The label for the block radio button.
171   *     cameraDisabled Whether to disable the camera dropdown.
172   *     micDisabled Whether to disable the microphone dropdown.
173   *     showBubble Wether to show the managed icon and bubble for the media
174   *                label.
175   *     bubbleText The text to use inside the bubble if it is shown.
176   */
177  ContentSettings.updateMediaUI = function(mediaSettings) {
178    $('media-stream-ask-label').innerHTML =
179        loadTimeData.getString(mediaSettings.askText);
180    $('media-stream-block-label').innerHTML =
181        loadTimeData.getString(mediaSettings.blockText);
182
183    if (mediaSettings.micDisabled)
184      $('media-select-mic').disabled = true;
185    if (mediaSettings.cameraDisabled)
186      $('media-select-camera').disabled = true;
187
188    PageManager.hideBubble();
189    // Create a synthetic pref change event decorated as
190    // CoreOptionsHandler::CreateValueForPref() does.
191    // TODO(arv): It was not clear what event type this should use?
192    var event = new Event('undefined');
193    event.value = {};
194
195    if (mediaSettings.showBubble) {
196      event.value = { controlledBy: 'policy' };
197      $('media-indicator').setAttribute(
198          'textpolicy', loadTimeData.getString(mediaSettings.bubbleText));
199      $('media-indicator').location = cr.ui.ArrowLocation.TOP_START;
200    }
201
202    $('media-indicator').handlePrefChange(event);
203  };
204
205  /**
206   * Initializes an exceptions list.
207   * @param {string} type The content type that we are setting exceptions for.
208   * @param {Array} exceptions An array of pairs, where the first element of
209   *     each pair is the filter string, and the second is the setting
210   *     (allow/block).
211   */
212  ContentSettings.setExceptions = function(type, exceptions) {
213    this.getExceptionsList(type, 'normal').setExceptions(exceptions);
214  };
215
216  ContentSettings.setHandlers = function(handlers) {
217    $('handlers-list').setHandlers(handlers);
218  };
219
220  ContentSettings.setIgnoredHandlers = function(ignoredHandlers) {
221    $('ignored-handlers-list').setHandlers(ignoredHandlers);
222  };
223
224  ContentSettings.setOTRExceptions = function(type, otrExceptions) {
225    var exceptionsList = this.getExceptionsList(type, 'otr');
226    // Settings for Guest hides many sections, so check for null first.
227    if (exceptionsList) {
228      exceptionsList.parentNode.hidden = false;
229      exceptionsList.setExceptions(otrExceptions);
230    }
231  };
232
233  /**
234   * @param {string} type The type of exceptions (e.g. "location") to get.
235   * @param {string} mode The mode of the desired exceptions list (e.g. otr).
236   * @return {?options.contentSettings.ExceptionsList} The corresponding
237   *     exceptions list or null.
238   */
239  ContentSettings.getExceptionsList = function(type, mode) {
240    var exceptionsList = document.querySelector(
241        'div[contentType=' + type + '] list[mode=' + mode + ']');
242    return !exceptionsList ? null :
243        assertInstanceof(exceptionsList,
244                         options.contentSettings.ExceptionsList);
245  };
246
247  /**
248   * The browser's response to a request to check the validity of a given URL
249   * pattern.
250   * @param {string} type The content type.
251   * @param {string} mode The browser mode.
252   * @param {string} pattern The pattern.
253   * @param {boolean} valid Whether said pattern is valid in the context of
254   *     a content exception setting.
255   */
256  ContentSettings.patternValidityCheckComplete =
257      function(type, mode, pattern, valid) {
258    this.getExceptionsList(type, mode).patternValidityCheckComplete(pattern,
259                                                                    valid);
260  };
261
262  /**
263   * Shows/hides the link to the Pepper Flash camera and microphone default
264   * settings.
265   * Please note that whether the link is actually showed or not is also
266   * affected by the style class pepper-flash-settings.
267   */
268  ContentSettings.showMediaPepperFlashDefaultLink = function(show) {
269    $('media-pepper-flash-default').hidden = !show;
270  };
271
272  /**
273   * Shows/hides the link to the Pepper Flash camera and microphone
274   * site-specific settings.
275   * Please note that whether the link is actually showed or not is also
276   * affected by the style class pepper-flash-settings.
277   */
278  ContentSettings.showMediaPepperFlashExceptionsLink = function(show) {
279    $('media-pepper-flash-exceptions').hidden = !show;
280  };
281
282  /**
283   * Shows/hides the whole Web MIDI settings.
284   * @param {boolean} show Wether to show the whole Web MIDI settings.
285   */
286  ContentSettings.showExperimentalWebMIDISettings = function(show) {
287    $('experimental-web-midi-settings').hidden = !show;
288  };
289
290  /**
291   * Updates the microphone/camera devices menu with the given entries.
292   * @param {string} type The device type.
293   * @param {Array} devices List of available devices.
294   * @param {string} defaultdevice The unique id of the current default device.
295   */
296  ContentSettings.updateDevicesMenu = function(type, devices, defaultdevice) {
297    var deviceSelect = '';
298    if (type == 'mic') {
299      deviceSelect = $('media-select-mic');
300    } else if (type == 'camera') {
301      deviceSelect = $('media-select-camera');
302    } else {
303      console.error('Unknown device type for <device select> UI element: ' +
304                    type);
305      return;
306    }
307
308    deviceSelect.textContent = '';
309
310    var deviceCount = devices.length;
311    var defaultIndex = -1;
312    for (var i = 0; i < deviceCount; i++) {
313      var device = devices[i];
314      var option = new Option(device.name, device.id);
315      if (option.value == defaultdevice)
316        defaultIndex = i;
317      deviceSelect.appendChild(option);
318    }
319    if (defaultIndex >= 0)
320      deviceSelect.selectedIndex = defaultIndex;
321  };
322
323  /**
324   * Enables/disables the protected content exceptions button.
325   * @param {boolean} enable Whether to enable the button.
326   */
327  ContentSettings.enableProtectedContentExceptions = function(enable) {
328    var exceptionsButton = $('protected-content-exceptions');
329    if (exceptionsButton)
330      exceptionsButton.disabled = !enable;
331  };
332
333  /**
334   * Set the default microphone device based on the popup selection.
335   * @private
336   */
337  ContentSettings.setDefaultMicrophone_ = function() {
338    var deviceSelect = $('media-select-mic');
339    chrome.send('setDefaultCaptureDevice', ['mic', deviceSelect.value]);
340  };
341
342  /**
343   * Set the default camera device based on the popup selection.
344   * @private
345   */
346  ContentSettings.setDefaultCamera_ = function() {
347    var deviceSelect = $('media-select-camera');
348    chrome.send('setDefaultCaptureDevice', ['camera', deviceSelect.value]);
349  };
350
351  // Export
352  return {
353    ContentSettings: ContentSettings
354  };
355
356});
357