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