content_settings_handler.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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#include "chrome/browser/ui/webui/options/content_settings_handler.h"
6
7#include <algorithm>
8#include <map>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/bind_helpers.h"
13#include "base/command_line.h"
14#include "base/prefs/pref_service.h"
15#include "base/strings/string_number_conversions.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/chrome_notification_types.h"
20#include "chrome/browser/content_settings/content_settings_details.h"
21#include "chrome/browser/content_settings/content_settings_utils.h"
22#include "chrome/browser/content_settings/host_content_settings_map.h"
23#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
24#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
25#include "chrome/browser/extensions/extension_service.h"
26#include "chrome/browser/extensions/extension_special_storage_policy.h"
27#include "chrome/browser/notifications/desktop_notification_service.h"
28#include "chrome/browser/notifications/desktop_notification_service_factory.h"
29#include "chrome/browser/profiles/profile.h"
30#include "chrome/browser/ui/browser_list.h"
31#include "chrome/common/chrome_switches.h"
32#include "chrome/common/content_settings.h"
33#include "chrome/common/content_settings_pattern.h"
34#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
35#include "chrome/common/pref_names.h"
36#include "chrome/common/url_constants.h"
37#include "components/google/core/browser/google_util.h"
38#include "content/public/browser/notification_service.h"
39#include "content/public/browser/notification_source.h"
40#include "content/public/browser/notification_types.h"
41#include "content/public/browser/user_metrics.h"
42#include "content/public/browser/web_ui.h"
43#include "content/public/common/content_switches.h"
44#include "content/public/common/page_zoom.h"
45#include "extensions/common/extension_set.h"
46#include "extensions/common/permissions/api_permission.h"
47#include "extensions/common/permissions/permissions_data.h"
48#include "grit/generated_resources.h"
49#include "grit/locale_settings.h"
50#include "ui/base/l10n/l10n_util.h"
51
52#if defined(OS_CHROMEOS)
53#include "chrome/browser/chromeos/login/users/user_manager.h"
54#endif
55
56using base::UserMetricsAction;
57using extensions::APIPermission;
58
59namespace {
60
61struct ContentSettingsTypeNameEntry {
62  ContentSettingsType type;
63  const char* name;
64};
65
66// Maps from a secondary pattern to a setting.
67typedef std::map<ContentSettingsPattern, ContentSetting>
68    OnePatternSettings;
69// Maps from a primary pattern/source pair to a OnePatternSettings. All the
70// mappings in OnePatternSettings share the given primary pattern and source.
71typedef std::map<std::pair<ContentSettingsPattern, std::string>,
72                 OnePatternSettings>
73    AllPatternsSettings;
74
75// The AppFilter is used in AddExceptionsGrantedByHostedApps() to choose
76// extensions which should have their extent displayed.
77typedef bool (*AppFilter)(const extensions::Extension& app, Profile* profile);
78
79const char kExceptionsLearnMoreUrl[] =
80    "https://support.google.com/chrome/?p=settings_manage_exceptions";
81
82const char* kSetting = "setting";
83const char* kOrigin = "origin";
84const char* kSource = "source";
85const char* kAppName = "appName";
86const char* kAppId = "appId";
87const char* kEmbeddingOrigin = "embeddingOrigin";
88const char* kPreferencesSource = "preference";
89const char* kVideoSetting = "video";
90const char* kZoom = "zoom";
91
92const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
93  {CONTENT_SETTINGS_TYPE_COOKIES, "cookies"},
94  {CONTENT_SETTINGS_TYPE_IMAGES, "images"},
95  {CONTENT_SETTINGS_TYPE_JAVASCRIPT, "javascript"},
96  {CONTENT_SETTINGS_TYPE_PLUGINS, "plugins"},
97  {CONTENT_SETTINGS_TYPE_POPUPS, "popups"},
98  {CONTENT_SETTINGS_TYPE_GEOLOCATION, "location"},
99  {CONTENT_SETTINGS_TYPE_NOTIFICATIONS, "notifications"},
100  {CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE, "auto-select-certificate"},
101  {CONTENT_SETTINGS_TYPE_FULLSCREEN, "fullscreen"},
102  {CONTENT_SETTINGS_TYPE_MOUSELOCK, "mouselock"},
103  {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, "register-protocol-handler"},
104  {CONTENT_SETTINGS_TYPE_MEDIASTREAM, "media-stream"},
105  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic"},
106  {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera"},
107  {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker"},
108  {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "multiple-automatic-downloads"},
109  {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex"},
110#if defined(OS_CHROMEOS)
111  {CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, "protectedContent"},
112#endif
113};
114
115// A pseudo content type. We use it to display data like a content setting even
116// though it is not a real content setting.
117const char* kZoomContentType = "zoomlevels";
118
119ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name) {
120  for (size_t i = 0; i < arraysize(kContentSettingsTypeGroupNames); ++i) {
121    if (name == kContentSettingsTypeGroupNames[i].name)
122      return kContentSettingsTypeGroupNames[i].type;
123  }
124
125  NOTREACHED() << name << " is not a recognized content settings type.";
126  return CONTENT_SETTINGS_TYPE_DEFAULT;
127}
128
129std::string ContentSettingToString(ContentSetting setting) {
130  switch (setting) {
131    case CONTENT_SETTING_ALLOW:
132      return "allow";
133    case CONTENT_SETTING_ASK:
134      return "ask";
135    case CONTENT_SETTING_BLOCK:
136      return "block";
137    case CONTENT_SETTING_SESSION_ONLY:
138      return "session";
139    case CONTENT_SETTING_DEFAULT:
140      return "default";
141    case CONTENT_SETTING_NUM_SETTINGS:
142      NOTREACHED();
143  }
144
145  return std::string();
146}
147
148ContentSetting ContentSettingFromString(const std::string& name) {
149  if (name == "allow")
150    return CONTENT_SETTING_ALLOW;
151  if (name == "ask")
152    return CONTENT_SETTING_ASK;
153  if (name == "block")
154    return CONTENT_SETTING_BLOCK;
155  if (name == "session")
156    return CONTENT_SETTING_SESSION_ONLY;
157
158  NOTREACHED() << name << " is not a recognized content setting.";
159  return CONTENT_SETTING_DEFAULT;
160}
161
162// Create a DictionaryValue* that will act as a data source for a single row
163// in a HostContentSettingsMap-controlled exceptions table (e.g., cookies).
164// Ownership of the pointer is passed to the caller.
165base::DictionaryValue* GetExceptionForPage(
166    const ContentSettingsPattern& pattern,
167    const ContentSettingsPattern& secondary_pattern,
168    const ContentSetting& setting,
169    const std::string& provider_name) {
170  base::DictionaryValue* exception = new base::DictionaryValue();
171  exception->SetString(kOrigin, pattern.ToString());
172  exception->SetString(kEmbeddingOrigin,
173                       secondary_pattern == ContentSettingsPattern::Wildcard() ?
174                           std::string() :
175                           secondary_pattern.ToString());
176  exception->SetString(kSetting, ContentSettingToString(setting));
177  exception->SetString(kSource, provider_name);
178  return exception;
179}
180
181// Create a DictionaryValue* that will act as a data source for a single row
182// in the Geolocation exceptions table. Ownership of the pointer is passed to
183// the caller.
184base::DictionaryValue* GetGeolocationExceptionForPage(
185    const ContentSettingsPattern& origin,
186    const ContentSettingsPattern& embedding_origin,
187    ContentSetting setting) {
188  base::DictionaryValue* exception = new base::DictionaryValue();
189  exception->SetString(kSetting, ContentSettingToString(setting));
190  exception->SetString(kOrigin, origin.ToString());
191  exception->SetString(kEmbeddingOrigin, embedding_origin.ToString());
192  return exception;
193}
194
195// Create a DictionaryValue* that will act as a data source for a single row
196// in the desktop notifications exceptions table. Ownership of the pointer is
197// passed to the caller.
198base::DictionaryValue* GetNotificationExceptionForPage(
199    const ContentSettingsPattern& pattern,
200    ContentSetting setting,
201    const std::string& provider_name) {
202  base::DictionaryValue* exception = new base::DictionaryValue();
203  exception->SetString(kSetting, ContentSettingToString(setting));
204  exception->SetString(kOrigin, pattern.ToString());
205  exception->SetString(kSource, provider_name);
206  return exception;
207}
208
209// Returns true whenever the |extension| is hosted and has |permission|.
210// Must have the AppFilter signature.
211template <APIPermission::ID permission>
212bool HostedAppHasPermission(
213    const extensions::Extension& extension, Profile* /*profile*/) {
214  return extension.is_hosted_app() &&
215         extension.permissions_data()->HasAPIPermission(permission);
216}
217
218// Add an "Allow"-entry to the list of |exceptions| for a |url_pattern| from
219// the web extent of a hosted |app|.
220void AddExceptionForHostedApp(const std::string& url_pattern,
221    const extensions::Extension& app, base::ListValue* exceptions) {
222  base::DictionaryValue* exception = new base::DictionaryValue();
223  exception->SetString(kSetting, ContentSettingToString(CONTENT_SETTING_ALLOW));
224  exception->SetString(kOrigin, url_pattern);
225  exception->SetString(kEmbeddingOrigin, url_pattern);
226  exception->SetString(kSource, "HostedApp");
227  exception->SetString(kAppName, app.name());
228  exception->SetString(kAppId, app.id());
229  exceptions->Append(exception);
230}
231
232// Asks the |profile| for hosted apps which have the |permission| set, and
233// adds their web extent and launch URL to the |exceptions| list.
234void AddExceptionsGrantedByHostedApps(
235    Profile* profile, AppFilter app_filter, base::ListValue* exceptions) {
236  const ExtensionService* extension_service = profile->GetExtensionService();
237  // After ExtensionSystem::Init has been called at the browser's start,
238  // GetExtensionService() should not return NULL, so this is safe:
239  const extensions::ExtensionSet* extensions = extension_service->extensions();
240
241  for (extensions::ExtensionSet::const_iterator extension = extensions->begin();
242       extension != extensions->end(); ++extension) {
243    if (!app_filter(*extension->get(), profile))
244      continue;
245
246    extensions::URLPatternSet web_extent = (*extension)->web_extent();
247    // Add patterns from web extent.
248    for (extensions::URLPatternSet::const_iterator pattern = web_extent.begin();
249         pattern != web_extent.end(); ++pattern) {
250      std::string url_pattern = pattern->GetAsString();
251      AddExceptionForHostedApp(url_pattern, *extension->get(), exceptions);
252    }
253    // Retrieve the launch URL.
254    GURL launch_url =
255        extensions::AppLaunchInfo::GetLaunchWebURL(extension->get());
256    // Skip adding the launch URL if it is part of the web extent.
257    if (web_extent.MatchesURL(launch_url))
258      continue;
259    AddExceptionForHostedApp(launch_url.spec(), *extension->get(), exceptions);
260  }
261}
262
263// Sort ZoomLevelChanges by host and scheme
264// (a.com < http://a.com < https://a.com < b.com).
265bool HostZoomSort(const content::HostZoomMap::ZoomLevelChange& a,
266                  const content::HostZoomMap::ZoomLevelChange& b) {
267  return a.host == b.host ? a.scheme < b.scheme : a.host < b.host;
268}
269
270}  // namespace
271
272namespace options {
273
274ContentSettingsHandler::MediaSettingsInfo::MediaSettingsInfo()
275    : flash_default_setting(CONTENT_SETTING_DEFAULT),
276      flash_settings_initialized(false),
277      last_flash_refresh_request_id(0),
278      show_flash_default_link(false),
279      show_flash_exceptions_link(false),
280      default_setting(CONTENT_SETTING_DEFAULT),
281      policy_disable_audio(false),
282      policy_disable_video(false),
283      default_setting_initialized(false),
284      exceptions_initialized(false) {
285}
286
287ContentSettingsHandler::MediaSettingsInfo::~MediaSettingsInfo() {
288}
289
290ContentSettingsHandler::ContentSettingsHandler() {
291}
292
293ContentSettingsHandler::~ContentSettingsHandler() {
294}
295
296void ContentSettingsHandler::GetLocalizedValues(
297    base::DictionaryValue* localized_strings) {
298  DCHECK(localized_strings);
299
300  static OptionsStringResource resources[] = {
301    { "allowException", IDS_EXCEPTIONS_ALLOW_BUTTON },
302    { "blockException", IDS_EXCEPTIONS_BLOCK_BUTTON },
303    { "sessionException", IDS_EXCEPTIONS_SESSION_ONLY_BUTTON },
304    { "askException", IDS_EXCEPTIONS_ASK_BUTTON },
305    { "otr_exceptions_explanation", IDS_EXCEPTIONS_OTR_LABEL },
306    { "addNewExceptionInstructions", IDS_EXCEPTIONS_ADD_NEW_INSTRUCTIONS },
307    { "manageExceptions", IDS_EXCEPTIONS_MANAGE },
308    { "manage_handlers", IDS_HANDLERS_MANAGE },
309    { "exceptionPatternHeader", IDS_EXCEPTIONS_PATTERN_HEADER },
310    { "exceptionBehaviorHeader", IDS_EXCEPTIONS_ACTION_HEADER },
311    { "exceptionZoomHeader", IDS_EXCEPTIONS_ZOOM_HEADER },
312    { "embeddedOnHost", IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST },
313    // Cookies filter.
314    { "cookies_tab_label", IDS_COOKIES_TAB_LABEL },
315    { "cookies_header", IDS_COOKIES_HEADER },
316    { "cookies_allow", IDS_COOKIES_ALLOW_RADIO },
317    { "cookies_block", IDS_COOKIES_BLOCK_RADIO },
318    { "cookies_session_only", IDS_COOKIES_SESSION_ONLY_RADIO },
319    { "cookies_block_3rd_party", IDS_COOKIES_BLOCK_3RDPARTY_CHKBOX },
320    { "cookies_clear_when_close", IDS_COOKIES_CLEAR_WHEN_CLOSE_CHKBOX },
321    { "cookies_lso_clear_when_close", IDS_COOKIES_LSO_CLEAR_WHEN_CLOSE_CHKBOX },
322    { "cookies_show_cookies", IDS_COOKIES_SHOW_COOKIES_BUTTON },
323    { "flash_storage_settings", IDS_FLASH_STORAGE_SETTINGS },
324    { "flash_storage_url", IDS_FLASH_STORAGE_URL },
325#if defined(ENABLE_GOOGLE_NOW)
326    { "googleGeolocationAccessEnable",
327       IDS_GEOLOCATION_GOOGLE_ACCESS_ENABLE_CHKBOX },
328#endif
329    // Image filter.
330    { "images_tab_label", IDS_IMAGES_TAB_LABEL },
331    { "images_header", IDS_IMAGES_HEADER },
332    { "images_allow", IDS_IMAGES_LOAD_RADIO },
333    { "images_block", IDS_IMAGES_NOLOAD_RADIO },
334    // JavaScript filter.
335    { "javascript_tab_label", IDS_JAVASCRIPT_TAB_LABEL },
336    { "javascript_header", IDS_JAVASCRIPT_HEADER },
337    { "javascript_allow", IDS_JS_ALLOW_RADIO },
338    { "javascript_block", IDS_JS_DONOTALLOW_RADIO },
339    // Plug-ins filter.
340    { "plugins_tab_label", IDS_PLUGIN_TAB_LABEL },
341    { "plugins_header", IDS_PLUGIN_HEADER },
342    { "plugins_ask", IDS_PLUGIN_ASK_RADIO },
343    { "plugins_allow", IDS_PLUGIN_LOAD_RADIO },
344    { "plugins_block", IDS_PLUGIN_NOLOAD_RADIO },
345    { "disableIndividualPlugins", IDS_PLUGIN_SELECTIVE_DISABLE },
346    // Pop-ups filter.
347    { "popups_tab_label", IDS_POPUP_TAB_LABEL },
348    { "popups_header", IDS_POPUP_HEADER },
349    { "popups_allow", IDS_POPUP_ALLOW_RADIO },
350    { "popups_block", IDS_POPUP_BLOCK_RADIO },
351    // Location filter.
352    { "location_tab_label", IDS_GEOLOCATION_TAB_LABEL },
353    { "location_header", IDS_GEOLOCATION_HEADER },
354    { "location_allow", IDS_GEOLOCATION_ALLOW_RADIO },
355    { "location_ask", IDS_GEOLOCATION_ASK_RADIO },
356    { "location_block", IDS_GEOLOCATION_BLOCK_RADIO },
357    { "set_by", IDS_GEOLOCATION_SET_BY_HOVER },
358    // Notifications filter.
359    { "notifications_tab_label", IDS_NOTIFICATIONS_TAB_LABEL },
360    { "notifications_header", IDS_NOTIFICATIONS_HEADER },
361    { "notifications_allow", IDS_NOTIFICATIONS_ALLOW_RADIO },
362    { "notifications_ask", IDS_NOTIFICATIONS_ASK_RADIO },
363    { "notifications_block", IDS_NOTIFICATIONS_BLOCK_RADIO },
364    // Fullscreen filter.
365    { "fullscreen_tab_label", IDS_FULLSCREEN_TAB_LABEL },
366    { "fullscreen_header", IDS_FULLSCREEN_HEADER },
367    // Mouse Lock filter.
368    { "mouselock_tab_label", IDS_MOUSE_LOCK_TAB_LABEL },
369    { "mouselock_header", IDS_MOUSE_LOCK_HEADER },
370    { "mouselock_allow", IDS_MOUSE_LOCK_ALLOW_RADIO },
371    { "mouselock_ask", IDS_MOUSE_LOCK_ASK_RADIO },
372    { "mouselock_block", IDS_MOUSE_LOCK_BLOCK_RADIO },
373#if defined(OS_CHROMEOS) || defined(OS_WIN)
374    // Protected Content filter
375    { "protectedContentTabLabel", IDS_PROTECTED_CONTENT_TAB_LABEL },
376    { "protectedContentInfo", IDS_PROTECTED_CONTENT_INFO },
377    { "protectedContentEnable", IDS_PROTECTED_CONTENT_ENABLE },
378    { "protectedContent_header", IDS_PROTECTED_CONTENT_HEADER },
379#endif  // defined(OS_CHROMEOS) || defined(OS_WIN)
380    // Media stream capture device filter.
381    { "mediaStreamTabLabel", IDS_MEDIA_STREAM_TAB_LABEL },
382    { "media-stream_header", IDS_MEDIA_STREAM_HEADER },
383    { "mediaStreamAsk", IDS_MEDIA_STREAM_ASK_RADIO },
384    { "mediaStreamBlock", IDS_MEDIA_STREAM_BLOCK_RADIO },
385    { "mediaStreamAudioAsk", IDS_MEDIA_STREAM_ASK_AUDIO_ONLY_RADIO },
386    { "mediaStreamAudioBlock", IDS_MEDIA_STREAM_BLOCK_AUDIO_ONLY_RADIO },
387    { "mediaStreamVideoAsk", IDS_MEDIA_STREAM_ASK_VIDEO_ONLY_RADIO },
388    { "mediaStreamVideoBlock", IDS_MEDIA_STREAM_BLOCK_VIDEO_ONLY_RADIO },
389    { "mediaStreamBubbleAudio", IDS_MEDIA_STREAM_AUDIO_MANAGED },
390    { "mediaStreamBubbleVideo", IDS_MEDIA_STREAM_VIDEO_MANAGED },
391    { "mediaAudioExceptionHeader", IDS_MEDIA_AUDIO_EXCEPTION_HEADER },
392    { "mediaVideoExceptionHeader", IDS_MEDIA_VIDEO_EXCEPTION_HEADER },
393    { "mediaPepperFlashDefaultDivergedLabel",
394      IDS_MEDIA_PEPPER_FLASH_DEFAULT_DIVERGED_LABEL },
395    { "mediaPepperFlashExceptionsDivergedLabel",
396      IDS_MEDIA_PEPPER_FLASH_EXCEPTIONS_DIVERGED_LABEL },
397    { "mediaPepperFlashChangeLink", IDS_MEDIA_PEPPER_FLASH_CHANGE_LINK },
398    { "mediaPepperFlashGlobalPrivacyURL", IDS_FLASH_GLOBAL_PRIVACY_URL },
399    { "mediaPepperFlashWebsitePrivacyURL", IDS_FLASH_WEBSITE_PRIVACY_URL },
400    // PPAPI broker filter.
401    { "ppapi-broker_header", IDS_PPAPI_BROKER_HEADER },
402    { "ppapiBrokerTabLabel", IDS_PPAPI_BROKER_TAB_LABEL },
403    { "ppapi_broker_allow", IDS_PPAPI_BROKER_ALLOW_RADIO },
404    { "ppapi_broker_ask", IDS_PPAPI_BROKER_ASK_RADIO },
405    { "ppapi_broker_block", IDS_PPAPI_BROKER_BLOCK_RADIO },
406    // Multiple automatic downloads
407    { "multiple-automatic-downloads_header",
408      IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL },
409    { "multiple-automatic-downloads_allow",
410      IDS_AUTOMATIC_DOWNLOADS_ALLOW_RADIO },
411    { "multiple-automatic-downloads_ask",
412      IDS_AUTOMATIC_DOWNLOADS_ASK_RADIO },
413    { "multiple-automatic-downloads_block",
414      IDS_AUTOMATIC_DOWNLOADS_BLOCK_RADIO },
415    // MIDI system exclusive messages
416    { "midi-sysex_header", IDS_MIDI_SYSEX_TAB_LABEL },
417    { "midiSysExAllow", IDS_MIDI_SYSEX_ALLOW_RADIO },
418    { "midiSysExAsk", IDS_MIDI_SYSEX_ASK_RADIO },
419    { "midiSysExBlock", IDS_MIDI_SYSEX_BLOCK_RADIO },
420    { "zoomlevels_header", IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL },
421    { "zoomLevelsManage", IDS_ZOOMLEVELS_MANAGE_BUTTON },
422  };
423
424  RegisterStrings(localized_strings, resources, arraysize(resources));
425  RegisterTitle(localized_strings, "contentSettingsPage",
426                IDS_CONTENT_SETTINGS_TITLE);
427
428  // Register titles for each of the individual settings whose exception
429  // dialogs will be processed by |ContentSettingsHandler|.
430  RegisterTitle(localized_strings, "cookies",
431                IDS_COOKIES_TAB_LABEL);
432  RegisterTitle(localized_strings, "images",
433                IDS_IMAGES_TAB_LABEL);
434  RegisterTitle(localized_strings, "javascript",
435                IDS_JAVASCRIPT_TAB_LABEL);
436  RegisterTitle(localized_strings, "plugins",
437                IDS_PLUGIN_TAB_LABEL);
438  RegisterTitle(localized_strings, "popups",
439                IDS_POPUP_TAB_LABEL);
440  RegisterTitle(localized_strings, "location",
441                IDS_GEOLOCATION_TAB_LABEL);
442  RegisterTitle(localized_strings, "notifications",
443                IDS_NOTIFICATIONS_TAB_LABEL);
444  RegisterTitle(localized_strings, "fullscreen",
445                IDS_FULLSCREEN_TAB_LABEL);
446  RegisterTitle(localized_strings, "mouselock",
447                IDS_MOUSE_LOCK_TAB_LABEL);
448#if defined(OS_CHROMEOS)
449  RegisterTitle(localized_strings, "protectedContent",
450                IDS_PROTECTED_CONTENT_TAB_LABEL);
451#endif
452  RegisterTitle(localized_strings, "media-stream",
453                IDS_MEDIA_STREAM_TAB_LABEL);
454  RegisterTitle(localized_strings, "ppapi-broker",
455                IDS_PPAPI_BROKER_TAB_LABEL);
456  RegisterTitle(localized_strings, "multiple-automatic-downloads",
457                IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL);
458  RegisterTitle(localized_strings, "midi-sysex",
459                IDS_MIDI_SYSEX_TAB_LABEL);
460  RegisterTitle(localized_strings, "zoomlevels",
461                IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL);
462
463  localized_strings->SetString("exceptionsLearnMoreUrl",
464                               kExceptionsLearnMoreUrl);
465}
466
467void ContentSettingsHandler::InitializeHandler() {
468  notification_registrar_.Add(
469      this, chrome::NOTIFICATION_PROFILE_CREATED,
470      content::NotificationService::AllSources());
471  notification_registrar_.Add(
472      this, chrome::NOTIFICATION_PROFILE_DESTROYED,
473      content::NotificationService::AllSources());
474
475  notification_registrar_.Add(
476      this, chrome::NOTIFICATION_CONTENT_SETTINGS_CHANGED,
477      content::NotificationService::AllSources());
478  notification_registrar_.Add(
479      this, chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
480      content::NotificationService::AllSources());
481  Profile* profile = Profile::FromWebUI(web_ui());
482  notification_registrar_.Add(
483      this, chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
484      content::Source<Profile>(profile));
485
486  PrefService* prefs = profile->GetPrefs();
487  pref_change_registrar_.Init(prefs);
488  pref_change_registrar_.Add(
489      prefs::kPepperFlashSettingsEnabled,
490      base::Bind(&ContentSettingsHandler::OnPepperFlashPrefChanged,
491                 base::Unretained(this)));
492  pref_change_registrar_.Add(
493      prefs::kAudioCaptureAllowed,
494      base::Bind(&ContentSettingsHandler::UpdateMediaSettingsView,
495                 base::Unretained(this)));
496  pref_change_registrar_.Add(
497      prefs::kVideoCaptureAllowed,
498      base::Bind(&ContentSettingsHandler::UpdateMediaSettingsView,
499                 base::Unretained(this)));
500  pref_change_registrar_.Add(
501      prefs::kEnableDRM,
502      base::Bind(
503          &ContentSettingsHandler::UpdateProtectedContentExceptionsButton,
504          base::Unretained(this)));
505
506  content::HostZoomMap* host_zoom_map =
507      content::HostZoomMap::GetForBrowserContext(profile);
508  host_zoom_map_subscription_ =
509      host_zoom_map->AddZoomLevelChangedCallback(
510          base::Bind(&ContentSettingsHandler::OnZoomLevelChanged,
511                     base::Unretained(this)));
512
513  flash_settings_manager_.reset(new PepperFlashSettingsManager(this, profile));
514}
515
516void ContentSettingsHandler::InitializePage() {
517  media_settings_ = MediaSettingsInfo();
518  RefreshFlashMediaSettings();
519
520  UpdateHandlersEnabledRadios();
521  UpdateAllExceptionsViewsFromModel();
522  UpdateProtectedContentExceptionsButton();
523}
524
525void ContentSettingsHandler::Observe(
526    int type,
527    const content::NotificationSource& source,
528    const content::NotificationDetails& details) {
529  switch (type) {
530    case chrome::NOTIFICATION_PROFILE_DESTROYED: {
531      if (content::Source<Profile>(source).ptr()->IsOffTheRecord()) {
532        web_ui()->CallJavascriptFunction(
533            "ContentSettingsExceptionsArea.OTRProfileDestroyed");
534      }
535      break;
536    }
537
538    case chrome::NOTIFICATION_PROFILE_CREATED: {
539      if (content::Source<Profile>(source).ptr()->IsOffTheRecord())
540        UpdateAllOTRExceptionsViewsFromModel();
541      break;
542    }
543
544    case chrome::NOTIFICATION_CONTENT_SETTINGS_CHANGED: {
545      // Filter out notifications from other profiles.
546      HostContentSettingsMap* map =
547          content::Source<HostContentSettingsMap>(source).ptr();
548      if (map != GetContentSettingsMap() &&
549          map != GetOTRContentSettingsMap())
550        break;
551
552      const ContentSettingsDetails* settings_details =
553          content::Details<const ContentSettingsDetails>(details).ptr();
554
555      // TODO(estade): we pretend update_all() is always true.
556      if (settings_details->update_all_types())
557        UpdateAllExceptionsViewsFromModel();
558      else
559        UpdateExceptionsViewFromModel(settings_details->type());
560      break;
561    }
562
563    case chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED: {
564      UpdateNotificationExceptionsView();
565      break;
566    }
567
568    case chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED: {
569      UpdateHandlersEnabledRadios();
570      break;
571    }
572  }
573}
574
575void ContentSettingsHandler::OnGetPermissionSettingsCompleted(
576    uint32 request_id,
577    bool success,
578    PP_Flash_BrowserOperations_Permission default_permission,
579    const ppapi::FlashSiteSettings& sites) {
580  if (success && request_id == media_settings_.last_flash_refresh_request_id) {
581    media_settings_.flash_settings_initialized = true;
582    media_settings_.flash_default_setting =
583        PepperFlashContentSettingsUtils::FlashPermissionToContentSetting(
584            default_permission);
585    PepperFlashContentSettingsUtils::FlashSiteSettingsToMediaExceptions(
586        sites, &media_settings_.flash_exceptions);
587    PepperFlashContentSettingsUtils::SortMediaExceptions(
588        &media_settings_.flash_exceptions);
589
590    UpdateFlashMediaLinksVisibility();
591  }
592}
593
594void ContentSettingsHandler::UpdateSettingDefaultFromModel(
595    ContentSettingsType type) {
596  base::DictionaryValue filter_settings;
597  std::string provider_id;
598  filter_settings.SetString(ContentSettingsTypeToGroupName(type) + ".value",
599                            GetSettingDefaultFromModel(type, &provider_id));
600  filter_settings.SetString(
601      ContentSettingsTypeToGroupName(type) + ".managedBy", provider_id);
602
603  web_ui()->CallJavascriptFunction(
604      "ContentSettings.setContentFilterSettingsValue", filter_settings);
605}
606
607void ContentSettingsHandler::UpdateMediaSettingsView() {
608  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
609  bool audio_disabled = !prefs->GetBoolean(prefs::kAudioCaptureAllowed) &&
610      prefs->IsManagedPreference(prefs::kAudioCaptureAllowed);
611  bool video_disabled = !prefs->GetBoolean(prefs::kVideoCaptureAllowed) &&
612      prefs->IsManagedPreference(prefs::kVideoCaptureAllowed);
613
614  media_settings_.policy_disable_audio = audio_disabled;
615  media_settings_.policy_disable_video = video_disabled;
616  media_settings_.default_setting =
617      GetContentSettingsMap()->GetDefaultContentSetting(
618          CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
619  media_settings_.default_setting_initialized = true;
620  UpdateFlashMediaLinksVisibility();
621
622  base::DictionaryValue media_ui_settings;
623  media_ui_settings.SetBoolean("cameraDisabled", video_disabled);
624  media_ui_settings.SetBoolean("micDisabled", audio_disabled);
625
626  // In case only video is enabled change the text appropriately.
627  if (audio_disabled && !video_disabled) {
628    media_ui_settings.SetString("askText", "mediaStreamVideoAsk");
629    media_ui_settings.SetString("blockText", "mediaStreamVideoBlock");
630    media_ui_settings.SetBoolean("showBubble", true);
631    media_ui_settings.SetString("bubbleText", "mediaStreamBubbleAudio");
632
633    web_ui()->CallJavascriptFunction("ContentSettings.updateMediaUI",
634                                     media_ui_settings);
635    return;
636  }
637
638  // In case only audio is enabled change the text appropriately.
639  if (video_disabled && !audio_disabled) {
640    base::DictionaryValue media_ui_settings;
641    media_ui_settings.SetString("askText", "mediaStreamAudioAsk");
642    media_ui_settings.SetString("blockText", "mediaStreamAudioBlock");
643    media_ui_settings.SetBoolean("showBubble", true);
644    media_ui_settings.SetString("bubbleText", "mediaStreamBubbleVideo");
645
646    web_ui()->CallJavascriptFunction("ContentSettings.updateMediaUI",
647                                     media_ui_settings);
648    return;
649  }
650
651  if (audio_disabled && video_disabled) {
652    // Fake policy controlled default because the user can not change anything
653    // until both audio and video are blocked.
654    base::DictionaryValue filter_settings;
655    std::string group_name =
656        ContentSettingsTypeToGroupName(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
657    filter_settings.SetString(group_name + ".value",
658                              ContentSettingToString(CONTENT_SETTING_BLOCK));
659    filter_settings.SetString(group_name + ".managedBy", "policy");
660    web_ui()->CallJavascriptFunction(
661        "ContentSettings.setContentFilterSettingsValue", filter_settings);
662  }
663
664  media_ui_settings.SetString("askText", "mediaStreamAsk");
665  media_ui_settings.SetString("blockText", "mediaStreamBlock");
666  media_ui_settings.SetBoolean("showBubble", false);
667  media_ui_settings.SetString("bubbleText", std::string());
668
669  web_ui()->CallJavascriptFunction("ContentSettings.updateMediaUI",
670                                   media_ui_settings);
671}
672
673std::string ContentSettingsHandler::GetSettingDefaultFromModel(
674    ContentSettingsType type, std::string* provider_id) {
675  Profile* profile = Profile::FromWebUI(web_ui());
676  ContentSetting default_setting;
677  if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
678    default_setting =
679        DesktopNotificationServiceFactory::GetForProfile(profile)->
680            GetDefaultContentSetting(provider_id);
681  } else {
682    default_setting =
683        profile->GetHostContentSettingsMap()->
684            GetDefaultContentSetting(type, provider_id);
685  }
686
687  return ContentSettingToString(default_setting);
688}
689
690void ContentSettingsHandler::UpdateHandlersEnabledRadios() {
691  base::FundamentalValue handlers_enabled(
692      GetProtocolHandlerRegistry()->enabled());
693
694  web_ui()->CallJavascriptFunction(
695      "ContentSettings.updateHandlersEnabledRadios",
696      handlers_enabled);
697}
698
699void ContentSettingsHandler::UpdateAllExceptionsViewsFromModel() {
700  for (int type = CONTENT_SETTINGS_TYPE_DEFAULT + 1;
701       type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
702    UpdateExceptionsViewFromModel(static_cast<ContentSettingsType>(type));
703  }
704  // Zoom levels are not actually a content type so we need to handle them
705  // separately.
706  UpdateZoomLevelsExceptionsView();
707}
708
709void ContentSettingsHandler::UpdateAllOTRExceptionsViewsFromModel() {
710  for (int type = CONTENT_SETTINGS_TYPE_DEFAULT + 1;
711       type < CONTENT_SETTINGS_NUM_TYPES; ++type) {
712    UpdateOTRExceptionsViewFromModel(static_cast<ContentSettingsType>(type));
713  }
714}
715
716void ContentSettingsHandler::UpdateExceptionsViewFromModel(
717    ContentSettingsType type) {
718  switch (type) {
719    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
720      UpdateGeolocationExceptionsView();
721      break;
722    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
723      UpdateNotificationExceptionsView();
724      break;
725    case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
726      UpdateMediaSettingsView();
727      break;
728    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
729    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
730      UpdateMediaExceptionsView();
731      break;
732    case CONTENT_SETTINGS_TYPE_MIXEDSCRIPT:
733      // We don't yet support exceptions for mixed scripting.
734      break;
735    case CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE:
736      // The content settings type CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE
737      // is supposed to be set by policy only. Hence there is no user facing UI
738      // for this content type and we skip it here.
739      break;
740    case CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS:
741      // The RPH settings are retrieved separately.
742      break;
743    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
744      UpdateMIDISysExExceptionsView();
745      break;
746#if defined(OS_WIN)
747    case CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP:
748      break;
749#endif
750    default:
751      UpdateExceptionsViewFromHostContentSettingsMap(type);
752      break;
753  }
754}
755
756void ContentSettingsHandler::UpdateOTRExceptionsViewFromModel(
757    ContentSettingsType type) {
758  switch (type) {
759    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
760    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
761    case CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE:
762    case CONTENT_SETTINGS_TYPE_MIXEDSCRIPT:
763#if defined(OS_WIN)
764    case CONTENT_SETTINGS_TYPE_METRO_SWITCH_TO_DESKTOP:
765#endif
766    case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
767    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
768    case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
769    case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS:
770    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
771      break;
772    default:
773      UpdateExceptionsViewFromOTRHostContentSettingsMap(type);
774      break;
775  }
776}
777
778// TODO(estade): merge with GetExceptionsFromHostContentSettingsMap.
779void ContentSettingsHandler::UpdateGeolocationExceptionsView() {
780  Profile* profile = Profile::FromWebUI(web_ui());
781  HostContentSettingsMap* map = profile->GetHostContentSettingsMap();
782
783  ContentSettingsForOneType all_settings;
784  map->GetSettingsForOneType(
785      CONTENT_SETTINGS_TYPE_GEOLOCATION,
786      std::string(),
787      &all_settings);
788
789  // Group geolocation settings by primary_pattern.
790  AllPatternsSettings all_patterns_settings;
791  for (ContentSettingsForOneType::iterator i = all_settings.begin();
792       i != all_settings.end(); ++i) {
793    // Don't add default settings.
794    if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
795        i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
796        i->source != kPreferencesSource) {
797      continue;
798    }
799    all_patterns_settings[std::make_pair(i->primary_pattern, i->source)]
800        [i->secondary_pattern] = i->setting;
801  }
802
803  base::ListValue exceptions;
804  AddExceptionsGrantedByHostedApps(
805      profile,
806      HostedAppHasPermission<APIPermission::kGeolocation>,
807      &exceptions);
808
809  for (AllPatternsSettings::iterator i = all_patterns_settings.begin();
810       i != all_patterns_settings.end(); ++i) {
811    const ContentSettingsPattern& primary_pattern = i->first.first;
812    const OnePatternSettings& one_settings = i->second;
813
814    OnePatternSettings::const_iterator parent =
815        one_settings.find(primary_pattern);
816
817    // Add the "parent" entry for the non-embedded setting.
818    ContentSetting parent_setting =
819        parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
820    exceptions.Append(GetGeolocationExceptionForPage(primary_pattern,
821                                                     primary_pattern,
822                                                     parent_setting));
823
824    // Add the "children" for any embedded settings.
825    for (OnePatternSettings::const_iterator j = one_settings.begin();
826         j != one_settings.end();
827         ++j) {
828      // Skip the non-embedded setting which we already added above.
829      if (j == parent)
830        continue;
831
832      exceptions.Append(GetGeolocationExceptionForPage(
833          primary_pattern, j->first, j->second));
834    }
835  }
836
837  base::StringValue type_string(
838      ContentSettingsTypeToGroupName(CONTENT_SETTINGS_TYPE_GEOLOCATION));
839  web_ui()->CallJavascriptFunction("ContentSettings.setExceptions",
840                                   type_string, exceptions);
841
842  // This is mainly here to keep this function ideologically parallel to
843  // UpdateExceptionsViewFromHostContentSettingsMap().
844  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_GEOLOCATION);
845}
846
847void ContentSettingsHandler::UpdateNotificationExceptionsView() {
848  Profile* profile = Profile::FromWebUI(web_ui());
849  DesktopNotificationService* service =
850      DesktopNotificationServiceFactory::GetForProfile(profile);
851
852  ContentSettingsForOneType settings;
853  service->GetNotificationsSettings(&settings);
854
855  base::ListValue exceptions;
856  AddExceptionsGrantedByHostedApps(profile,
857      HostedAppHasPermission<APIPermission::kNotification>,
858      &exceptions);
859
860  for (ContentSettingsForOneType::const_iterator i =
861           settings.begin();
862       i != settings.end();
863       ++i) {
864    // Don't add default settings.
865    if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
866        i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
867        i->source != kPreferencesSource) {
868      continue;
869    }
870
871    exceptions.Append(
872        GetNotificationExceptionForPage(i->primary_pattern, i->setting,
873                                        i->source));
874  }
875
876  base::StringValue type_string(
877      ContentSettingsTypeToGroupName(CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
878  web_ui()->CallJavascriptFunction("ContentSettings.setExceptions",
879                                   type_string, exceptions);
880
881  // This is mainly here to keep this function ideologically parallel to
882  // UpdateExceptionsViewFromHostContentSettingsMap().
883  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
884}
885
886void ContentSettingsHandler::UpdateMediaExceptionsView() {
887  base::ListValue media_exceptions;
888  GetExceptionsFromHostContentSettingsMap(
889      GetContentSettingsMap(),
890      CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
891      &media_exceptions);
892
893  base::ListValue video_exceptions;
894  GetExceptionsFromHostContentSettingsMap(
895      GetContentSettingsMap(),
896      CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
897      &video_exceptions);
898
899  // Merge the |video_exceptions| list to |media_exceptions| list.
900  std::map<std::string, base::DictionaryValue*> entries_map;
901  for (base::ListValue::const_iterator media_entry(media_exceptions.begin());
902       media_entry != media_exceptions.end(); ++media_entry) {
903    base::DictionaryValue* media_dict = NULL;
904    if (!(*media_entry)->GetAsDictionary(&media_dict))
905      NOTREACHED();
906
907    media_dict->SetString(kVideoSetting,
908                          ContentSettingToString(CONTENT_SETTING_ASK));
909
910    std::string media_origin;
911    media_dict->GetString(kOrigin, &media_origin);
912    entries_map[media_origin] = media_dict;
913  }
914
915  for (base::ListValue::iterator video_entry = video_exceptions.begin();
916       video_entry != video_exceptions.end(); ++video_entry) {
917    base::DictionaryValue* video_dict = NULL;
918    if (!(*video_entry)->GetAsDictionary(&video_dict))
919      NOTREACHED();
920
921    std::string video_origin;
922    std::string video_setting;
923    video_dict->GetString(kOrigin, &video_origin);
924    video_dict->GetString(kSetting, &video_setting);
925
926    std::map<std::string, base::DictionaryValue*>::iterator iter =
927        entries_map.find(video_origin);
928    if (iter == entries_map.end()) {
929      base::DictionaryValue* exception = new base::DictionaryValue();
930      exception->SetString(kOrigin, video_origin);
931      exception->SetString(kSetting,
932                           ContentSettingToString(CONTENT_SETTING_ASK));
933      exception->SetString(kVideoSetting, video_setting);
934      exception->SetString(kSource, kPreferencesSource);
935
936      // Append the new entry to the list and map.
937      media_exceptions.Append(exception);
938      entries_map[video_origin] = exception;
939    } else {
940      // Modify the existing entry.
941      iter->second->SetString(kVideoSetting, video_setting);
942    }
943  }
944
945  media_settings_.exceptions.clear();
946  for (base::ListValue::const_iterator media_entry = media_exceptions.begin();
947       media_entry != media_exceptions.end(); ++media_entry) {
948    base::DictionaryValue* media_dict = NULL;
949    bool result = (*media_entry)->GetAsDictionary(&media_dict);
950    DCHECK(result);
951
952    std::string origin;
953    std::string audio_setting;
954    std::string video_setting;
955    media_dict->GetString(kOrigin, &origin);
956    media_dict->GetString(kSetting, &audio_setting);
957    media_dict->GetString(kVideoSetting, &video_setting);
958    media_settings_.exceptions.push_back(MediaException(
959        ContentSettingsPattern::FromString(origin),
960        ContentSettingFromString(audio_setting),
961        ContentSettingFromString(video_setting)));
962  }
963  PepperFlashContentSettingsUtils::SortMediaExceptions(
964      &media_settings_.exceptions);
965  media_settings_.exceptions_initialized = true;
966  UpdateFlashMediaLinksVisibility();
967
968  base::StringValue type_string(
969       ContentSettingsTypeToGroupName(CONTENT_SETTINGS_TYPE_MEDIASTREAM));
970  web_ui()->CallJavascriptFunction("ContentSettings.setExceptions",
971                                   type_string, media_exceptions);
972
973  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MEDIASTREAM);
974}
975
976void ContentSettingsHandler::UpdateMIDISysExExceptionsView() {
977  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableWebMIDI)) {
978    web_ui()->CallJavascriptFunction(
979        "ContentSettings.showExperimentalWebMIDISettings",
980        base::FundamentalValue(true));
981  }
982
983  UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
984  UpdateExceptionsViewFromHostContentSettingsMap(
985      CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
986}
987
988void ContentSettingsHandler::UpdateZoomLevelsExceptionsView() {
989  base::ListValue zoom_levels_exceptions;
990
991  content::HostZoomMap* host_zoom_map =
992      content::HostZoomMap::GetForBrowserContext(Profile::FromWebUI(web_ui()));
993  content::HostZoomMap::ZoomLevelVector zoom_levels(
994      host_zoom_map->GetAllZoomLevels());
995  std::sort(zoom_levels.begin(), zoom_levels.end(), HostZoomSort);
996
997  for (content::HostZoomMap::ZoomLevelVector::const_iterator i =
998           zoom_levels.begin();
999       i != zoom_levels.end();
1000       ++i) {
1001    scoped_ptr<base::DictionaryValue> exception(new base::DictionaryValue);
1002    switch (i->mode) {
1003      case content::HostZoomMap::ZOOM_CHANGED_FOR_HOST:
1004        exception->SetString(kOrigin, i->host);
1005        break;
1006      case content::HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST:
1007        // These are not stored in preferences and get cleared on next browser
1008        // start. Therefore, we don't care for them.
1009        break;
1010      case content::HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM:
1011        NOTREACHED();
1012    }
1013    exception->SetString(kSetting,
1014                         ContentSettingToString(CONTENT_SETTING_DEFAULT));
1015
1016    // Calculate the zoom percent from the factor. Round up to the nearest whole
1017    // number.
1018    int zoom_percent = static_cast<int>(
1019        content::ZoomLevelToZoomFactor(i->zoom_level) * 100 + 0.5);
1020    exception->SetString(
1021        kZoom,
1022        l10n_util::GetStringFUTF16(IDS_ZOOM_PERCENT,
1023                                   base::IntToString16(zoom_percent)));
1024    exception->SetString(kSource, kPreferencesSource);
1025    // Append the new entry to the list and map.
1026    zoom_levels_exceptions.Append(exception.release());
1027  }
1028
1029  base::StringValue type_string(kZoomContentType);
1030  web_ui()->CallJavascriptFunction("ContentSettings.setExceptions",
1031                                   type_string, zoom_levels_exceptions);
1032}
1033
1034void ContentSettingsHandler::UpdateExceptionsViewFromHostContentSettingsMap(
1035    ContentSettingsType type) {
1036  base::ListValue exceptions;
1037  GetExceptionsFromHostContentSettingsMap(
1038      GetContentSettingsMap(), type, &exceptions);
1039  base::StringValue type_string(ContentSettingsTypeToGroupName(type));
1040  web_ui()->CallJavascriptFunction("ContentSettings.setExceptions", type_string,
1041                                   exceptions);
1042
1043  UpdateExceptionsViewFromOTRHostContentSettingsMap(type);
1044
1045  // TODO(koz): The default for fullscreen is always 'ask'.
1046  // http://crbug.com/104683
1047  if (type == CONTENT_SETTINGS_TYPE_FULLSCREEN)
1048    return;
1049
1050#if defined(OS_CHROMEOS)
1051  // Also the default for protected contents is managed in another place.
1052  if (type == CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER)
1053    return;
1054#endif
1055
1056  // The default may also have changed (we won't get a separate notification).
1057  // If it hasn't changed, this call will be harmless.
1058  UpdateSettingDefaultFromModel(type);
1059}
1060
1061void ContentSettingsHandler::UpdateExceptionsViewFromOTRHostContentSettingsMap(
1062    ContentSettingsType type) {
1063  const HostContentSettingsMap* otr_settings_map = GetOTRContentSettingsMap();
1064  if (!otr_settings_map)
1065    return;
1066  base::ListValue exceptions;
1067  GetExceptionsFromHostContentSettingsMap(otr_settings_map, type, &exceptions);
1068  base::StringValue type_string(ContentSettingsTypeToGroupName(type));
1069  web_ui()->CallJavascriptFunction("ContentSettings.setOTRExceptions",
1070                                   type_string, exceptions);
1071}
1072
1073void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
1074    const HostContentSettingsMap* map,
1075    ContentSettingsType type,
1076    base::ListValue* exceptions) {
1077  ContentSettingsForOneType entries;
1078  map->GetSettingsForOneType(type, std::string(), &entries);
1079  // Group settings by primary_pattern.
1080  AllPatternsSettings all_patterns_settings;
1081  for (ContentSettingsForOneType::iterator i = entries.begin();
1082       i != entries.end(); ++i) {
1083    // Don't add default settings.
1084    if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
1085        i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
1086        i->source != kPreferencesSource) {
1087      continue;
1088    }
1089
1090    // Off-the-record HostContentSettingsMap contains incognito content settings
1091    // as well as normal content settings. Here, we use the incongnito settings
1092    // only.
1093    if (map->is_off_the_record() && !i->incognito)
1094      continue;
1095
1096    all_patterns_settings[std::make_pair(i->primary_pattern, i->source)]
1097        [i->secondary_pattern] = i->setting;
1098  }
1099
1100  // Keep the exceptions sorted by provider so they will be displayed in
1101  // precedence order.
1102  std::vector<std::vector<base::Value*> > all_provider_exceptions;
1103  all_provider_exceptions.resize(HostContentSettingsMap::NUM_PROVIDER_TYPES);
1104
1105  for (AllPatternsSettings::iterator i = all_patterns_settings.begin();
1106       i != all_patterns_settings.end();
1107       ++i) {
1108    const ContentSettingsPattern& primary_pattern = i->first.first;
1109    const OnePatternSettings& one_settings = i->second;
1110
1111    // The "parent" entry either has an identical primary and secondary pattern,
1112    // or has a wildcard secondary. The two cases are indistinguishable in the
1113    // UI.
1114    OnePatternSettings::const_iterator parent =
1115        one_settings.find(primary_pattern);
1116    if (parent == one_settings.end())
1117      parent = one_settings.find(ContentSettingsPattern::Wildcard());
1118
1119    const std::string& source = i->first.second;
1120    std::vector<base::Value*>* this_provider_exceptions =
1121        &all_provider_exceptions.at(
1122            HostContentSettingsMap::GetProviderTypeFromSource(source));
1123
1124    // Add the "parent" entry for the non-embedded setting.
1125    ContentSetting parent_setting =
1126        parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
1127    const ContentSettingsPattern& secondary_pattern =
1128        parent == one_settings.end() ? primary_pattern : parent->first;
1129    this_provider_exceptions->push_back(GetExceptionForPage(primary_pattern,
1130                                                            secondary_pattern,
1131                                                            parent_setting,
1132                                                            source));
1133
1134    // Add the "children" for any embedded settings.
1135    for (OnePatternSettings::const_iterator j = one_settings.begin();
1136         j != one_settings.end(); ++j) {
1137      // Skip the non-embedded setting which we already added above.
1138      if (j == parent)
1139        continue;
1140
1141      ContentSetting content_setting = j->second;
1142      this_provider_exceptions->push_back(GetExceptionForPage(
1143          primary_pattern,
1144          j->first,
1145          content_setting,
1146          source));
1147    }
1148  }
1149
1150  for (size_t i = 0; i < all_provider_exceptions.size(); ++i) {
1151    for (size_t j = 0; j < all_provider_exceptions[i].size(); ++j) {
1152      exceptions->Append(all_provider_exceptions[i][j]);
1153    }
1154  }
1155}
1156
1157void ContentSettingsHandler::RemoveNotificationException(
1158    const base::ListValue* args) {
1159  Profile* profile = Profile::FromWebUI(web_ui());
1160  std::string origin;
1161  std::string setting;
1162  bool rv = args->GetString(1, &origin);
1163  DCHECK(rv);
1164  rv = args->GetString(2, &setting);
1165  DCHECK(rv);
1166  ContentSetting content_setting = ContentSettingFromString(setting);
1167
1168  DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
1169         content_setting == CONTENT_SETTING_BLOCK);
1170  DesktopNotificationServiceFactory::GetForProfile(profile)->
1171      ClearSetting(ContentSettingsPattern::FromString(origin));
1172}
1173
1174void ContentSettingsHandler::RemoveMediaException(const base::ListValue* args) {
1175  std::string mode;
1176  bool rv = args->GetString(1, &mode);
1177  DCHECK(rv);
1178
1179  std::string pattern;
1180  rv = args->GetString(2, &pattern);
1181  DCHECK(rv);
1182
1183  HostContentSettingsMap* settings_map =
1184      mode == "normal" ? GetContentSettingsMap() :
1185                         GetOTRContentSettingsMap();
1186  if (settings_map) {
1187    settings_map->SetWebsiteSetting(ContentSettingsPattern::FromString(pattern),
1188                                    ContentSettingsPattern::Wildcard(),
1189                                    CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
1190                                    std::string(),
1191                                    NULL);
1192    settings_map->SetWebsiteSetting(ContentSettingsPattern::FromString(pattern),
1193                                    ContentSettingsPattern::Wildcard(),
1194                                    CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
1195                                    std::string(),
1196                                    NULL);
1197  }
1198}
1199
1200void ContentSettingsHandler::RemoveExceptionFromHostContentSettingsMap(
1201    const base::ListValue* args,
1202    ContentSettingsType type) {
1203  std::string mode;
1204  bool rv = args->GetString(1, &mode);
1205  DCHECK(rv);
1206
1207  std::string pattern;
1208  rv = args->GetString(2, &pattern);
1209  DCHECK(rv);
1210
1211  // The fourth argument to this handler is optional.
1212  std::string secondary_pattern;
1213  if (args->GetSize() >= 4U) {
1214    rv = args->GetString(3, &secondary_pattern);
1215    DCHECK(rv);
1216  }
1217
1218  HostContentSettingsMap* settings_map =
1219      mode == "normal" ? GetContentSettingsMap() :
1220                         GetOTRContentSettingsMap();
1221  if (settings_map) {
1222    settings_map->SetWebsiteSetting(
1223        ContentSettingsPattern::FromString(pattern),
1224        secondary_pattern.empty() ?
1225            ContentSettingsPattern::Wildcard() :
1226            ContentSettingsPattern::FromString(secondary_pattern),
1227        type,
1228        std::string(),
1229        NULL);
1230  }
1231}
1232
1233void ContentSettingsHandler::RemoveZoomLevelException(
1234    const base::ListValue* args) {
1235  std::string mode;
1236  bool rv = args->GetString(1, &mode);
1237  DCHECK(rv);
1238
1239  std::string pattern;
1240  rv = args->GetString(2, &pattern);
1241  DCHECK(rv);
1242
1243  content::HostZoomMap* host_zoom_map =
1244      content::HostZoomMap::GetForBrowserContext(Profile::FromWebUI(web_ui()));
1245  double default_level = host_zoom_map->GetDefaultZoomLevel();
1246  host_zoom_map->SetZoomLevelForHost(pattern, default_level);
1247}
1248
1249void ContentSettingsHandler::RegisterMessages() {
1250  web_ui()->RegisterMessageCallback("setContentFilter",
1251      base::Bind(&ContentSettingsHandler::SetContentFilter,
1252                 base::Unretained(this)));
1253  web_ui()->RegisterMessageCallback("removeException",
1254      base::Bind(&ContentSettingsHandler::RemoveException,
1255                 base::Unretained(this)));
1256  web_ui()->RegisterMessageCallback("setException",
1257      base::Bind(&ContentSettingsHandler::SetException,
1258                 base::Unretained(this)));
1259  web_ui()->RegisterMessageCallback("checkExceptionPatternValidity",
1260      base::Bind(&ContentSettingsHandler::CheckExceptionPatternValidity,
1261                 base::Unretained(this)));
1262}
1263
1264void ContentSettingsHandler::ApplyWhitelist(ContentSettingsType content_type,
1265                                            ContentSetting default_setting) {
1266  Profile* profile = Profile::FromWebUI(web_ui());
1267  HostContentSettingsMap* map = GetContentSettingsMap();
1268  if (content_type != CONTENT_SETTINGS_TYPE_PLUGINS)
1269    return;
1270  const int kDefaultWhitelistVersion = 1;
1271  PrefService* prefs = profile->GetPrefs();
1272  int version = prefs->GetInteger(
1273      prefs::kContentSettingsDefaultWhitelistVersion);
1274  if (version >= kDefaultWhitelistVersion)
1275    return;
1276  ContentSetting old_setting =
1277      map->GetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS, NULL);
1278  // TODO(bauerb): Remove this once the Google Talk plug-in works nicely with
1279  // click-to-play (b/6090625).
1280  if (old_setting == CONTENT_SETTING_ALLOW &&
1281      default_setting == CONTENT_SETTING_ASK) {
1282    map->SetWebsiteSetting(
1283        ContentSettingsPattern::Wildcard(),
1284        ContentSettingsPattern::Wildcard(),
1285        CONTENT_SETTINGS_TYPE_PLUGINS,
1286        "google-talk",
1287        new base::FundamentalValue(CONTENT_SETTING_ALLOW));
1288  }
1289  prefs->SetInteger(prefs::kContentSettingsDefaultWhitelistVersion,
1290                    kDefaultWhitelistVersion);
1291}
1292
1293void ContentSettingsHandler::SetContentFilter(const base::ListValue* args) {
1294  DCHECK_EQ(2U, args->GetSize());
1295  std::string group, setting;
1296  if (!(args->GetString(0, &group) &&
1297        args->GetString(1, &setting))) {
1298    NOTREACHED();
1299    return;
1300  }
1301
1302  ContentSetting default_setting = ContentSettingFromString(setting);
1303  ContentSettingsType content_type = ContentSettingsTypeFromGroupName(group);
1304  Profile* profile = Profile::FromWebUI(web_ui());
1305
1306#if defined(OS_CHROMEOS)
1307  // ChromeOS special case : in Guest mode settings are opened in Incognito
1308  // mode, so we need original profile to actually modify settings.
1309  if (chromeos::UserManager::Get()->IsLoggedInAsGuest())
1310    profile = profile->GetOriginalProfile();
1311#endif
1312
1313  if (content_type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
1314    DesktopNotificationServiceFactory::GetForProfile(profile)->
1315        SetDefaultContentSetting(default_setting);
1316  } else {
1317    HostContentSettingsMap* map = profile->GetHostContentSettingsMap();
1318    ApplyWhitelist(content_type, default_setting);
1319    map->SetDefaultContentSetting(content_type, default_setting);
1320  }
1321  switch (content_type) {
1322    case CONTENT_SETTINGS_TYPE_COOKIES:
1323      content::RecordAction(
1324          UserMetricsAction("Options_DefaultCookieSettingChanged"));
1325      break;
1326    case CONTENT_SETTINGS_TYPE_IMAGES:
1327      content::RecordAction(
1328          UserMetricsAction("Options_DefaultImagesSettingChanged"));
1329      break;
1330    case CONTENT_SETTINGS_TYPE_JAVASCRIPT:
1331      content::RecordAction(
1332          UserMetricsAction("Options_DefaultJavaScriptSettingChanged"));
1333      break;
1334    case CONTENT_SETTINGS_TYPE_PLUGINS:
1335      content::RecordAction(
1336          UserMetricsAction("Options_DefaultPluginsSettingChanged"));
1337      break;
1338    case CONTENT_SETTINGS_TYPE_POPUPS:
1339      content::RecordAction(
1340          UserMetricsAction("Options_DefaultPopupsSettingChanged"));
1341      break;
1342    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
1343      content::RecordAction(
1344          UserMetricsAction("Options_DefaultNotificationsSettingChanged"));
1345      break;
1346    case CONTENT_SETTINGS_TYPE_GEOLOCATION:
1347      content::RecordAction(
1348          UserMetricsAction("Options_DefaultGeolocationSettingChanged"));
1349      break;
1350    case CONTENT_SETTINGS_TYPE_MOUSELOCK:
1351      content::RecordAction(
1352          UserMetricsAction("Options_DefaultMouseLockSettingChanged"));
1353      break;
1354    case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
1355      content::RecordAction(
1356          UserMetricsAction("Options_DefaultMediaStreamMicSettingChanged"));
1357      break;
1358    case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS:
1359      content::RecordAction(
1360          UserMetricsAction("Options_DefaultMultipleAutomaticDLSettingChange"));
1361      break;
1362    case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
1363      content::RecordAction(
1364          UserMetricsAction("Options_DefaultMIDISysExSettingChanged"));
1365      break;
1366    default:
1367      break;
1368  }
1369}
1370
1371void ContentSettingsHandler::RemoveException(const base::ListValue* args) {
1372  std::string type_string;
1373  CHECK(args->GetString(0, &type_string));
1374
1375  // Zoom levels are no actual content type so we need to handle them
1376  // separately. They would not be recognized by
1377  // ContentSettingsTypeFromGroupName.
1378  if (type_string == kZoomContentType) {
1379    RemoveZoomLevelException(args);
1380    return;
1381  }
1382
1383  ContentSettingsType type = ContentSettingsTypeFromGroupName(type_string);
1384  switch (type) {
1385    case CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
1386      RemoveNotificationException(args);
1387      break;
1388    case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
1389      RemoveMediaException(args);
1390      break;
1391    default:
1392      RemoveExceptionFromHostContentSettingsMap(args, type);
1393      break;
1394  }
1395}
1396
1397void ContentSettingsHandler::SetException(const base::ListValue* args) {
1398  std::string type_string;
1399  CHECK(args->GetString(0, &type_string));
1400  std::string mode;
1401  CHECK(args->GetString(1, &mode));
1402  std::string pattern;
1403  CHECK(args->GetString(2, &pattern));
1404  std::string setting;
1405  CHECK(args->GetString(3, &setting));
1406
1407  ContentSettingsType type = ContentSettingsTypeFromGroupName(type_string);
1408  if (type == CONTENT_SETTINGS_TYPE_GEOLOCATION ||
1409      type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS ||
1410      type == CONTENT_SETTINGS_TYPE_MEDIASTREAM ||
1411      type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
1412      type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
1413    NOTREACHED();
1414  } else {
1415    HostContentSettingsMap* settings_map =
1416        mode == "normal" ? GetContentSettingsMap() :
1417                           GetOTRContentSettingsMap();
1418
1419    // The settings map could be null if the mode was OTR but the OTR profile
1420    // got destroyed before we received this message.
1421    if (!settings_map)
1422      return;
1423    settings_map->SetContentSetting(ContentSettingsPattern::FromString(pattern),
1424                                    ContentSettingsPattern::Wildcard(),
1425                                    type,
1426                                    std::string(),
1427                                    ContentSettingFromString(setting));
1428  }
1429}
1430
1431void ContentSettingsHandler::CheckExceptionPatternValidity(
1432    const base::ListValue* args) {
1433  std::string type_string;
1434  CHECK(args->GetString(0, &type_string));
1435  std::string mode_string;
1436  CHECK(args->GetString(1, &mode_string));
1437  std::string pattern_string;
1438  CHECK(args->GetString(2, &pattern_string));
1439
1440  ContentSettingsPattern pattern =
1441      ContentSettingsPattern::FromString(pattern_string);
1442
1443  web_ui()->CallJavascriptFunction(
1444      "ContentSettings.patternValidityCheckComplete",
1445      base::StringValue(type_string),
1446      base::StringValue(mode_string),
1447      base::StringValue(pattern_string),
1448      base::FundamentalValue(pattern.IsValid()));
1449}
1450
1451// static
1452std::string ContentSettingsHandler::ContentSettingsTypeToGroupName(
1453    ContentSettingsType type) {
1454  for (size_t i = 0; i < arraysize(kContentSettingsTypeGroupNames); ++i) {
1455    if (type == kContentSettingsTypeGroupNames[i].type)
1456      return kContentSettingsTypeGroupNames[i].name;
1457  }
1458
1459  NOTREACHED();
1460  return std::string();
1461}
1462
1463HostContentSettingsMap* ContentSettingsHandler::GetContentSettingsMap() {
1464  return Profile::FromWebUI(web_ui())->GetHostContentSettingsMap();
1465}
1466
1467ProtocolHandlerRegistry* ContentSettingsHandler::GetProtocolHandlerRegistry() {
1468  return ProtocolHandlerRegistryFactory::GetForProfile(
1469      Profile::FromWebUI(web_ui()));
1470}
1471
1472HostContentSettingsMap*
1473    ContentSettingsHandler::GetOTRContentSettingsMap() {
1474  Profile* profile = Profile::FromWebUI(web_ui());
1475  if (profile->HasOffTheRecordProfile())
1476    return profile->GetOffTheRecordProfile()->GetHostContentSettingsMap();
1477  return NULL;
1478}
1479
1480void ContentSettingsHandler::RefreshFlashMediaSettings() {
1481  media_settings_.flash_settings_initialized = false;
1482
1483  media_settings_.last_flash_refresh_request_id =
1484      flash_settings_manager_->GetPermissionSettings(
1485          PP_FLASH_BROWSEROPERATIONS_SETTINGTYPE_CAMERAMIC);
1486}
1487
1488void ContentSettingsHandler::OnPepperFlashPrefChanged() {
1489  ShowFlashMediaLink(DEFAULT_SETTING, false);
1490  ShowFlashMediaLink(EXCEPTIONS, false);
1491
1492  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
1493  if (prefs->GetBoolean(prefs::kPepperFlashSettingsEnabled))
1494    RefreshFlashMediaSettings();
1495  else
1496    media_settings_.flash_settings_initialized = false;
1497}
1498
1499void ContentSettingsHandler::OnZoomLevelChanged(
1500    const content::HostZoomMap::ZoomLevelChange& change) {
1501  UpdateZoomLevelsExceptionsView();
1502}
1503
1504void ContentSettingsHandler::ShowFlashMediaLink(LinkType link_type, bool show) {
1505  bool& show_link = link_type == DEFAULT_SETTING ?
1506      media_settings_.show_flash_default_link :
1507      media_settings_.show_flash_exceptions_link;
1508  if (show_link != show) {
1509    web_ui()->CallJavascriptFunction(
1510        link_type == DEFAULT_SETTING ?
1511            "ContentSettings.showMediaPepperFlashDefaultLink" :
1512            "ContentSettings.showMediaPepperFlashExceptionsLink",
1513        base::FundamentalValue(show));
1514    show_link = show;
1515  }
1516}
1517
1518void ContentSettingsHandler::UpdateFlashMediaLinksVisibility() {
1519  if (!media_settings_.flash_settings_initialized ||
1520      !media_settings_.default_setting_initialized ||
1521      !media_settings_.exceptions_initialized) {
1522    return;
1523  }
1524
1525  // Flash won't send us notifications when its settings get changed, which
1526  // means the Flash settings in |media_settings_| may be out-dated, especially
1527  // after we show links to change Flash settings.
1528  // In order to avoid confusion, we won't hide the links once they are showed.
1529  // One exception is that we will hide them when Pepper Flash is disabled
1530  // (handled in OnPepperFlashPrefChanged()).
1531  if (media_settings_.show_flash_default_link &&
1532      media_settings_.show_flash_exceptions_link) {
1533    return;
1534  }
1535
1536  if (!media_settings_.show_flash_default_link) {
1537    // If both audio and video capture are disabled by policy, the link
1538    // shouldn't be showed. Flash conforms to the policy in this case because
1539    // it cannot open those devices. We don't have to look at the Flash
1540    // settings.
1541    if (!(media_settings_.policy_disable_audio &&
1542          media_settings_.policy_disable_video) &&
1543        media_settings_.flash_default_setting !=
1544            media_settings_.default_setting) {
1545      ShowFlashMediaLink(DEFAULT_SETTING, true);
1546    }
1547  }
1548  if (!media_settings_.show_flash_exceptions_link) {
1549    // If audio or video capture is disabled by policy, we skip comparison of
1550    // exceptions for audio or video capture, respectively.
1551    if (!PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
1552            media_settings_.default_setting,
1553            media_settings_.exceptions,
1554            media_settings_.flash_default_setting,
1555            media_settings_.flash_exceptions,
1556            media_settings_.policy_disable_audio,
1557            media_settings_.policy_disable_video)) {
1558      ShowFlashMediaLink(EXCEPTIONS, true);
1559    }
1560  }
1561}
1562
1563void ContentSettingsHandler::UpdateProtectedContentExceptionsButton() {
1564  PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
1565  // Exceptions apply only when the feature is enabled.
1566  bool enable_exceptions = prefs->GetBoolean(prefs::kEnableDRM);
1567  web_ui()->CallJavascriptFunction(
1568      "ContentSettings.enableProtectedContentExceptions",
1569      base::FundamentalValue(enable_exceptions));
1570}
1571
1572}  // namespace options
1573