root/chrome/browser/ui/content_settings/content_setting_bubble_model.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetIdForContentType
  2. GetMediaDeviceById
  3. delegate_
  4. SetTitle
  5. SetManageLink
  6. OnManageLinkClicked
  7. OnCustomLinkClicked
  8. SetCustomLink
  9. selected_item
  10. selected_item_
  11. settings_changed
  12. SetRadioGroup
  13. AddException
  14. OnRadioClicked
  15. OnCustomLinkClicked
  16. OnCustomLinkClicked
  17. SetPopups
  18. OnPopupClicked
  19. state_
  20. SetTitle
  21. SetRadioGroup
  22. UpdateSettings
  23. UpdateDefaultDeviceForType
  24. SetMediaMenus
  25. OnRadioClicked
  26. OnMediaMenuClicked
  27. MaybeAddDomainList
  28. SetDomainsAndCustomLink
  29. OnCustomLinkClicked
  30. OnCustomLinkClicked
  31. previous_handler_
  32. OnRadioClicked
  33. OnDoneClicked
  34. RegisterProtocolHandler
  35. UnregisterProtocolHandler
  36. IgnoreProtocolHandler
  37. ClearOrSetPreviousHandler
  38. MaybeAddDomainList
  39. SetDomainsAndCustomLink
  40. OnCustomLinkClicked
  41. CreateContentSettingBubbleModel
  42. setting_is_managed_
  43. custom_link_enabled
  44. Observe

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"

#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/content_settings_utils.h"
#include "chrome/browser/content_settings/cookie_settings.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
#include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/content_settings/media_setting_changed_infobar_delegate.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/content_settings.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
#include "net/base/net_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"

using base::UserMetricsAction;
using content::WebContents;
using content_settings::SettingInfo;
using content_settings::SettingSource;
using content_settings::SETTING_SOURCE_USER;
using content_settings::SETTING_SOURCE_NONE;

namespace {

const int kAllowButtonIndex = 0;

struct ContentSettingsTypeIdEntry {
  ContentSettingsType type;
  int id;
};

int GetIdForContentType(const ContentSettingsTypeIdEntry* entries,
                        size_t num_entries,
                        ContentSettingsType type) {
  for (size_t i = 0; i < num_entries; ++i) {
    if (entries[i].type == type)
      return entries[i].id;
  }
  return 0;
}

const content::MediaStreamDevice& GetMediaDeviceById(
    const std::string& device_id,
    const content::MediaStreamDevices& devices) {
  DCHECK(!devices.empty());
  for (content::MediaStreamDevices::const_iterator it = devices.begin();
       it != devices.end(); ++it) {
    if (it->id == device_id)
      return *(it);
  }

  // A device with the |device_id| was not found. It is likely that the device
  // has been unplugged from the OS. Return the first device as the default
  // device.
  return *devices.begin();
}

}  // namespace

ContentSettingTitleAndLinkModel::ContentSettingTitleAndLinkModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingBubbleModel(web_contents, profile, content_type),
        delegate_(delegate) {
  // Notifications do not have a bubble.
  DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
  SetTitle();
  SetManageLink();
}

void ContentSettingTitleAndLinkModel::SetTitle() {
  static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_TITLE},
    {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_TITLE},
    {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_TITLE},
    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_MESSAGE},
    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_TITLE},
    {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT,
        IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER,
        IDS_BLOCKED_PPAPI_BROKER_TITLE},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_TITLE},
  };
  // Fields as for kBlockedTitleIDs, above.
  static const ContentSettingsTypeIdEntry kAccessedTitleIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_ACCESSED_COOKIES_TITLE},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_TITLE},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_TITLE},
  };
  const ContentSettingsTypeIdEntry *title_ids = kBlockedTitleIDs;
  size_t num_title_ids = arraysize(kBlockedTitleIDs);
  if (web_contents() &&
      TabSpecificContentSettings::FromWebContents(
          web_contents())->IsContentAllowed(content_type()) &&
      !TabSpecificContentSettings::FromWebContents(
          web_contents())->IsContentBlocked(content_type())) {
    title_ids = kAccessedTitleIDs;
    num_title_ids = arraysize(kAccessedTitleIDs);
  }
  int title_id =
      GetIdForContentType(title_ids, num_title_ids, content_type());
  if (title_id)
    set_title(l10n_util::GetStringUTF8(title_id));
}

void ContentSettingTitleAndLinkModel::SetManageLink() {
  static const ContentSettingsTypeIdEntry kLinkIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_LINK},
    {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_LINK},
    {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_LINK},
    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_LINK},
    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_LINK},
    {CONTENT_SETTINGS_TYPE_GEOLOCATION, IDS_GEOLOCATION_BUBBLE_MANAGE_LINK},
    {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDS_LEARN_MORE},
    {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, IDS_HANDLERS_BUBBLE_MANAGE_LINK},
    {CONTENT_SETTINGS_TYPE_MEDIASTREAM, IDS_MEDIASTREAM_BUBBLE_MANAGE_LINK},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_PPAPI_BROKER_BUBBLE_MANAGE_LINK},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOADS_LINK},
    {CONTENT_SETTINGS_TYPE_MIDI_SYSEX, IDS_MIDI_SYSEX_BUBBLE_MANAGE_LINK},
  };
  set_manage_link(l10n_util::GetStringUTF8(
      GetIdForContentType(kLinkIDs, arraysize(kLinkIDs), content_type())));
}

void ContentSettingTitleAndLinkModel::OnManageLinkClicked() {
  if (delegate_)
    delegate_->ShowContentSettingsPage(content_type());
}

