Home | History | Annotate | Download | only in options
      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 
     52 using content::UserMetricsAction;
     53 using extensions::APIPermission;
     54 
     55 namespace {
     56 
     57 struct ContentSettingsTypeNameEntry {
     58   ContentSettingsType type;
     59   const char* name;
     60 };
     61 
     62 // Maps from a secondary pattern to a setting.
     63 typedef std::map<ContentSettingsPattern, ContentSetting>
     64     OnePatternSettings;
     65 // Maps from a primary pattern/source pair to a OnePatternSettings. All the
     66 // mappings in OnePatternSettings share the given primary pattern and source.
     67 typedef std::map<std::pair<ContentSettingsPattern, std::string>,
     68                  OnePatternSettings>
     69     AllPatternsSettings;
     70 
     71 // The AppFilter is used in AddExceptionsGrantedByHostedApps() to choose
     72 // extensions which should have their extent displayed.
     73 typedef bool (*AppFilter)(const extensions::Extension& app, Profile* profile);
     74 
     75 const char kExceptionsLearnMoreUrl[] =
     76     "https://support.google.com/chrome/?p=settings_manage_exceptions";
     77 
     78 const char* kSetting = "setting";
     79 const char* kOrigin = "origin";
     80 const char* kSource = "source";
     81 const char* kAppName = "appName";
     82 const char* kAppId = "appId";
     83 const char* kEmbeddingOrigin = "embeddingOrigin";
     84 const char* kPreferencesSource = "preference";
     85 const char* kVideoSetting = "video";
     86 
     87 const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
     88   {CONTENT_SETTINGS_TYPE_COOKIES, "cookies"},
     89   {CONTENT_SETTINGS_TYPE_IMAGES, "images"},
     90   {CONTENT_SETTINGS_TYPE_JAVASCRIPT, "javascript"},
     91   {CONTENT_SETTINGS_TYPE_PLUGINS, "plugins"},
     92   {CONTENT_SETTINGS_TYPE_POPUPS, "popups"},
     93   {CONTENT_SETTINGS_TYPE_GEOLOCATION, "location"},
     94   {CONTENT_SETTINGS_TYPE_NOTIFICATIONS, "notifications"},
     95   {CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE, "auto-select-certificate"},
     96   {CONTENT_SETTINGS_TYPE_FULLSCREEN, "fullscreen"},
     97   {CONTENT_SETTINGS_TYPE_MOUSELOCK, "mouselock"},
     98   {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, "register-protocol-handler"},
     99   {CONTENT_SETTINGS_TYPE_MEDIASTREAM, "media-stream"},
    100   {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic"},
    101   {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera"},
    102   {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker"},
    103   {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "multiple-automatic-downloads"},
    104   {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex"},
    105 };
    106 
    107 ContentSettingsType 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 
    117 std::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 
    136 ContentSetting 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.
    153 DictionaryValue* 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.
    172 DictionaryValue* 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.
    186 DictionaryValue* 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.
    199 template <APIPermission::ID permission>
    200 bool 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|.
    207 void 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.
    221 void 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 
    252 namespace options {
    253 
    254 ContentSettingsHandler::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 
    267 ContentSettingsHandler::MediaSettingsInfo::~MediaSettingsInfo() {
    268 }
    269 
    270 ContentSettingsHandler::ContentSettingsHandler() {
    271 }
    272 
    273 ContentSettingsHandler::~ContentSettingsHandler() {
    274 }
    275 
    276 void 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 
    442 void 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 
    479 void ContentSettingsHandler::InitializePage() {
    480   media_settings_ = MediaSettingsInfo();
    481   RefreshFlashMediaSettings();
    482 
    483   UpdateHandlersEnabledRadios();
    484   UpdateAllExceptionsViewsFromModel();
    485 }
    486 
    487 void 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 
    540 void 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 
    559 void 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 
    572 void 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 
    638 std::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 
    655 void ContentSettingsHandler::UpdateHandlersEnabledRadios() {
    656   base::FundamentalValue handlers_enabled(
    657       GetProtocolHandlerRegistry()->enabled());
    658 
    659   web_ui()->CallJavascriptFunction(
    660       "ContentSettings.updateHandlersEnabledRadios",
    661       handlers_enabled);
    662 }
    663 
    664 void 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 
    671 void 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 
    678 void 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 
    718 void 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.
    741 void 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 
    809 void 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 
    848 void 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 
    938 void 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 
    950 void 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 
    971 void 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 
    983 void 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 
   1066 void 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 
   1083 void 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 
   1110 void 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 
   1140 void 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 
   1155 void 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 
   1184 void 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 
   1262 void 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 
   1281 void 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 
   1316 void 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
   1338 std::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 
   1349 HostContentSettingsMap* ContentSettingsHandler::GetContentSettingsMap() {
   1350   return Profile::FromWebUI(web_ui())->GetHostContentSettingsMap();
   1351 }
   1352 
   1353 ProtocolHandlerRegistry* ContentSettingsHandler::GetProtocolHandlerRegistry() {
   1354   return ProtocolHandlerRegistryFactory::GetForProfile(
   1355       Profile::FromWebUI(web_ui()));
   1356 }
   1357 
   1358 HostContentSettingsMap*
   1359     ContentSettingsHandler::GetOTRContentSettingsMap() {
   1360   Profile* profile = Profile::FromWebUI(web_ui());
   1361   if (profile->HasOffTheRecordProfile())
   1362     return profile->GetOffTheRecordProfile()->GetHostContentSettingsMap();
   1363   return NULL;
   1364 }
   1365 
   1366 void 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 
   1374 void 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 
   1385 void 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 
   1399 void 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