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