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
57using base::UserMetricsAction;
58using content_settings::ContentSettingToString;
59using content_settings::ContentSettingFromString;
60using extensions::APIPermission;
61
62namespace {
63
64struct ContentSettingsTypeNameEntry {
65  ContentSettingsType type;
66  const char* name;
67};
68
69// Maps from a secondary pattern to a setting.
70typedef 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.
74typedef 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.
80typedef bool (*AppFilter)(const extensions::Extension& app,
81                          content::BrowserContext* profile);
82
83const char kExceptionsLearnMoreUrl[] =
84    "https://support.google.com/chrome/?p=settings_manage_exceptions";
85
86const char* kSetting = "setting";
87const char* kOrigin = "origin";
88const char* kSource = "source";
89const char* kAppName = "appName";
90const char* kAppId = "appId";
91const char* kEmbeddingOrigin = "embeddingOrigin";
92const char* kPreferencesSource = "preference";
93const char* kVideoSetting = "video";
94const char* kZoom = "zoom";
95
96const 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.
123const char* kZoomContentType = "zoomlevels";
124
125content::BrowserContext* GetBrowserContext(content::WebUI* web_ui) {
126  return web_ui->GetWebContents()->GetBrowserContext();
127}
128
129ContentSettingsType 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.
142base::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.
161base::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.
175base::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.
194template <APIPermission::ID permission>
195bool 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|.
203void 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.
217void 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).
246bool 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
253namespace options {
254
255ContentSettingsHandler::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
268ContentSettingsHandler::MediaSettingsInfo::~MediaSettingsInfo() {
269}
270
271ContentSettingsHandler::ContentSettingsHandler() : observer_(this) {
272}
273
274ContentSettingsHandler::~ContentSettingsHandler() {
275}
276
277void 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
454void 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
508void ContentSettingsHandler::InitializePage() {
509  media_settings_ = MediaSettingsInfo();
510  RefreshFlashMediaSettings();
511
512  UpdateHandlersEnabledRadios();
513  UpdateAllExceptionsViewsFromModel();
514  UpdateProtectedContentExceptionsButton();
515}
516
517void 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
531void 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
568void 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
587void 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
600void 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
666std::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
677void ContentSettingsHandler::UpdateHandlersEnabledRadios() {
678  base::FundamentalValue handlers_enabled(
679      GetProtocolHandlerRegistry()->enabled());
680
681  web_ui()->CallJavascriptFunction(
682      "ContentSettings.updateHandlersEnabledRadios",
683      handlers_enabled);
684}
685
686void 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
696void 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
703void 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
748void 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.
772void 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
840void 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
879void 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
969void 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
981void 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
1028void 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
1055void 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
1067void 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
1151void 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
1177void 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
1210void 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
1227void 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
1242void 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
1320void 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
1339void 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
1373void 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
1394std::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
1405HostContentSettingsMap* ContentSettingsHandler::GetContentSettingsMap() {
1406  return Profile::FromWebUI(web_ui())->GetHostContentSettingsMap();
1407}
1408
1409ProtocolHandlerRegistry* ContentSettingsHandler::GetProtocolHandlerRegistry() {
1410  return ProtocolHandlerRegistryFactory::GetForBrowserContext(
1411      GetBrowserContext(web_ui()));
1412}
1413
1414HostContentSettingsMap*
1415    ContentSettingsHandler::GetOTRContentSettingsMap() {
1416  Profile* profile = Profile::FromWebUI(web_ui());
1417  if (profile->HasOffTheRecordProfile())
1418    return profile->GetOffTheRecordProfile()->GetHostContentSettingsMap();
1419  return NULL;
1420}
1421
1422void 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
1430void 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
1441void ContentSettingsHandler::OnZoomLevelChanged(
1442    const content::HostZoomMap::ZoomLevelChange& change) {
1443  UpdateZoomLevelsExceptionsView();
1444}
1445
1446void 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
1460void 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
1505void 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
1521