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