class ContentSettingTitleLinkAndCustomModel
    : public ContentSettingTitleAndLinkModel {
 public:
  ContentSettingTitleLinkAndCustomModel(Delegate* delegate,
                                        WebContents* web_contents,
                                        Profile* profile,
                                        ContentSettingsType content_type);
  virtual ~ContentSettingTitleLinkAndCustomModel() {}

 private:
  void SetCustomLink();
  virtual void OnCustomLinkClicked() OVERRIDE {}
};

ContentSettingTitleLinkAndCustomModel::ContentSettingTitleLinkAndCustomModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingTitleAndLinkModel(
          delegate, web_contents, profile, content_type) {
  SetCustomLink();
}

void ContentSettingTitleLinkAndCustomModel::SetCustomLink() {
  static const ContentSettingsTypeIdEntry kCustomIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_INFO},
    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_LOAD_ALL},
    {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDS_ALLOW_INSECURE_CONTENT_BUTTON},
  };
  int custom_link_id =
      GetIdForContentType(kCustomIDs, arraysize(kCustomIDs), content_type());
  if (custom_link_id)
    set_custom_link(l10n_util::GetStringUTF8(custom_link_id));
}

class ContentSettingSingleRadioGroup
    : public ContentSettingTitleLinkAndCustomModel {
 public:
  ContentSettingSingleRadioGroup(Delegate* delegate,
                                 WebContents* web_contents,
                                 Profile* profile,
                                 ContentSettingsType content_type);
  virtual ~ContentSettingSingleRadioGroup();

 protected:
  bool settings_changed() const;
  int selected_item() const { return selected_item_; }

 private:
  void SetRadioGroup();
  void AddException(ContentSetting setting);
  virtual void OnRadioClicked(int radio_index) OVERRIDE;

  ContentSetting block_setting_;
  int selected_item_;
};

ContentSettingSingleRadioGroup::ContentSettingSingleRadioGroup(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingTitleLinkAndCustomModel(delegate, web_contents, profile,
                                            content_type),
      block_setting_(CONTENT_SETTING_BLOCK),
      selected_item_(0) {
  SetRadioGroup();
}

ContentSettingSingleRadioGroup::~ContentSettingSingleRadioGroup() {
  if (settings_changed()) {
    ContentSetting setting =
        selected_item_ == kAllowButtonIndex ?
                          CONTENT_SETTING_ALLOW :
                          block_setting_;
    AddException(setting);
  }
}

bool ContentSettingSingleRadioGroup::settings_changed() const {
  return selected_item_ != bubble_content().radio_group.default_item;
}

// Initialize the radio group by setting the appropriate labels for the
// content type and setting the default value based on the content setting.
void ContentSettingSingleRadioGroup::SetRadioGroup() {
  GURL url = web_contents()->GetURL();
  base::string16 display_host;
  net::AppendFormattedHost(
      url,
      profile()->GetPrefs()->GetString(prefs::kAcceptLanguages),
      &display_host);

  if (display_host.empty())
    display_host = base::ASCIIToUTF16(url.spec());

  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  bool allowed =
      !content_settings->IsContentBlocked(content_type());
  DCHECK(!allowed ||
         content_settings->IsContentAllowed(content_type()));

  RadioGroup radio_group;
  radio_group.url = url;

  static const ContentSettingsTypeIdEntry kBlockedAllowIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_UNBLOCK_ALL},
    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_BLOCKED_PPAPI_BROKER_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_UNBLOCK},
  };
  // Fields as for kBlockedAllowIDs, above.
  static const ContentSettingsTypeIdEntry kAllowedAllowIDs[] = {
    // TODO(bauerb): The string shouldn't be "unblock" (they weren't blocked).
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_UNBLOCK},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_NO_ACTION},
  };

  std::string radio_allow_label;
  if (allowed) {
    int resource_id = GetIdForContentType(kAllowedAllowIDs,
                                          arraysize(kAllowedAllowIDs),
                                          content_type());
    radio_allow_label = (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) ?
        l10n_util::GetStringFUTF8(resource_id, display_host) :
        l10n_util::GetStringUTF8(resource_id);
  } else {
    radio_allow_label = l10n_util::GetStringFUTF8(
        GetIdForContentType(kBlockedAllowIDs, arraysize(kBlockedAllowIDs),
                            content_type()),
        display_host);
  }

  static const ContentSettingsTypeIdEntry kBlockedBlockIDs[] = {
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_IMAGES, IDS_BLOCKED_IMAGES_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDS_BLOCKED_JAVASCRIPT_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_PLUGINS, IDS_BLOCKED_PLUGINS_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_POPUPS, IDS_BLOCKED_POPUPS_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_BLOCKED_PPAPI_BROKER_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_BLOCKED_DOWNLOAD_NO_ACTION},
  };
  static const ContentSettingsTypeIdEntry kAllowedBlockIDs[] = {
    // TODO(bauerb): The string should say "block".
    {CONTENT_SETTINGS_TYPE_COOKIES, IDS_BLOCKED_COOKIES_NO_ACTION},
    {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_BLOCK},
    {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_BLOCK},
  };

  std::string radio_block_label;
  if (allowed) {
    int resource_id = GetIdForContentType(kAllowedBlockIDs,
                                          arraysize(kAllowedBlockIDs),
                                          content_type());
    radio_block_label = (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) ?
        l10n_util::GetStringUTF8(resource_id) :
        l10n_util::GetStringFUTF8(resource_id, display_host);
  } else {
    radio_block_label = l10n_util::GetStringUTF8(
        GetIdForContentType(kBlockedBlockIDs, arraysize(kBlockedBlockIDs),
                            content_type()));
  }

  radio_group.radio_items.push_back(radio_allow_label);
  radio_group.radio_items.push_back(radio_block_label);
  ContentSetting setting;
  SettingSource setting_source = SETTING_SOURCE_NONE;
  bool setting_is_wildcard = false;

  if (content_type() == CONTENT_SETTINGS_TYPE_COOKIES) {
    CookieSettings* cookie_settings =
        CookieSettings::Factory::GetForProfile(profile()).get();
    setting = cookie_settings->GetCookieSetting(
        url, url, true, &setting_source);
  } else {
    SettingInfo info;
    HostContentSettingsMap* map = profile()->GetHostContentSettingsMap();
    scoped_ptr<base::Value> value(map->GetWebsiteSetting(
        url, url, content_type(), std::string(), &info));
    setting = content_settings::ValueToContentSetting(value.get());
    setting_source = info.source;
    setting_is_wildcard =
        info.primary_pattern == ContentSettingsPattern::Wildcard() &&
        info.secondary_pattern == ContentSettingsPattern::Wildcard();
  }

  if (content_type() == CONTENT_SETTINGS_TYPE_PLUGINS &&
      setting == CONTENT_SETTING_ALLOW &&
      setting_is_wildcard) {
    // In the corner case of unrecognized plugins (which are now blocked by
    // default) we indicate the blocked state in the UI and allow the user to
    // whitelist.
    radio_group.default_item = 1;
  } else if (setting == CONTENT_SETTING_ALLOW) {
    radio_group.default_item = kAllowButtonIndex;
    // |block_setting_| is already set to |CONTENT_SETTING_BLOCK|.
  } else {
    radio_group.default_item = 1;
    block_setting_ = setting;
  }

  set_setting_is_managed(setting_source != SETTING_SOURCE_USER);
  if (setting_source != SETTING_SOURCE_USER) {
    set_radio_group_enabled(false);
  } else {
    set_radio_group_enabled(true);
  }
  selected_item_ = radio_group.default_item;
  set_radio_group(radio_group);
}

