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