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