void ContentSettingSingleRadioGroup::AddException(ContentSetting setting) {
  if (profile()) {
    profile()->GetHostContentSettingsMap()->AddExceptionForURL(
        bubble_content().radio_group.url,
        bubble_content().radio_group.url,
        content_type(),
        setting);
  }
}

void ContentSettingSingleRadioGroup::OnRadioClicked(int radio_index) {
  selected_item_ = radio_index;
}

class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup {
 public:
  ContentSettingCookiesBubbleModel(Delegate* delegate,
                                   WebContents* web_contents,
                                   Profile* profile,
                                   ContentSettingsType content_type);

  virtual ~ContentSettingCookiesBubbleModel();

 private:
  virtual void OnCustomLinkClicked() OVERRIDE;
};

ContentSettingCookiesBubbleModel::ContentSettingCookiesBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingSingleRadioGroup(
          delegate, web_contents, profile, content_type) {
  DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type);
  set_custom_link_enabled(true);
}

ContentSettingCookiesBubbleModel::~ContentSettingCookiesBubbleModel() {
  // On some plattforms e.g. MacOS X it is possible to close a tab while the
  // cookies settings bubble is open. This resets the web contents to NULL.
  if (settings_changed() && web_contents()) {
    CollectedCookiesInfoBarDelegate::Create(
        InfoBarService::FromWebContents(web_contents()));
  }
}

void ContentSettingCookiesBubbleModel::OnCustomLinkClicked() {
  if (!web_contents())
    return;
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
      content::Source<TabSpecificContentSettings>(
          TabSpecificContentSettings::FromWebContents(web_contents())),
      content::NotificationService::NoDetails());
  delegate()->ShowCollectedCookiesDialog(web_contents());
}

class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup {
 public:
  ContentSettingPluginBubbleModel(Delegate* delegate,
                                  WebContents* web_contents,
                                  Profile* profile,
                                  ContentSettingsType content_type);

  virtual ~ContentSettingPluginBubbleModel();

 private:
  virtual void OnCustomLinkClicked() OVERRIDE;
};

ContentSettingPluginBubbleModel::ContentSettingPluginBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingSingleRadioGroup(
          delegate, web_contents, profile, content_type) {
  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS);
  // Disable the "Run all plugins this time" link if the setting is managed and
  // can't be controlled by the user or if the user already clicked on the link
  // and ran all plugins.
  set_custom_link_enabled(!setting_is_managed() &&
                          web_contents &&
                          TabSpecificContentSettings::FromWebContents(
                              web_contents)->load_plugins_link_enabled());
}

ContentSettingPluginBubbleModel::~ContentSettingPluginBubbleModel() {
  if (settings_changed()) {
    // If the user elected to allow all plugins then run plugins at this time.
    if (selected_item() == kAllowButtonIndex)
      OnCustomLinkClicked();
  }
}

void ContentSettingPluginBubbleModel::OnCustomLinkClicked() {
  content::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble"));
  DCHECK(web_contents());
#if defined(ENABLE_PLUGINS)
  // TODO(bauerb): We should send the identifiers of blocked plug-ins here.
  ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins(
      web_contents(), true, std::string());
#endif
  set_custom_link_enabled(false);
  TabSpecificContentSettings::FromWebContents(web_contents())->
      set_load_plugins_link_enabled(false);
}

class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup {
 public:
  ContentSettingPopupBubbleModel(Delegate* delegate,
                                 WebContents* web_contents,
                                 Profile* profile,
                                 ContentSettingsType content_type);
  virtual ~ContentSettingPopupBubbleModel() {}

