content_setting_bubble_model.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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/content_setting_bubble_model.h" 6 7#include "app/l10n_util.h" 8#include "base/command_line.h" 9#include "base/utf_string_conversions.h" 10#include "chrome/browser/blocked_popup_container.h" 11#include "chrome/browser/geolocation/geolocation_content_settings_map.h" 12#include "chrome/browser/host_content_settings_map.h" 13#include "chrome/browser/metrics/user_metrics.h" 14#include "chrome/browser/prefs/pref_service.h" 15#include "chrome/browser/profile.h" 16#include "chrome/browser/renderer_host/render_view_host.h" 17#include "chrome/browser/tab_contents/tab_contents.h" 18#include "chrome/browser/tab_contents/tab_contents_delegate.h" 19#include "chrome/browser/tab_contents/tab_specific_content_settings.h" 20#include "chrome/common/chrome_switches.h" 21#include "chrome/common/notification_service.h" 22#include "chrome/common/pref_names.h" 23#include "grit/generated_resources.h" 24#include "net/base/net_util.h" 25 26class ContentSettingTitleAndLinkModel : public ContentSettingBubbleModel { 27 public: 28 ContentSettingTitleAndLinkModel(TabContents* tab_contents, Profile* profile, 29 ContentSettingsType content_type) 30 : ContentSettingBubbleModel(tab_contents, profile, content_type) { 31 // Notifications do not have a bubble. 32 DCHECK_NE(content_type, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); 33 SetBlockedResources(); 34 SetTitle(); 35 SetManageLink(); 36 } 37 38 private: 39 void SetBlockedResources() { 40 TabSpecificContentSettings* settings = 41 tab_contents()->GetTabSpecificContentSettings(); 42 const std::set<std::string>& resources = settings->BlockedResourcesForType( 43 content_type()); 44 for (std::set<std::string>::const_iterator it = resources.begin(); 45 it != resources.end(); ++it) { 46 AddBlockedResource(*it); 47 } 48 } 49 50 void SetTitle() { 51 static const int kBlockedTitleIDs[] = { 52 IDS_BLOCKED_COOKIES_TITLE, 53 IDS_BLOCKED_IMAGES_TITLE, 54 IDS_BLOCKED_JAVASCRIPT_TITLE, 55 IDS_BLOCKED_PLUGINS_MESSAGE, 56 IDS_BLOCKED_POPUPS_TITLE, 57 0, // Geolocation does not have an overall title. 58 0, // Notifications do not have a bubble. 59 }; 60 // Fields as for kBlockedTitleIDs, above. 61 static const int kResourceSpecificBlockedTitleIDs[] = { 62 0, 63 0, 64 0, 65 IDS_BLOCKED_PLUGINS_TITLE, 66 0, 67 0, 68 0, 69 }; 70 static const int kAccessedTitleIDs[] = { 71 IDS_ACCESSED_COOKIES_TITLE, 72 0, 73 0, 74 0, 75 0, 76 0, 77 0, 78 }; 79 COMPILE_ASSERT(arraysize(kAccessedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES, 80 Need_a_setting_for_every_content_settings_type); 81 COMPILE_ASSERT(arraysize(kBlockedTitleIDs) == CONTENT_SETTINGS_NUM_TYPES, 82 Need_a_setting_for_every_content_settings_type); 83 COMPILE_ASSERT(arraysize(kResourceSpecificBlockedTitleIDs) == 84 CONTENT_SETTINGS_NUM_TYPES, 85 Need_a_setting_for_every_content_settings_type); 86 const int *title_ids = kBlockedTitleIDs; 87 if (tab_contents() && 88 tab_contents()->GetTabSpecificContentSettings()->IsContentAccessed( 89 content_type()) && 90 !tab_contents()->GetTabSpecificContentSettings()->IsContentBlocked( 91 content_type())) { 92 title_ids = kAccessedTitleIDs; 93 } else if (!bubble_content().resource_identifiers.empty()) { 94 title_ids = kResourceSpecificBlockedTitleIDs; 95 } 96 if (title_ids[content_type()]) 97 set_title(l10n_util::GetStringUTF8(title_ids[content_type()])); 98 } 99 100 void SetManageLink() { 101 static const int kLinkIDs[] = { 102 IDS_BLOCKED_COOKIES_LINK, 103 IDS_BLOCKED_IMAGES_LINK, 104 IDS_BLOCKED_JAVASCRIPT_LINK, 105 IDS_BLOCKED_PLUGINS_LINK, 106 IDS_BLOCKED_POPUPS_LINK, 107 IDS_GEOLOCATION_BUBBLE_MANAGE_LINK, 108 0, // Notifications do not have a bubble. 109 }; 110 COMPILE_ASSERT(arraysize(kLinkIDs) == CONTENT_SETTINGS_NUM_TYPES, 111 Need_a_setting_for_every_content_settings_type); 112 set_manage_link(l10n_util::GetStringUTF8(kLinkIDs[content_type()])); 113 } 114 115 virtual void OnManageLinkClicked() { 116 if (tab_contents()) 117 tab_contents()->delegate()->ShowContentSettingsWindow(content_type()); 118 } 119}; 120 121class ContentSettingTitleLinkAndInfoModel 122 : public ContentSettingTitleAndLinkModel { 123 public: 124 ContentSettingTitleLinkAndInfoModel(TabContents* tab_contents, 125 Profile* profile, 126 ContentSettingsType content_type) 127 : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) { 128 SetInfoLink(); 129 } 130 131 private: 132 void SetInfoLink() { 133 static const int kInfoIDs[] = { 134 IDS_BLOCKED_COOKIES_INFO, 135 0, // Images do not have an info link. 136 0, // Javascript doesn't have an info link. 137 0, // Plugins do not have an info link. 138 0, // Popups do not have an info link. 139 0, // Geolocation does not have an info link. 140 0, // Notifications do not have a bubble. 141 }; 142 COMPILE_ASSERT(arraysize(kInfoIDs) == CONTENT_SETTINGS_NUM_TYPES, 143 Need_a_setting_for_every_content_settings_type); 144 if (kInfoIDs[content_type()]) 145 set_info_link(l10n_util::GetStringUTF8(kInfoIDs[content_type()])); 146 } 147 148 virtual void OnInfoLinkClicked() { 149 DCHECK(content_type() == CONTENT_SETTINGS_TYPE_COOKIES); 150 if (tab_contents()) { 151 NotificationService::current()->Notify( 152 NotificationType::COLLECTED_COOKIES_SHOWN, 153 Source<TabSpecificContentSettings>( 154 tab_contents()->GetTabSpecificContentSettings()), 155 NotificationService::NoDetails()); 156 tab_contents()->delegate()->ShowCollectedCookiesDialog(tab_contents()); 157 } 158 } 159}; 160 161 162class ContentSettingSingleRadioGroup : public ContentSettingTitleAndLinkModel { 163 public: 164 ContentSettingSingleRadioGroup(TabContents* tab_contents, Profile* profile, 165 ContentSettingsType content_type) 166 : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) { 167 SetRadioGroup(); 168 } 169 170 private: 171 void SetRadioGroup() { 172 GURL url = tab_contents()->GetURL(); 173 std::wstring display_host_wide; 174 net::AppendFormattedHost(url, 175 UTF8ToWide(profile()->GetPrefs()->GetString(prefs::kAcceptLanguages)), 176 &display_host_wide, NULL, NULL); 177 std::string display_host(WideToUTF8(display_host_wide)); 178 179 const std::set<std::string>& resources = 180 bubble_content().resource_identifiers; 181 182 RadioGroup radio_group; 183 radio_group.url = url; 184 185 static const int kAllowIDs[] = { 186 0, // We don't manage cookies here. 187 IDS_BLOCKED_IMAGES_UNBLOCK, 188 IDS_BLOCKED_JAVASCRIPT_UNBLOCK, 189 IDS_BLOCKED_PLUGINS_UNBLOCK_ALL, 190 IDS_BLOCKED_POPUPS_UNBLOCK, 191 0, // We don't manage geolocation here. 192 0, // Notifications do not have a bubble. 193 }; 194 COMPILE_ASSERT(arraysize(kAllowIDs) == CONTENT_SETTINGS_NUM_TYPES, 195 Need_a_setting_for_every_content_settings_type); 196 // Fields as for kAllowIDs, above. 197 static const int kResourceSpecificAllowIDs[] = { 198 0, 199 0, 200 0, 201 IDS_BLOCKED_PLUGINS_UNBLOCK, 202 0, 203 0, 204 0, 205 }; 206 COMPILE_ASSERT( 207 arraysize(kResourceSpecificAllowIDs) == CONTENT_SETTINGS_NUM_TYPES, 208 Need_a_setting_for_every_content_settings_type); 209 std::string radio_allow_label; 210 const int* allowIDs = resources.empty() ? 211 kAllowIDs : kResourceSpecificAllowIDs; 212 radio_allow_label = l10n_util::GetStringFUTF8( 213 allowIDs[content_type()], UTF8ToUTF16(display_host)); 214 215 static const int kBlockIDs[] = { 216 0, // We don't manage cookies here. 217 IDS_BLOCKED_IMAGES_NO_ACTION, 218 IDS_BLOCKED_JAVASCRIPT_NO_ACTION, 219 IDS_BLOCKED_PLUGINS_NO_ACTION, 220 IDS_BLOCKED_POPUPS_NO_ACTION, 221 0, // We don't manage geolocation here. 222 0, // Notifications do not have a bubble. 223 }; 224 COMPILE_ASSERT(arraysize(kBlockIDs) == CONTENT_SETTINGS_NUM_TYPES, 225 Need_a_setting_for_every_content_settings_type); 226 std::string radio_block_label; 227 radio_block_label = l10n_util::GetStringFUTF8( 228 kBlockIDs[content_type()], UTF8ToUTF16(display_host)); 229 230 radio_group.radio_items.push_back(radio_allow_label); 231 radio_group.radio_items.push_back(radio_block_label); 232 HostContentSettingsMap* map = profile()->GetHostContentSettingsMap(); 233 if (resources.empty()) { 234 ContentSetting setting = map->GetContentSetting(url, content_type(), 235 std::string()); 236 radio_group.default_item = (setting == CONTENT_SETTING_ALLOW) ? 0 : 1; 237 } else { 238 // The default item is "block" if at least one of the resources 239 // is blocked. 240 radio_group.default_item = 0; 241 for (std::set<std::string>::const_iterator it = resources.begin(); 242 it != resources.end(); ++it) { 243 ContentSetting setting = map->GetContentSetting( 244 url, content_type(), *it); 245 if (setting == CONTENT_SETTING_BLOCK) { 246 radio_group.default_item = 1; 247 break; 248 } 249 } 250 } 251 set_radio_group(radio_group); 252 } 253 254 void AddException(ContentSetting setting, 255 const std::string& resource_identifier) { 256 profile()->GetHostContentSettingsMap()->AddExceptionForURL( 257 bubble_content().radio_group.url, content_type(), resource_identifier, 258 setting); 259 } 260 261 virtual void OnRadioClicked(int radio_index) { 262 ContentSetting setting = 263 radio_index == 0 ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; 264 const std::set<std::string>& resources = 265 bubble_content().resource_identifiers; 266 if (resources.empty()) { 267 AddException(setting, std::string()); 268 } else { 269 for (std::set<std::string>::const_iterator it = resources.begin(); 270 it != resources.end(); ++it) { 271 AddException(setting, *it); 272 } 273 } 274 } 275}; 276 277class ContentSettingPluginBubbleModel : public ContentSettingSingleRadioGroup { 278 public: 279 ContentSettingPluginBubbleModel(TabContents* tab_contents, Profile* profile, 280 ContentSettingsType content_type) 281 : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) { 282 DCHECK_EQ(content_type, CONTENT_SETTINGS_TYPE_PLUGINS); 283 SetLoadPluginsLinkTitle(); 284 } 285 286 private: 287 void SetLoadPluginsLinkTitle() { 288 if (!CommandLine::ForCurrentProcess()->HasSwitch( 289 switches::kDisableClickToPlay)) { 290 set_load_plugins_link_title( 291 l10n_util::GetStringUTF8(IDS_BLOCKED_PLUGINS_LOAD_ALL)); 292 } 293 } 294 295 virtual void OnLoadPluginsLinkClicked() { 296 DCHECK(!CommandLine::ForCurrentProcess()->HasSwitch( 297 switches::kDisableClickToPlay)); 298 UserMetrics::RecordAction(UserMetricsAction("ClickToPlay_LoadAll_Bubble")); 299 if (tab_contents()) { 300 tab_contents()->render_view_host()->LoadBlockedPlugins(); 301 } 302 set_load_plugins_link_enabled(false); 303 TabSpecificContentSettings* settings = 304 tab_contents()->GetTabSpecificContentSettings(); 305 settings->set_load_plugins_link_enabled(false); 306 } 307}; 308 309class ContentSettingPopupBubbleModel : public ContentSettingSingleRadioGroup { 310 public: 311 ContentSettingPopupBubbleModel(TabContents* tab_contents, Profile* profile, 312 ContentSettingsType content_type) 313 : ContentSettingSingleRadioGroup(tab_contents, profile, content_type) { 314 SetPopups(); 315 } 316 317 private: 318 void SetPopups() { 319 // check for crbug.com/53176 320 if (!tab_contents()->blocked_popup_container()) 321 return; 322 BlockedPopupContainer::BlockedContents blocked_contents; 323 tab_contents()->blocked_popup_container()->GetBlockedContents( 324 &blocked_contents); 325 for (BlockedPopupContainer::BlockedContents::const_iterator 326 i(blocked_contents.begin()); i != blocked_contents.end(); ++i) { 327 std::string title(UTF16ToUTF8((*i)->GetTitle())); 328 // The popup may not have committed a load yet, in which case it won't 329 // have a URL or title. 330 if (title.empty()) 331 title = l10n_util::GetStringUTF8(IDS_TAB_LOADING_TITLE); 332 PopupItem popup_item; 333 popup_item.title = title; 334 popup_item.bitmap = (*i)->GetFavIcon(); 335 popup_item.tab_contents = (*i); 336 add_popup(popup_item); 337 } 338 } 339 340 virtual void OnPopupClicked(int index) { 341 if (tab_contents() && tab_contents()->blocked_popup_container()) { 342 tab_contents()->blocked_popup_container()->LaunchPopupForContents( 343 bubble_content().popup_items[index].tab_contents); 344 } 345 } 346}; 347 348class ContentSettingDomainListBubbleModel 349 : public ContentSettingTitleAndLinkModel { 350 public: 351 ContentSettingDomainListBubbleModel(TabContents* tab_contents, 352 Profile* profile, 353 ContentSettingsType content_type) 354 : ContentSettingTitleAndLinkModel(tab_contents, profile, content_type) { 355 DCHECK_EQ(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type) << 356 "SetDomains currently only supports geolocation content type"; 357 SetDomainsAndClearLink(); 358 } 359 360 private: 361 void MaybeAddDomainList(const std::set<std::string>& hosts, int title_id) { 362 if (!hosts.empty()) { 363 DomainList domain_list; 364 domain_list.title = l10n_util::GetStringUTF8(title_id); 365 domain_list.hosts = hosts; 366 add_domain_list(domain_list); 367 } 368 } 369 void SetDomainsAndClearLink() { 370 TabSpecificContentSettings* content_settings = 371 tab_contents()->GetTabSpecificContentSettings(); 372 const GeolocationSettingsState& settings = 373 content_settings->geolocation_settings_state(); 374 GeolocationSettingsState::FormattedHostsPerState formatted_hosts_per_state; 375 unsigned int tab_state_flags = 0; 376 settings.GetDetailedInfo(&formatted_hosts_per_state, &tab_state_flags); 377 // Divide the tab's current geolocation users into sets according to their 378 // permission state. 379 MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_ALLOW], 380 IDS_GEOLOCATION_BUBBLE_SECTION_ALLOWED); 381 382 MaybeAddDomainList(formatted_hosts_per_state[CONTENT_SETTING_BLOCK], 383 IDS_GEOLOCATION_BUBBLE_SECTION_DENIED); 384 385 if (tab_state_flags & GeolocationSettingsState::TABSTATE_HAS_EXCEPTION) { 386 set_clear_link( 387 l10n_util::GetStringUTF8(IDS_GEOLOCATION_BUBBLE_CLEAR_LINK)); 388 } else if (tab_state_flags & 389 GeolocationSettingsState::TABSTATE_HAS_CHANGED) { 390 // It is a slight abuse of the domain list field to use it for the reload 391 // hint, but works fine for now. TODO(joth): If we need to style it 392 // differently, consider adding an explicit field, or generalize the 393 // domain list to be a flat list of style formatted lines. 394 DomainList reload_section; 395 reload_section.title = l10n_util::GetStringUTF8( 396 IDS_GEOLOCATION_BUBBLE_REQUIRE_RELOAD_TO_CLEAR); 397 add_domain_list(reload_section); 398 } 399 } 400 virtual void OnClearLinkClicked() { 401 if (!tab_contents()) 402 return; 403 // Reset this embedder's entry to default for each of the requesting 404 // origins currently on the page. 405 const GURL& embedder_url = tab_contents()->GetURL(); 406 TabSpecificContentSettings* content_settings = 407 tab_contents()->GetTabSpecificContentSettings(); 408 const GeolocationSettingsState::StateMap& state_map = 409 content_settings->geolocation_settings_state().state_map(); 410 GeolocationContentSettingsMap* settings_map = 411 profile()->GetGeolocationContentSettingsMap(); 412 for (GeolocationSettingsState::StateMap::const_iterator it = 413 state_map.begin(); it != state_map.end(); ++it) { 414 settings_map->SetContentSetting(it->first, embedder_url, 415 CONTENT_SETTING_DEFAULT); 416 } 417 } 418}; 419 420// static 421ContentSettingBubbleModel* 422 ContentSettingBubbleModel::CreateContentSettingBubbleModel( 423 TabContents* tab_contents, 424 Profile* profile, 425 ContentSettingsType content_type) { 426 if (content_type == CONTENT_SETTINGS_TYPE_COOKIES) { 427 return new ContentSettingTitleLinkAndInfoModel(tab_contents, profile, 428 content_type); 429 } 430 if (content_type == CONTENT_SETTINGS_TYPE_POPUPS) { 431 return new ContentSettingPopupBubbleModel(tab_contents, profile, 432 content_type); 433 } 434 if (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) { 435 return new ContentSettingDomainListBubbleModel(tab_contents, profile, 436 content_type); 437 } 438 if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS) { 439 return new ContentSettingPluginBubbleModel(tab_contents, profile, 440 content_type); 441 } 442 return new ContentSettingSingleRadioGroup(tab_contents, profile, 443 content_type); 444} 445 446ContentSettingBubbleModel::ContentSettingBubbleModel( 447 TabContents* tab_contents, Profile* profile, 448 ContentSettingsType content_type) 449 : tab_contents_(tab_contents), profile_(profile), 450 content_type_(content_type) { 451 if (tab_contents) { 452 TabSpecificContentSettings* settings = 453 tab_contents->GetTabSpecificContentSettings(); 454 set_load_plugins_link_enabled(settings->load_plugins_link_enabled()); 455 } else { 456 set_load_plugins_link_enabled(true); 457 } 458 registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, 459 Source<TabContents>(tab_contents)); 460} 461 462ContentSettingBubbleModel::~ContentSettingBubbleModel() { 463} 464 465void ContentSettingBubbleModel::AddBlockedResource( 466 const std::string& resource_identifier) { 467 bubble_content_.resource_identifiers.insert(resource_identifier); 468} 469 470void ContentSettingBubbleModel::Observe(NotificationType type, 471 const NotificationSource& source, 472 const NotificationDetails& details) { 473 DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); 474 DCHECK(source == Source<TabContents>(tab_contents_)); 475 tab_contents_ = NULL; 476} 477