Home | History | Annotate | Download | only in browser
      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