 private:
  void SetPopups();
  virtual void OnPopupClicked(int index) OVERRIDE;
};

ContentSettingPopupBubbleModel::ContentSettingPopupBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingSingleRadioGroup(
        delegate, web_contents, profile, content_type) {
  SetPopups();
}


void ContentSettingPopupBubbleModel::SetPopups() {
  std::map<int32, GURL> blocked_popups =
      PopupBlockerTabHelper::FromWebContents(web_contents())
          ->GetBlockedPopupRequests();
  for (std::map<int32, GURL>::const_iterator iter = blocked_popups.begin();
       iter != blocked_popups.end();
       ++iter) {
    std::string title(iter->second.spec());
    // The popup may not have a valid URL.
    if (title.empty())
      title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE);
    PopupItem popup_item(
        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
            IDR_DEFAULT_FAVICON),
        title,
        iter->first);
    add_popup(popup_item);
  }
}

void ContentSettingPopupBubbleModel::OnPopupClicked(int index) {
  if (web_contents()) {
    PopupBlockerTabHelper::FromWebContents(web_contents())->
        ShowBlockedPopup(bubble_content().popup_items[index].popup_id);
  }
}

// The model of the content settings bubble for media settings.
class ContentSettingMediaStreamBubbleModel
    : public ContentSettingTitleAndLinkModel {
 public:
  ContentSettingMediaStreamBubbleModel(Delegate* delegate,
                                       WebContents* web_contents,
                                       Profile* profile);

  virtual ~ContentSettingMediaStreamBubbleModel();

 private:
  void SetTitle();
  // Sets the data for the radio buttons of the bubble.
  void SetRadioGroup();
  // Sets the data for the media menus of the bubble.
  void SetMediaMenus();
  // Updates the camera and microphone setting with the passed |setting|.
  void UpdateSettings(ContentSetting setting);
  // Updates the camera and microphone default device with the passed |type|
  // and device.
  void UpdateDefaultDeviceForType(content::MediaStreamType type,
                                  const std::string& device);

  // ContentSettingBubbleModel implementation.
  virtual void OnRadioClicked(int radio_index) OVERRIDE;
  virtual void OnMediaMenuClicked(content::MediaStreamType type,
                                  const std::string& selected_device) OVERRIDE;

  // The index of the selected radio item.
  int selected_item_;
  // The content settings that are associated with the individual radio
  // buttons.
  ContentSetting radio_item_setting_[2];
  // The state of the microphone and camera access.
  TabSpecificContentSettings::MicrophoneCameraState state_;
};

ContentSettingMediaStreamBubbleModel::ContentSettingMediaStreamBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile)
    : ContentSettingTitleAndLinkModel(
          delegate, web_contents, profile, CONTENT_SETTINGS_TYPE_MEDIASTREAM),
      selected_item_(0),
      state_(TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED) {
  DCHECK(profile);
  // Initialize the content settings associated with the individual radio
  // buttons.
  radio_item_setting_[0] = CONTENT_SETTING_ASK;
  radio_item_setting_[1] = CONTENT_SETTING_BLOCK;

  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents);
  state_ = content_settings->GetMicrophoneCameraState();

  SetTitle();
  SetRadioGroup();
  SetMediaMenus();
}

ContentSettingMediaStreamBubbleModel::~ContentSettingMediaStreamBubbleModel() {
  // On some platforms (e.g. MacOS X) it is possible to close a tab while the
  // media stream bubble is open. This resets the web contents to NULL.
  if (!web_contents())
    return;

  bool media_setting_changed = false;
  for (MediaMenuMap::const_iterator it = bubble_content().media_menus.begin();
      it != bubble_content().media_menus.end(); ++it) {
    if (it->second.selected_device.id != it->second.default_device.id) {
      UpdateDefaultDeviceForType(it->first, it->second.selected_device.id);
      media_setting_changed = true;
    }
  }

  // Update the media settings if the radio button selection was changed.
  if (selected_item_ != bubble_content().radio_group.default_item) {
    UpdateSettings(radio_item_setting_[selected_item_]);
    media_setting_changed = true;
  }

  // Trigger the reload infobar if the media setting has been changed.
  if (media_setting_changed) {
    MediaSettingChangedInfoBarDelegate::Create(
        InfoBarService::FromWebContents(web_contents()));
  }
}

void ContentSettingMediaStreamBubbleModel::SetTitle() {
  int title_id = 0;
  switch (state_) {
    case TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED:
      // If neither microphone nor camera stream was accessed, then there is no
      // icon didplayed in the omnibox and no settings bubble availbale. Hence
      // there is no title.
      NOTREACHED();
      return;
    case TabSpecificContentSettings::MICROPHONE_ACCESSED:
      title_id = IDS_MICROPHONE_ACCESSED;
      break;
    case TabSpecificContentSettings::CAMERA_ACCESSED:
      title_id = IDS_CAMERA_ACCESSED;
      break;
    case TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED:
      title_id = IDS_MICROPHONE_CAMERA_ALLOWED;
      break;
    case TabSpecificContentSettings::MICROPHONE_BLOCKED:
      title_id = IDS_MICROPHONE_BLOCKED;
      break;
    case TabSpecificContentSettings::CAMERA_BLOCKED:
      title_id = IDS_CAMERA_BLOCKED;
      break;
    case TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED:
      title_id = IDS_MICROPHONE_CAMERA_BLOCKED;
      break;
  }
  set_title(l10n_util::GetStringUTF8(title_id));
}

