| // Copyright (c) 2011 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/content_setting_bubble_model.h" |
| |
| #include "base/command_line.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/blocked_content_container.h" |
| #include "chrome/browser/content_settings/host_content_settings_map.h" |
| #include "chrome/browser/geolocation/geolocation_content_settings_map.h" |
| #include "chrome/browser/metrics/user_metrics.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/tab_contents/tab_specific_content_settings.h" |
| #include "chrome/browser/ui/collected_cookies_infobar_delegate.h" |
| #include "chrome/common/pref_names.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/browser/tab_contents/tab_contents_delegate.h" |
| #include "content/common/notification_service.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/net_util.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| class ContentSettingTitleAndLinkModel : public ContentSettingBubbleModel { |
| public: |
| ContentSettingTitleAndLinkModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingBubbleModel(tab_contents, profile, content_type) { |
| // Notifications do not have a bubble. |
| DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); |
| SetBlockedResources(); |
| SetTitle(); |
| SetManageLink(); |
| } |
| |
| virtual ~ContentSettingTitleAndLinkModel() {} |
| |
| private: |
| void SetBlockedResources() { |
| TabSpecificContentSettings* settings = |
| tab_contents()->GetTabSpecificContentSettings(); |
| const std::set<std::string>& resources = settings->BlockedResourcesForType( |
| content_type()); |
| for (std::set<std::string>::const_iterator it = resources.begin(); |
| it != resources.end(); ++it) { |
| AddBlockedResource(*it); |
| } |
| } |
| |
| void SetTitle() { |
| static const int kBlockedTitleIDs[] = { |
| IDS_BLOCKED_COOKIES_TITLE, |
| IDS_BLOCKED_IMAGES_TITLE, |
| IDS_BLOCKED_JAVASCRIPT_TITLE, |
| IDS_BLOCKED_PLUGINS_MESSAGE, |
| IDS_BLOCKED_POPUPS_TITLE, |
| 0, // Geolocation does not have an overall title. |
| 0, // Notifications do not have a bubble. |
| 0, // Prerender does not have a bubble. |
| }; |
| // Fields as for kBlockedTitleIDs, above. |
| static const int kResourceSpecificBlockedTitleIDs[] = { |
| 0, |
| 0, |
| 0, |
| IDS_BLOCKED_PLUGINS_TITLE, |
| 0, |
| 0, |
| 0, |
| 0, |
| }; |
| static const int kAccessedTitleIDs[] = { |
| IDS_ACCESSED_COOKIES_TITLE, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| }; |
| COMPILE_ASSERT(arraysize(kAccessedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| COMPILE_ASSERT(arraysize(kBlockedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| COMPILE_ASSERT(arraysize(kResourceSpecificBlockedTitleIDs) == |
| CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| const int *title_ids = kBlockedTitleIDs; |
| if (tab_contents() && |
| tab_contents()->GetTabSpecificContentSettings()->IsContentAccessed( |
| content_type()) && |
| !tab_contents()->GetTabSpecificContentSettings()->IsContentBlocked( |
| content_type())) { |
| title_ids = kAccessedTitleIDs; |
| } else if (!bubble_content().resource_identifiers.empty()) { |
| title_ids = kResourceSpecificBlockedTitleIDs; |
| } |
| if (title_ids[content_type()]) |
| set_title(l10n_util::GetStringUTF8(title_ids[content_type()])); |
| } |
| |
| void SetManageLink() { |
| static const int kLinkIDs[] = { |
| IDS_BLOCKED_COOKIES_LINK, |
| IDS_BLOCKED_IMAGES_LINK, |
| IDS_BLOCKED_JAVASCRIPT_LINK, |
| IDS_BLOCKED_PLUGINS_LINK, |
| IDS_BLOCKED_POPUPS_LINK, |
| IDS_GEOLOCATION_BUBBLE_MANAGE_LINK, |
| 0, // Notifications do not have a bubble. |
| 0, // Prerender does not have a bubble. |
| }; |
| COMPILE_ASSERT(arraysize(kLinkIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| set_manage_link(l10n_util::GetStringUTF8(kLinkIDs[content_type()])); |
| } |
| |
| virtual void OnManageLinkClicked() { |
| if (tab_contents()) |
| tab_contents()->delegate()->ShowContentSettingsPage(content_type()); |
| } |
| }; |
| |
| class ContentSettingTitleLinkAndCustomModel |
| : public ContentSettingTitleAndLinkModel { |
| public: |
| ContentSettingTitleLinkAndCustomModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) { |
| SetCustomLink(); |
| } |
| |
| virtual ~ContentSettingTitleLinkAndCustomModel() {} |
| |
| private: |
| void SetCustomLink() { |
| static const int kCustomIDs[] = { |
| IDS_BLOCKED_COOKIES_INFO, |
| 0, // Images do not have a custom link. |
| 0, // Javascript doesn't have a custom link. |
| IDS_BLOCKED_PLUGINS_LOAD_ALL, |
| 0, // Popups do not have a custom link. |
| 0, // Geolocation custom links are set within that class. |
| 0, // Notifications do not have a bubble. |
| 0, // Prerender does not have a bubble. |
| }; |
| COMPILE_ASSERT(arraysize(kCustomIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| if (kCustomIDs[content_type()]) |
| set_custom_link(l10n_util::GetStringUTF8(kCustomIDs[content_type()])); |
| } |
| |
| virtual void OnCustomLinkClicked() {} |
| }; |
| |
| |
| class ContentSettingSingleRadioGroup |
| : public ContentSettingTitleLinkAndCustomModel { |
| public: |
| ContentSettingSingleRadioGroup(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingTitleLinkAndCustomModel(tab_contents, profile, |
| content_type), |
| block_setting_(CONTENT_SETTING_BLOCK), |
| selected_item_(0) { |
| SetRadioGroup(); |
| } |
| |
| virtual ~ContentSettingSingleRadioGroup() { |
| if (settings_changed()) { |
| ContentSetting setting = |
| selected_item_ == 0 ? CONTENT_SETTING_ALLOW : block_setting_; |
| const std::set<std::string>& resources = |
| bubble_content().resource_identifiers; |
| if (resources.empty()) { |
| AddException(setting, std::string()); |
| } else { |
| for (std::set<std::string>::const_iterator it = resources.begin(); |
| it != resources.end(); ++it) { |
| AddException(setting, *it); |
| } |
| } |
| } |
| } |
| |
| protected: |
| bool settings_changed() const { |
| return selected_item_ != bubble_content().radio_group.default_item; |
| } |
| |
| private: |
| ContentSetting block_setting_; |
| int selected_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 SetRadioGroup() { |
| GURL url = tab_contents()->GetURL(); |
| std::wstring display_host_wide; |
| net::AppendFormattedHost(url, |
| UTF8ToWide(profile()->GetPrefs()->GetString(prefs::kAcceptLanguages)), |
| &display_host_wide, NULL, NULL); |
| std::string display_host(WideToUTF8(display_host_wide)); |
| |
| if (display_host.empty()) |
| display_host = url.spec(); |
| |
| const std::set<std::string>& resources = |
| bubble_content().resource_identifiers; |
| |
| RadioGroup radio_group; |
| radio_group.url = url; |
| |
| static const int kAllowIDs[] = { |
| IDS_BLOCKED_COOKIES_UNBLOCK, |
| IDS_BLOCKED_IMAGES_UNBLOCK, |
| IDS_BLOCKED_JAVASCRIPT_UNBLOCK, |
| IDS_BLOCKED_PLUGINS_UNBLOCK_ALL, |
| IDS_BLOCKED_POPUPS_UNBLOCK, |
| 0, // We don't manage geolocation here. |
| 0, // Notifications do not have a bubble. |
| 0, // Prerender does not have a bubble. |
| }; |
| COMPILE_ASSERT(arraysize(kAllowIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| // Fields as for kAllowIDs, above. |
| static const int kResourceSpecificAllowIDs[] = { |
| 0, |
| 0, |
| 0, |
| IDS_BLOCKED_PLUGINS_UNBLOCK, |
| 0, |
| 0, |
| 0, |
| 0, // Prerender does not have a bubble. |
| }; |
| COMPILE_ASSERT( |
| arraysize(kResourceSpecificAllowIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| std::string radio_allow_label; |
| const int* allowIDs = resources.empty() ? |
| kAllowIDs : kResourceSpecificAllowIDs; |
| radio_allow_label = l10n_util::GetStringFUTF8( |
| allowIDs[content_type()], UTF8ToUTF16(display_host)); |
| |
| static const int kBlockIDs[] = { |
| IDS_BLOCKED_COOKIES_NO_ACTION, |
| IDS_BLOCKED_IMAGES_NO_ACTION, |
| IDS_BLOCKED_JAVASCRIPT_NO_ACTION, |
| IDS_BLOCKED_PLUGINS_NO_ACTION, |
| IDS_BLOCKED_POPUPS_NO_ACTION, |
| 0, // We don't manage geolocation here. |
| 0, // Notifications do not have a bubble. |
| 0, // Prerender does not have a bubble. |
| }; |
| COMPILE_ASSERT(arraysize(kBlockIDs) == CONTENT_SETTINGS_NUM_TYPES, |
| Need_a_setting_for_every_content_settings_type); |
| std::string radio_block_label; |
| radio_block_label = l10n_util::GetStringUTF8(kBlockIDs[content_type()]); |
| |
| radio_group.radio_items.push_back(radio_allow_label); |
| radio_group.radio_items.push_back(radio_block_label); |
| HostContentSettingsMap* map = profile()->GetHostContentSettingsMap(); |
| ContentSetting mostRestrictiveSetting; |
| if (resources.empty()) { |
| mostRestrictiveSetting = |
| map->GetContentSetting(url, content_type(), std::string()); |
| } else { |
| mostRestrictiveSetting = CONTENT_SETTING_ALLOW; |
| for (std::set<std::string>::const_iterator it = resources.begin(); |
| it != resources.end(); ++it) { |
| ContentSetting setting = map->GetContentSetting(url, |
| content_type(), |
| *it); |
| if (setting == CONTENT_SETTING_BLOCK) { |
| mostRestrictiveSetting = CONTENT_SETTING_BLOCK; |
| break; |
| } |
| if (setting == CONTENT_SETTING_ASK) |
| mostRestrictiveSetting = CONTENT_SETTING_ASK; |
| } |
| } |
| if (mostRestrictiveSetting == CONTENT_SETTING_ALLOW) { |
| radio_group.default_item = 0; |
| // |block_setting_| is already set to |CONTENT_SETTING_BLOCK|. |
| } else { |
| radio_group.default_item = 1; |
| block_setting_ = mostRestrictiveSetting; |
| } |
| selected_item_ = radio_group.default_item; |
| set_radio_group(radio_group); |
| } |
| |
| void AddException(ContentSetting setting, |
| const std::string& resource_identifier) { |
| profile()->GetHostContentSettingsMap()->AddExceptionForURL( |
| bubble_content().radio_group.url, content_type(), resource_identifier, |
| setting); |
| } |
| |
| virtual void OnRadioClicked(int radio_index) { |
| selected_item_ = radio_index; |
| } |
| }; |
| |
| class ContentSettingCookiesBubbleModel : public ContentSettingSingleRadioGroup { |
| public: |
| ContentSettingCookiesBubbleModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) { |
| DCHECK_EQ(CONTENT_SETTINGS_TYPE_COOKIES, content_type); |
| set_custom_link_enabled(true); |
| } |
| |
| virtual ~ContentSettingCookiesBubbleModel() { |
| if (settings_changed()) { |
| tab_contents()->AddInfoBar( |
| new CollectedCookiesInfoBarDelegate(tab_contents())); |
| } |
| } |
| |
| private: |
| virtual void OnCustomLinkClicked() OVERRIDE { |
| if (tab_contents()) { |
| NotificationService::current()->Notify( |
| NotificationType::COLLECTED_COOKIES_SHOWN, |
| Source<TabSpecificContentSettings>( |
| tab_contents()->GetTabSpecificContentSettings()), |
| NotificationService::NoDetails()); |
| tab_contents()->delegate()->ShowCollectedCookiesDialog(tab_contents()); |
| } |
| } |
| }; |
| |
| class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup { |
| public: |
| ContentSettingPluginBubbleModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) { |
| DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS); |
| set_custom_link_enabled(tab_contents && tab_contents-> |
| GetTabSpecificContentSettings()->load_plugins_link_enabled()); |
| } |
| |
| virtual ~ContentSettingPluginBubbleModel() {} |
| |
| private: |
| virtual void OnCustomLinkClicked() OVERRIDE { |
| UserMetrics::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble")); |
| DCHECK(tab_contents()); |
| tab_contents()->render_view_host()->LoadBlockedPlugins(); |
| set_custom_link_enabled(false); |
| tab_contents()->GetTabSpecificContentSettings()-> |
| set_load_plugins_link_enabled(false); |
| } |
| }; |
| |
| class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup { |
| public: |
| ContentSettingPopupBubbleModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) { |
| SetPopups(); |
| } |
| |
| virtual ~ContentSettingPopupBubbleModel() {} |
| |
| private: |
| void SetPopups() { |
| // check for crbug.com/53176 |
| if (!tab_contents()->blocked_content_container()) |
| return; |
| std::vector<TabContents*> blocked_contents; |
| tab_contents()->blocked_content_container()->GetBlockedContents( |
| &blocked_contents); |
| for (std::vector<TabContents*>::const_iterator |
| i(blocked_contents.begin()); i != blocked_contents.end(); ++i) { |
| std::string title(UTF16ToUTF8((*i)->GetTitle())); |
| // The popup may not have committed a load yet, in which case it won't |
| // have a URL or title. |
| if (title.empty()) |
| title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE); |
| PopupItem popup_item; |
| popup_item.title = title; |
| popup_item.bitmap = (*i)->GetFavicon(); |
| popup_item.tab_contents = (*i); |
| add_popup(popup_item); |
| } |
| } |
| |
| virtual void OnPopupClicked(int index) { |
| if (tab_contents() && tab_contents()->blocked_content_container()) { |
| tab_contents()->blocked_content_container()->LaunchForContents( |
| bubble_content().popup_items[index].tab_contents); |
| } |
| } |
| }; |
| |
| class ContentSettingDomainListBubbleModel |
| : public ContentSettingTitleAndLinkModel { |
| public: |
| ContentSettingDomainListBubbleModel(TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) { |
| DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) << |
| "SetDomains currently only supports geolocation content type"; |
| SetDomainsAndCustomLink(); |
| } |
| |
| virtual ~ContentSettingDomainListBubbleModel() {} |
| |
| private: |
| void 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 SetDomainsAndCustomLink() { |
| TabSpecificContentSettings* content_settings = |
| tab_contents()->GetTabSpecificContentSettings(); |
| const GeolocationSettingsState& settings = |
| content_settings->geolocation_settings_state(); |
| GeolocationSettingsState::FormattedHostsPerState formatted_hosts_per_state; |
| unsigned int tab_state_flags = 0; |
| settings.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 & GeolocationSettingsState::TABSTATE_HAS_EXCEPTION) { |
| set_custom_link(l10n_util::GetStringUTF8( |
| IDS_GEOLOCATION_BUBBLE_CLEAR_LINK)); |
| set_custom_link_enabled(true); |
| } else if (tab_state_flags & |
| GeolocationSettingsState::TABSTATE_HAS_CHANGED) { |
| set_custom_link(l10n_util::GetStringUTF8( |
| IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR)); |
| } |
| } |
| virtual void OnCustomLinkClicked() OVERRIDE { |
| if (!tab_contents()) |
| return; |
| // Reset this embedder's entry to default for each of the requesting |
| // origins currently on the page. |
| const GURL& embedder_url = tab_contents()->GetURL(); |
| TabSpecificContentSettings* content_settings = |
| tab_contents()->GetTabSpecificContentSettings(); |
| const GeolocationSettingsState::StateMap& state_map = |
| content_settings->geolocation_settings_state().state_map(); |
| GeolocationContentSettingsMap* settings_map = |
| profile()->GetGeolocationContentSettingsMap(); |
| for (GeolocationSettingsState::StateMap::const_iterator it = |
| state_map.begin(); it != state_map.end(); ++it) { |
| settings_map->SetContentSetting(it->first, embedder_url, |
| CONTENT_SETTING_DEFAULT); |
| } |
| } |
| }; |
| |
| // static |
| ContentSettingBubbleModel* |
| ContentSettingBubbleModel::CreateContentSettingBubbleModel( |
| TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) { |
| if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) { |
| return new ContentSettingCookiesBubbleModel(tab_contents, profile, |
| content_type); |
| } |
| if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) { |
| return new ContentSettingPopupBubbleModel(tab_contents, profile, |
| content_type); |
| } |
| if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) { |
| return new ContentSettingDomainListBubbleModel(tab_contents, profile, |
| content_type); |
| } |
| if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) { |
| return new ContentSettingPluginBubbleModel(tab_contents, profile, |
| content_type); |
| } |
| return new ContentSettingSingleRadioGroup(tab_contents, profile, |
| content_type); |
| } |
| |
| ContentSettingBubbleModel::ContentSettingBubbleModel( |
| TabContents* tab_contents, |
| Profile* profile, |
| ContentSettingsType content_type) |
| : tab_contents_(tab_contents), |
| profile_(profile), |
| content_type_(content_type) { |
| registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, |
| Source<TabContents>(tab_contents)); |
| } |
| |
| ContentSettingBubbleModel::~ContentSettingBubbleModel() { |
| } |
| |
| ContentSettingBubbleModel::RadioGroup::RadioGroup() : default_item(0) {} |
| |
| ContentSettingBubbleModel::RadioGroup::~RadioGroup() {} |
| |
| ContentSettingBubbleModel::DomainList::DomainList() {} |
| |
| ContentSettingBubbleModel::DomainList::~DomainList() {} |
| |
| ContentSettingBubbleModel::BubbleContent::BubbleContent() |
| : custom_link_enabled(false) { |
| } |
| |
| ContentSettingBubbleModel::BubbleContent::~BubbleContent() {} |
| |
| |
| void ContentSettingBubbleModel::AddBlockedResource( |
| const std::string& resource_identifier) { |
| bubble_content_.resource_identifiers.insert(resource_identifier); |
| } |
| |
| void ContentSettingBubbleModel::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); |
| DCHECK(source == Source<TabContents>(tab_contents_)); |
| tab_contents_ = NULL; |
| } |