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