void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  GURL url = content_settings->media_stream_access_origin();
  RadioGroup radio_group;
  radio_group.url = url;

  base::string16 display_host_utf16;
  net::AppendFormattedHost(
      url,
      profile()->GetPrefs()->GetString(prefs::kAcceptLanguages),
      &display_host_utf16);
  std::string display_host(base::UTF16ToUTF8(display_host_utf16));
  if (display_host.empty())
    display_host = url.spec();

  int radio_allow_label_id = 0;
  int radio_block_label_id = 0;
  switch (state_) {
    case TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED:
      NOTREACHED();
      return;
    case TabSpecificContentSettings::MICROPHONE_ACCESSED:
      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION;
      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK;
      selected_item_ = 0;
      break;
    case TabSpecificContentSettings::CAMERA_ACCESSED:
      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_BLOCK;
      selected_item_ = 0;
      break;
    case TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED:
      radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
      radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_BLOCK;
      selected_item_ = 0;
      break;
    case TabSpecificContentSettings::MICROPHONE_BLOCKED:
      if (url.SchemeIsSecure()) {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_ALLOW;
        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
      } else {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_ASK;
      }

      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_NO_ACTION;
      selected_item_ = 1;
      break;
    case TabSpecificContentSettings::CAMERA_BLOCKED:
      if (url.SchemeIsSecure()) {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ALLOW;
        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
      } else {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_ASK;
      }

      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_CAMERA_NO_ACTION;
      selected_item_ = 1;
      break;
    case TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED:
      if (url.SchemeIsSecure()) {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ALLOW;
        radio_item_setting_[0] = CONTENT_SETTING_ALLOW;
      } else {
        radio_allow_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_ASK;
      }

      radio_block_label_id = IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
      selected_item_ = 1;
      break;
  }

  std::string radio_allow_label = l10n_util::GetStringFUTF8(
      radio_allow_label_id, base::UTF8ToUTF16(display_host));
  std::string radio_block_label =
      l10n_util::GetStringUTF8(radio_block_label_id);

  radio_group.default_item = selected_item_;
  radio_group.radio_items.push_back(radio_allow_label);
  radio_group.radio_items.push_back(radio_block_label);

  set_radio_group(radio_group);
  set_radio_group_enabled(true);
}

void ContentSettingMediaStreamBubbleModel::UpdateSettings(
    ContentSetting setting) {
  if (profile()) {
    HostContentSettingsMap* content_settings =
        profile()->GetHostContentSettingsMap();
    TabSpecificContentSettings* tab_content_settings =
        TabSpecificContentSettings::FromWebContents(web_contents());
    // The same patterns must be used as in other places (e.g. the infobar) in
    // order to override the existing rule. Otherwise a new rule is created.
    // TODO(markusheintz): Extract to a helper so that there is only a single
    // place to touch.
    ContentSettingsPattern primary_pattern =
        ContentSettingsPattern::FromURLNoWildcard(
            tab_content_settings->media_stream_access_origin());
    ContentSettingsPattern secondary_pattern =
        ContentSettingsPattern::Wildcard();
    if (state_ == TabSpecificContentSettings::MICROPHONE_ACCESSED ||
        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
        state_ == TabSpecificContentSettings::MICROPHONE_BLOCKED ||
        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED) {
      content_settings->SetContentSetting(
          primary_pattern, secondary_pattern,
          CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(), setting);
    }
    if (state_ == TabSpecificContentSettings::CAMERA_ACCESSED ||
        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
        state_ == TabSpecificContentSettings::CAMERA_BLOCKED ||
        state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED) {
      content_settings->SetContentSetting(
          primary_pattern, secondary_pattern,
          CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(), setting);
    }
  }
}

void ContentSettingMediaStreamBubbleModel::UpdateDefaultDeviceForType(
    content::MediaStreamType type,
    const std::string& device) {
  PrefService* prefs = profile()->GetPrefs();
  if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
    prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
  } else {
    DCHECK_EQ(content::MEDIA_DEVICE_VIDEO_CAPTURE, type);
    prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
  }
}

void ContentSettingMediaStreamBubbleModel::SetMediaMenus() {
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  const std::string& requested_microphone =
       content_settings->media_stream_requested_audio_device();
   const std::string& requested_camera =
       content_settings->media_stream_requested_video_device();

  // Add microphone menu.
  PrefService* prefs = profile()->GetPrefs();
  MediaCaptureDevicesDispatcher* dispatcher =
      MediaCaptureDevicesDispatcher::GetInstance();
  const content::MediaStreamDevices& microphones =
      dispatcher->GetAudioCaptureDevices();

  bool show_mic_menu =
      (state_ == TabSpecificContentSettings::MICROPHONE_ACCESSED ||
       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
       state_ == TabSpecificContentSettings::MICROPHONE_BLOCKED ||
       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED);
  bool show_camera_menu =
      (state_ == TabSpecificContentSettings::CAMERA_ACCESSED ||
       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_ACCESSED ||
       state_ == TabSpecificContentSettings::CAMERA_BLOCKED ||
       state_ == TabSpecificContentSettings::MICROPHONE_CAMERA_BLOCKED);
  DCHECK(show_mic_menu || show_camera_menu);

  if (show_mic_menu) {
    MediaMenu mic_menu;
    mic_menu.label = l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_MIC_LABEL);
    if (!microphones.empty()) {
      std::string preferred_mic;
      if (requested_microphone.empty()) {
        preferred_mic = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
        mic_menu.disabled = false;
      } else {
        // Set the |disabled| to true in order to disable the device selection
        // menu on the media settings bubble. This must be done if the website
        // manages the microphone devices itself.
        preferred_mic = requested_microphone;
        mic_menu.disabled = true;
      }

      mic_menu.default_device = GetMediaDeviceById(preferred_mic, microphones);
      mic_menu.selected_device = mic_menu.default_device;
    }
    add_media_menu(content::MEDIA_DEVICE_AUDIO_CAPTURE, mic_menu);
  }

  if (show_camera_menu) {
    const content::MediaStreamDevices& cameras =
        dispatcher->GetVideoCaptureDevices();
    MediaMenu camera_menu;
    camera_menu.label =
        l10n_util::GetStringUTF8(IDS_MEDIA_SELECTED_CAMERA_LABEL);
    if (!cameras.empty()) {
      std::string preferred_camera;
      if (requested_camera.empty()) {
        preferred_camera = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
        camera_menu.disabled = false;
      } else {
        // Disable the menu since the website is managing the camera devices
        // itself.
        preferred_camera = requested_camera;
        camera_menu.disabled = true;
      }

      camera_menu.default_device =
          GetMediaDeviceById(preferred_camera, cameras);
      camera_menu.selected_device = camera_menu.default_device;
    }
    add_media_menu(content::MEDIA_DEVICE_VIDEO_CAPTURE, camera_menu);
  }
}

