Home | History | Annotate | Download | only in views
      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/ui/views/content_setting_bubble_contents.h"
      6 
      7 #if defined(OS_LINUX)
      8 #include <gdk/gdk.h>
      9 #endif
     10 
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/blocked_content_container.h"
     13 #include "chrome/browser/content_setting_bubble_model.h"
     14 #include "chrome/browser/content_settings/host_content_settings_map.h"
     15 #include "chrome/browser/plugin_updater.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/ui/views/browser_dialogs.h"
     18 #include "chrome/browser/ui/views/bubble/bubble.h"
     19 #include "content/browser/tab_contents/tab_contents.h"
     20 #include "content/common/notification_source.h"
     21 #include "content/common/notification_type.h"
     22 #include "grit/generated_resources.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "views/controls/button/native_button.h"
     25 #include "views/controls/button/radio_button.h"
     26 #include "views/controls/image_view.h"
     27 #include "views/controls/label.h"
     28 #include "views/controls/separator.h"
     29 #include "views/layout/grid_layout.h"
     30 #include "views/layout/layout_constants.h"
     31 #include "webkit/glue/plugins/plugin_list.h"
     32 
     33 #if defined(OS_LINUX)
     34 #include "ui/gfx/gtk_util.h"
     35 #endif
     36 
     37 // If we don't clamp the maximum width, then very long URLs and titles can make
     38 // the bubble arbitrarily wide.
     39 const int kMaxContentsWidth = 500;
     40 
     41 // When we have multiline labels, we should set a minimum width lest we get very
     42 // narrow bubbles with lots of line-wrapping.
     43 const int kMinMultiLineContentsWidth = 250;
     44 
     45 class ContentSettingBubbleContents::Favicon : public views::ImageView {
     46  public:
     47   Favicon(const SkBitmap& image,
     48           ContentSettingBubbleContents* parent,
     49           views::Link* link);
     50   virtual ~Favicon();
     51 
     52  private:
     53 #if defined(OS_WIN)
     54   static HCURSOR g_hand_cursor;
     55 #endif
     56 
     57   // views::View overrides:
     58   virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
     59   virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
     60   virtual gfx::NativeCursor GetCursorForPoint(ui::EventType event_type,
     61                                               const gfx::Point& p) OVERRIDE;
     62 
     63   ContentSettingBubbleContents* parent_;
     64   views::Link* link_;
     65 };
     66 
     67 #if defined(OS_WIN)
     68 HCURSOR ContentSettingBubbleContents::Favicon::g_hand_cursor = NULL;
     69 #endif
     70 
     71 ContentSettingBubbleContents::Favicon::Favicon(
     72     const SkBitmap& image,
     73     ContentSettingBubbleContents* parent,
     74     views::Link* link)
     75     : parent_(parent),
     76       link_(link) {
     77   SetImage(image);
     78 }
     79 
     80 ContentSettingBubbleContents::Favicon::~Favicon() {
     81 }
     82 
     83 bool ContentSettingBubbleContents::Favicon::OnMousePressed(
     84     const views::MouseEvent& event) {
     85   return event.IsLeftMouseButton() || event.IsMiddleMouseButton();
     86 }
     87 
     88 void ContentSettingBubbleContents::Favicon::OnMouseReleased(
     89     const views::MouseEvent& event) {
     90   if ((event.IsLeftMouseButton() || event.IsMiddleMouseButton()) &&
     91      HitTest(event.location())) {
     92     parent_->LinkActivated(link_, event.flags());
     93   }
     94 }
     95 
     96 gfx::NativeCursor ContentSettingBubbleContents::Favicon::GetCursorForPoint(
     97     ui::EventType event_type,
     98     const gfx::Point& p) {
     99 #if defined(OS_WIN)
    100   if (!g_hand_cursor)
    101     g_hand_cursor = LoadCursor(NULL, IDC_HAND);
    102   return g_hand_cursor;
    103 #elif defined(OS_LINUX)
    104   return gfx::GetCursor(GDK_HAND2);
    105 #endif
    106 }
    107 
    108 ContentSettingBubbleContents::ContentSettingBubbleContents(
    109     ContentSettingBubbleModel* content_setting_bubble_model,
    110     Profile* profile,
    111     TabContents* tab_contents)
    112     : content_setting_bubble_model_(content_setting_bubble_model),
    113       profile_(profile),
    114       tab_contents_(tab_contents),
    115       bubble_(NULL),
    116       custom_link_(NULL),
    117       manage_link_(NULL),
    118       close_button_(NULL) {
    119   registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
    120                  Source<TabContents>(tab_contents));
    121 }
    122 
    123 ContentSettingBubbleContents::~ContentSettingBubbleContents() {
    124 }
    125 
    126 gfx::Size ContentSettingBubbleContents::GetPreferredSize() {
    127   gfx::Size preferred_size(views::View::GetPreferredSize());
    128   int preferred_width =
    129       (!content_setting_bubble_model_->bubble_content().domain_lists.empty() &&
    130        (kMinMultiLineContentsWidth > preferred_size.width())) ?
    131       kMinMultiLineContentsWidth : preferred_size.width();
    132   preferred_size.set_width(std::min(preferred_width, kMaxContentsWidth));
    133   return preferred_size;
    134 }
    135 
    136 void ContentSettingBubbleContents::ViewHierarchyChanged(bool is_add,
    137                                                         View* parent,
    138                                                         View* child) {
    139   if (is_add && (child == this))
    140     InitControlLayout();
    141 }
    142 
    143 void ContentSettingBubbleContents::ButtonPressed(views::Button* sender,
    144                                                  const views::Event& event) {
    145   if (sender == close_button_) {
    146     bubble_->set_fade_away_on_close(true);
    147     bubble_->Close();  // CAREFUL: This deletes us.
    148     return;
    149   }
    150 
    151   for (RadioGroup::const_iterator i = radio_group_.begin();
    152        i != radio_group_.end(); ++i) {
    153     if (sender == *i) {
    154       content_setting_bubble_model_->OnRadioClicked(i - radio_group_.begin());
    155       return;
    156     }
    157   }
    158   NOTREACHED() << "unknown radio";
    159 }
    160 
    161 void ContentSettingBubbleContents::LinkActivated(views::Link* source,
    162                                                  int event_flags) {
    163   if (source == custom_link_) {
    164     content_setting_bubble_model_->OnCustomLinkClicked();
    165     bubble_->set_fade_away_on_close(true);
    166     bubble_->Close();  // CAREFUL: This deletes us.
    167     return;
    168   }
    169   if (source == manage_link_) {
    170     bubble_->set_fade_away_on_close(true);
    171     content_setting_bubble_model_->OnManageLinkClicked();
    172     // CAREFUL: Showing the settings window activates it, which deactivates the
    173     // info bubble, which causes it to close, which deletes us.
    174     return;
    175   }
    176 
    177   PopupLinks::const_iterator i(popup_links_.find(source));
    178   DCHECK(i != popup_links_.end());
    179   content_setting_bubble_model_->OnPopupClicked(i->second);
    180 }
    181 
    182 void ContentSettingBubbleContents::Observe(NotificationType type,
    183                                            const NotificationSource& source,
    184                                            const NotificationDetails& details) {
    185   DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
    186   DCHECK(source == Source<TabContents>(tab_contents_));
    187   tab_contents_ = NULL;
    188 }
    189 
    190 void ContentSettingBubbleContents::InitControlLayout() {
    191   using views::GridLayout;
    192 
    193   GridLayout* layout = new views::GridLayout(this);
    194   SetLayoutManager(layout);
    195 
    196   const int single_column_set_id = 0;
    197   views::ColumnSet* column_set = layout->AddColumnSet(single_column_set_id);
    198   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
    199                         GridLayout::USE_PREF, 0, 0);
    200 
    201   const ContentSettingBubbleModel::BubbleContent& bubble_content =
    202       content_setting_bubble_model_->bubble_content();
    203   bool bubble_content_empty = true;
    204 
    205   if (!bubble_content.title.empty()) {
    206     views::Label* title_label = new views::Label(UTF8ToWide(
    207         bubble_content.title));
    208     layout->StartRow(0, single_column_set_id);
    209     layout->AddView(title_label);
    210     bubble_content_empty = false;
    211   }
    212 
    213   const std::set<std::string>& plugins = bubble_content.resource_identifiers;
    214   if (!plugins.empty()) {
    215     if (!bubble_content_empty)
    216       layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    217     for (std::set<std::string>::const_iterator it = plugins.begin();
    218         it != plugins.end(); ++it) {
    219       std::wstring name = UTF16ToWide(
    220           NPAPI::PluginList::Singleton()->GetPluginGroupName(*it));
    221       if (name.empty())
    222         name = UTF8ToWide(*it);
    223       layout->StartRow(0, single_column_set_id);
    224       layout->AddView(new views::Label(name));
    225       bubble_content_empty = false;
    226     }
    227   }
    228 
    229   if (content_setting_bubble_model_->content_type() ==
    230       CONTENT_SETTINGS_TYPE_POPUPS) {
    231     const int popup_column_set_id = 2;
    232     views::ColumnSet* popup_column_set =
    233         layout->AddColumnSet(popup_column_set_id);
    234     popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
    235                                 GridLayout::USE_PREF, 0, 0);
    236     popup_column_set->AddPaddingColumn(
    237         0, views::kRelatedControlHorizontalSpacing);
    238     popup_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1,
    239                                 GridLayout::USE_PREF, 0, 0);
    240 
    241     for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
    242          i(bubble_content.popup_items.begin());
    243          i != bubble_content.popup_items.end(); ++i) {
    244       if (!bubble_content_empty)
    245         layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    246       layout->StartRow(0, popup_column_set_id);
    247 
    248       views::Link* link = new views::Link(UTF8ToWide(i->title));
    249       link->SetController(this);
    250       link->SetElideInMiddle(true);
    251       popup_links_[link] = i - bubble_content.popup_items.begin();
    252       layout->AddView(new Favicon((*i).bitmap, this, link));
    253       layout->AddView(link);
    254       bubble_content_empty = false;
    255     }
    256   }
    257 
    258   const ContentSettingBubbleModel::RadioGroup& radio_group =
    259       bubble_content.radio_group;
    260   if (!radio_group.radio_items.empty()) {
    261     for (ContentSettingBubbleModel::RadioItems::const_iterator i =
    262          radio_group.radio_items.begin();
    263          i != radio_group.radio_items.end(); ++i) {
    264       views::RadioButton* radio = new views::RadioButton(UTF8ToWide(*i), 0);
    265       radio->set_listener(this);
    266       radio_group_.push_back(radio);
    267       if (!bubble_content_empty)
    268         layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    269       layout->StartRow(0, single_column_set_id);
    270       layout->AddView(radio);
    271       bubble_content_empty = false;
    272     }
    273     DCHECK(!radio_group_.empty());
    274     // Now that the buttons have been added to the view hierarchy, it's safe
    275     // to call SetChecked() on them.
    276     radio_group_[radio_group.default_item]->SetChecked(true);
    277   }
    278 
    279   gfx::Font domain_font =
    280       views::Label().font().DeriveFont(0, gfx::Font::BOLD);
    281   const int indented_single_column_set_id = 3;
    282   // Insert a column set to indent the domain list.
    283   views::ColumnSet* indented_single_column_set =
    284       layout->AddColumnSet(indented_single_column_set_id);
    285   indented_single_column_set->AddPaddingColumn(
    286       0, views::kPanelHorizIndentation);
    287   indented_single_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
    288                                         1, GridLayout::USE_PREF, 0, 0);
    289   for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i =
    290        bubble_content.domain_lists.begin();
    291        i != bubble_content.domain_lists.end(); ++i) {
    292     layout->StartRow(0, single_column_set_id);
    293     views::Label* section_title = new views::Label(UTF8ToWide(i->title));
    294     section_title->SetMultiLine(true);
    295     section_title->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
    296     layout->AddView(section_title, 1, 1, GridLayout::FILL, GridLayout::LEADING);
    297     for (std::set<std::string>::const_iterator j = i->hosts.begin();
    298          j != i->hosts.end(); ++j) {
    299       layout->StartRow(0, indented_single_column_set_id);
    300       layout->AddView(new views::Label(UTF8ToWide(*j), domain_font));
    301     }
    302     bubble_content_empty = false;
    303   }
    304 
    305   if (!bubble_content.custom_link.empty()) {
    306     custom_link_ = new views::Link(UTF8ToWide(bubble_content.custom_link));
    307     custom_link_->SetEnabled(bubble_content.custom_link_enabled);
    308     custom_link_->SetController(this);
    309     if (!bubble_content_empty)
    310       layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    311     layout->StartRow(0, single_column_set_id);
    312     layout->AddView(custom_link_);
    313     bubble_content_empty = false;
    314   }
    315 
    316   if (!bubble_content_empty) {
    317     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    318     layout->StartRow(0, single_column_set_id);
    319     layout->AddView(new views::Separator, 1, 1,
    320                     GridLayout::FILL, GridLayout::FILL);
    321     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    322   }
    323 
    324   const int double_column_set_id = 1;
    325   views::ColumnSet* double_column_set =
    326       layout->AddColumnSet(double_column_set_id);
    327   double_column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 1,
    328                         GridLayout::USE_PREF, 0, 0);
    329   double_column_set->AddPaddingColumn(
    330       0, views::kUnrelatedControlHorizontalSpacing);
    331   double_column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
    332                         GridLayout::USE_PREF, 0, 0);
    333 
    334   layout->StartRow(0, double_column_set_id);
    335   manage_link_ = new views::Link(UTF8ToWide(bubble_content.manage_link));
    336   manage_link_->SetController(this);
    337   layout->AddView(manage_link_);
    338 
    339   close_button_ =
    340       new views::NativeButton(this,
    341                               UTF16ToWide(l10n_util::GetStringUTF16(IDS_DONE)));
    342   layout->AddView(close_button_);
    343 }
    344