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