void ContentSettingMediaStreamBubbleModel::OnRadioClicked(int radio_index) {
  selected_item_ = radio_index;
}

void ContentSettingMediaStreamBubbleModel::OnMediaMenuClicked(
    content::MediaStreamType type,
    const std::string& selected_device_id) {
  DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
         type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
  DCHECK_EQ(1U, bubble_content().media_menus.count(type));
  MediaCaptureDevicesDispatcher* dispatcher =
      MediaCaptureDevicesDispatcher::GetInstance();
  const content::MediaStreamDevices& devices =
      (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) ?
          dispatcher->GetAudioCaptureDevices() :
          dispatcher->GetVideoCaptureDevices();
  set_selected_device(GetMediaDeviceById(selected_device_id, devices));
}

class ContentSettingDomainListBubbleModel
    : public ContentSettingTitleAndLinkModel {
 public:
  ContentSettingDomainListBubbleModel(Delegate* delegate,
                                      WebContents* web_contents,
                                      Profile* profile,
                                      ContentSettingsType content_type);
  virtual ~ContentSettingDomainListBubbleModel() {}

 private:
  void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
  void SetDomainsAndCustomLink();
  virtual void OnCustomLinkClicked() OVERRIDE;
};

ContentSettingDomainListBubbleModel::ContentSettingDomainListBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingTitleAndLinkModel(
        delegate, web_contents, profile, content_type) {
  DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) <<
      "SetDomains currently only supports geolocation content type";
  SetDomainsAndCustomLink();
}

void ContentSettingDomainListBubbleModel::MaybeAddDomainList(
    const std::set<std::string>& hosts, int title_id) {
  if (!hosts.empty()) {
    DomainList domain_list;
    domain_list.title = l10n_util::GetStringUTF8(title_id);
    domain_list.hosts = hosts;
    add_domain_list(domain_list);
  }
}

