• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Favicon(const SkBitmap & image,ContentSettingBubbleContents * parent,views::Link * link)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 
~Favicon()80 ContentSettingBubbleContents::Favicon::~Favicon() {
81 }
82 
OnMousePressed(const views::MouseEvent & event)83 bool ContentSettingBubbleContents::Favicon::OnMousePressed(
84     const views::MouseEvent& event) {
85   return event.IsLeftMouseButton() || event.IsMiddleMouseButton();
86 }
87 
OnMouseReleased(const views::MouseEvent & event)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 
GetCursorForPoint(ui::EventType event_type,const gfx::Point & p)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 
ContentSettingBubbleContents(ContentSettingBubbleModel * content_setting_bubble_model,Profile * profile,TabContents * tab_contents)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 
~ContentSettingBubbleContents()123 ContentSettingBubbleContents::~ContentSettingBubbleContents() {
124 }
125 
GetPreferredSize()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 
ViewHierarchyChanged(bool is_add,View * parent,View * child)136 void ContentSettingBubbleContents::ViewHierarchyChanged(bool is_add,
137                                                         View* parent,
138                                                         View* child) {
139   if (is_add && (child == this))
140     InitControlLayout();
141 }
142 
ButtonPressed(views::Button * sender,const views::Event & event)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 
LinkActivated(views::Link * source,int event_flags)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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
InitControlLayout()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