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