void ContentSettingDomainListBubbleModel::SetDomainsAndCustomLink() {
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  const ContentSettingsUsagesState& usages =
      content_settings->geolocation_usages_state();
  ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
  unsigned int tab_state_flags = 0;
  usages.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
  // Divide the tab's current geolocation users into sets according to their
  // permission state.
  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
                     IDS_GEOLOCATION_BUBBLE_SECTION_ALLOWED);

  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
                     IDS_GEOLOCATION_BUBBLE_SECTION_DENIED);

  if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
    set_custom_link(l10n_util::GetStringUTF8(
        IDS_GEOLOCATION_BUBBLE_CLEAR_LINK));
    set_custom_link_enabled(true);
  } else if (tab_state_flags &
             ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
    set_custom_link(l10n_util::GetStringUTF8(
        IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
  }
}

void ContentSettingDomainListBubbleModel::OnCustomLinkClicked() {
  if (!web_contents())
    return;
  // Reset this embedder's entry to default for each of the requesting
  // origins currently on the page.
  const GURL& embedder_url = web_contents()->GetURL();
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  const ContentSettingsUsagesState::StateMap& state_map =
      content_settings->geolocation_usages_state().state_map();
  HostContentSettingsMap* settings_map =
      profile()->GetHostContentSettingsMap();

  for (ContentSettingsUsagesState::StateMap::const_iterator it =
       state_map.begin(); it != state_map.end(); ++it) {
    settings_map->SetContentSetting(
        ContentSettingsPattern::FromURLNoWildcard(it->first),
        ContentSettingsPattern::FromURLNoWildcard(embedder_url),
        CONTENT_SETTINGS_TYPE_GEOLOCATION,
        std::string(),
        CONTENT_SETTING_DEFAULT);
  }
}

class ContentSettingMixedScriptBubbleModel
    : public ContentSettingTitleLinkAndCustomModel {
 public:
  ContentSettingMixedScriptBubbleModel(Delegate* delegate,
                                       WebContents* web_contents,
                                       Profile* profile,
                                       ContentSettingsType content_type);

  virtual ~ContentSettingMixedScriptBubbleModel() {}

 private:
  virtual void OnCustomLinkClicked() OVERRIDE;
};

ContentSettingMixedScriptBubbleModel::ContentSettingMixedScriptBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingTitleLinkAndCustomModel(
        delegate, web_contents, profile, content_type) {
  DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
  set_custom_link_enabled(true);
}

void ContentSettingMixedScriptBubbleModel::OnCustomLinkClicked() {
  content::RecordAction(UserMetricsAction("MixedScript_LoadAnyway_Bubble"));
  DCHECK(web_contents());
  content::RenderViewHost* host = web_contents()->GetRenderViewHost();
  host->Send(new ChromeViewMsg_SetAllowRunningInsecureContent(
      host->GetRoutingID(), true));
}

ContentSettingRPHBubbleModel::ContentSettingRPHBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ProtocolHandlerRegistry* registry,
    ContentSettingsType content_type)
    : ContentSettingTitleAndLinkModel(
          delegate, web_contents, profile, content_type),
      selected_item_(0),
      registry_(registry),
      pending_handler_(ProtocolHandler::EmptyProtocolHandler()),
      previous_handler_(ProtocolHandler::EmptyProtocolHandler()) {

  DCHECK_EQ(CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, content_type);

  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents);
  pending_handler_ = content_settings->pending_protocol_handler();
  previous_handler_ = content_settings->previous_protocol_handler();

  base::string16 protocol;
  if (pending_handler_.protocol() == "mailto") {
    protocol = l10n_util::GetStringUTF16(
        IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME);
  } else if (pending_handler_.protocol() == "webcal") {
    protocol = l10n_util::GetStringUTF16(
        IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME);
  } else {
    protocol = base::UTF8ToUTF16(pending_handler_.protocol());
  }

  if (previous_handler_.IsEmpty()) {
    set_title(l10n_util::GetStringFUTF8(
        IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM,
        pending_handler_.title(),
        base::UTF8ToUTF16(pending_handler_.url().host()),
        protocol));
  } else {
    set_title(l10n_util::GetStringFUTF8(
        IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE,
        pending_handler_.title(),
        base::UTF8ToUTF16(pending_handler_.url().host()),
        protocol, previous_handler_.title()));
  }

  std::string radio_allow_label =
      l10n_util::GetStringFUTF8(IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT,
                                pending_handler_.title());
  std::string radio_deny_label =
      l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_DENY);
  std::string radio_ignore_label =
      l10n_util::GetStringUTF8(IDS_REGISTER_PROTOCOL_HANDLER_IGNORE);

  GURL url = web_contents->GetURL();
  RadioGroup radio_group;
  radio_group.url = url;

  radio_group.radio_items.push_back(radio_allow_label);
  radio_group.radio_items.push_back(radio_deny_label);
  radio_group.radio_items.push_back(radio_ignore_label);
  ContentSetting setting =
      content_settings->pending_protocol_handler_setting();
  if (setting == CONTENT_SETTING_ALLOW)
    radio_group.default_item = RPH_ALLOW;
  else if (setting == CONTENT_SETTING_BLOCK)
    radio_group.default_item = RPH_BLOCK;
  else
    radio_group.default_item = RPH_IGNORE;

  selected_item_ = radio_group.default_item;
  set_radio_group_enabled(true);
  set_radio_group(radio_group);
}

void ContentSettingRPHBubbleModel::OnRadioClicked(int radio_index) {
  if (selected_item_ == radio_index)
    return;

  selected_item_ = radio_index;

  if (radio_index == RPH_ALLOW)
    RegisterProtocolHandler();
  else if (radio_index == RPH_BLOCK)
    UnregisterProtocolHandler();
  else if (radio_index == RPH_IGNORE)
    IgnoreProtocolHandler();
  else
    NOTREACHED();
}

void ContentSettingRPHBubbleModel::OnDoneClicked() {
  // The user has one chance to deal with the RPH content setting UI,
  // then we remove it.
  TabSpecificContentSettings::FromWebContents(web_contents())->
      ClearPendingProtocolHandler();
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
      content::Source<WebContents>(web_contents()),
      content::NotificationService::NoDetails());
}

void ContentSettingRPHBubbleModel::RegisterProtocolHandler() {
  // A no-op if the handler hasn't been ignored, but needed in case the user
  // selects sequences like register/ignore/register.
  registry_->RemoveIgnoredHandler(pending_handler_);

  registry_->OnAcceptRegisterProtocolHandler(pending_handler_);
  TabSpecificContentSettings::FromWebContents(web_contents())->
      set_pending_protocol_handler_setting(CONTENT_SETTING_ALLOW);
}

void ContentSettingRPHBubbleModel::UnregisterProtocolHandler() {
  registry_->OnDenyRegisterProtocolHandler(pending_handler_);
  TabSpecificContentSettings::FromWebContents(web_contents())->
      set_pending_protocol_handler_setting(CONTENT_SETTING_BLOCK);
  ClearOrSetPreviousHandler();
}

void ContentSettingRPHBubbleModel::IgnoreProtocolHandler() {
  registry_->OnIgnoreRegisterProtocolHandler(pending_handler_);
  TabSpecificContentSettings::FromWebContents(web_contents())->
      set_pending_protocol_handler_setting(CONTENT_SETTING_DEFAULT);
  ClearOrSetPreviousHandler();
}

void ContentSettingRPHBubbleModel::ClearOrSetPreviousHandler() {
  if (previous_handler_.IsEmpty()) {
    registry_->ClearDefault(pending_handler_.protocol());
  } else {
    registry_->OnAcceptRegisterProtocolHandler(previous_handler_);
  }
}

class ContentSettingMidiSysExBubbleModel
    : public ContentSettingTitleAndLinkModel {
 public:
  ContentSettingMidiSysExBubbleModel(Delegate* delegate,
                                     WebContents* web_contents,
                                     Profile* profile,
                                     ContentSettingsType content_type);
  virtual ~ContentSettingMidiSysExBubbleModel() {}

 private:
  void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id);
  void SetDomainsAndCustomLink();
  virtual void OnCustomLinkClicked() OVERRIDE;
};

