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