ContentSettingMidiSysExBubbleModel::ContentSettingMidiSysExBubbleModel(
    Delegate* delegate,
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : ContentSettingTitleAndLinkModel(
        delegate, web_contents, profile, content_type) {
  DCHECK_EQ(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, content_type);
  SetDomainsAndCustomLink();
}

void ContentSettingMidiSysExBubbleModel::MaybeAddDomainList(
    const std::set<std::string>& hosts, int title_id) {
  if (!hosts.empty()) {
    DomainList domain_list;
    domain_list.title = l10n_util::GetStringUTF8(title_id);
    domain_list.hosts = hosts;
    add_domain_list(domain_list);
  }
}

void ContentSettingMidiSysExBubbleModel::SetDomainsAndCustomLink() {
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  const ContentSettingsUsagesState& usages_state =
      content_settings->midi_usages_state();
  ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
  unsigned int tab_state_flags = 0;
  usages_state.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags);
  // Divide the tab's current MIDI sysex users into sets according to their
  // permission state.
  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW],
                     IDS_MIDI_SYSEX_BUBBLE_ALLOWED);

  MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK],
                     IDS_MIDI_SYSEX_BUBBLE_DENIED);

  if (tab_state_flags & ContentSettingsUsagesState::TABSTATE_HAS_EXCEPTION) {
    set_custom_link(l10n_util::GetStringUTF8(
        IDS_MIDI_SYSEX_BUBBLE_CLEAR_LINK));
    set_custom_link_enabled(true);
  } else if (tab_state_flags &
             ContentSettingsUsagesState::TABSTATE_HAS_CHANGED) {
    set_custom_link(l10n_util::GetStringUTF8(
        IDS_MIDI_SYSEX_BUBBLE_REQUIRE_RELOAD_TO_CLEAR));
  }
}

void ContentSettingMidiSysExBubbleModel::OnCustomLinkClicked() {
  if (!web_contents())
    return;
  // Reset this embedder's entry to default for each of the requesting
  // origins currently on the page.
  TabSpecificContentSettings* content_settings =
      TabSpecificContentSettings::FromWebContents(web_contents());
  const ContentSettingsUsagesState::StateMap& state_map =
      content_settings->midi_usages_state().state_map();
  HostContentSettingsMap* settings_map =
      profile()->GetHostContentSettingsMap();

  for (ContentSettingsUsagesState::StateMap::const_iterator it =
       state_map.begin(); it != state_map.end(); ++it) {
    settings_map->SetContentSetting(
        ContentSettingsPattern::FromURLNoWildcard(it->first),
        ContentSettingsPattern::Wildcard(),
        CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
        std::string(),
        CONTENT_SETTING_DEFAULT);
  }
}

// static
ContentSettingBubbleModel*
    ContentSettingBubbleModel::CreateContentSettingBubbleModel(
        Delegate* delegate,
        WebContents* web_contents,
        Profile* profile,
        ContentSettingsType content_type) {
  if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) {
    return new ContentSettingCookiesBubbleModel(delegate, web_contents, profile,
                                                content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) {
    return new ContentSettingPopupBubbleModel(delegate, web_contents, profile,
                                              content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
    return new ContentSettingDomainListBubbleModel(delegate, web_contents,
                                                   profile, content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM) {
    return new ContentSettingMediaStreamBubbleModel(delegate, web_contents,
                                                    profile);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) {
    return new ContentSettingPluginBubbleModel(delegate, web_contents, profile,
                                               content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
    return new ContentSettingMixedScriptBubbleModel(delegate, web_contents,
                                                    profile, content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) {
    ProtocolHandlerRegistry* registry =
        ProtocolHandlerRegistryFactory::GetForProfile(profile);
    return new ContentSettingRPHBubbleModel(delegate, web_contents, profile,
                                            registry, content_type);
  }
  if (content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
    return new ContentSettingMidiSysExBubbleModel(delegate, web_contents,
                                                  profile, content_type);
  }
  return new ContentSettingSingleRadioGroup(delegate, web_contents, profile,
                                            content_type);
}

ContentSettingBubbleModel::ContentSettingBubbleModel(
    WebContents* web_contents,
    Profile* profile,
    ContentSettingsType content_type)
    : web_contents_(web_contents),
      profile_(profile),
      content_type_(content_type),
      setting_is_managed_(false) {
  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
                 content::Source<WebContents>(web_contents));
  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                 content::Source<Profile>(profile_));
}

ContentSettingBubbleModel::~ContentSettingBubbleModel() {
}

ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {}

ContentSettingBubbleModel::RadioGroup::~RadioGroup() {}

ContentSettingBubbleModel::DomainList::DomainList() {}

ContentSettingBubbleModel::DomainList::~DomainList() {}

ContentSettingBubbleModel::MediaMenu::MediaMenu() : disabled(false) {}

ContentSettingBubbleModel::MediaMenu::~MediaMenu() {}

ContentSettingBubbleModel::BubbleContent::BubbleContent()
    : radio_group_enabled(false),
      custom_link_enabled(false) {
}

ContentSettingBubbleModel::BubbleContent::~BubbleContent() {}

void ContentSettingBubbleModel::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    DCHECK_EQ(web_contents_,
              content::Source<WebContents>(source).ptr());
    web_contents_ = NULL;
  } else {
    DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
    DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
    profile_ = NULL;
  }
}

/* [<][>][^][v][top][bottom][index][help] */