• 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/page_info_bubble_view.h"
6 
7 #include "base/utf_string_conversions.h"
8 #include "chrome/browser/google/google_util.h"
9 #include "chrome/browser/ui/browser_list.h"
10 #include "chrome/browser/ui/views/frame/browser_view.h"
11 #include "chrome/browser/ui/views/bubble/bubble.h"
12 #include "chrome/browser/ui/views/toolbar_view.h"
13 #include "chrome/common/url_constants.h"
14 #include "content/browser/cert_store.h"
15 #include "content/browser/certificate_viewer.h"
16 #include "grit/generated_resources.h"
17 #include "grit/locale_settings.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/gfx/image.h"
20 #include "views/controls/image_view.h"
21 #include "views/controls/label.h"
22 #include "views/controls/separator.h"
23 #include "views/layout/grid_layout.h"
24 #include "views/widget/widget.h"
25 #include "views/window/window.h"
26 
27 namespace {
28 
29 // Layout constants.
30 const int kHGapToBorder = 11;
31 const int kVGapToImage = 10;
32 const int kVGapToHeadline = 7;
33 const int kHGapImageToDescription = 6;
34 const int kTextPaddingRight = 10;
35 const int kPaddingBelowSeparator = 4;
36 const int kPaddingAboveSeparator = 13;
37 const int kIconHorizontalOffset = 27;
38 const int kIconVerticalOffset = -7;
39 
40 // The duration of the animation that resizes the bubble once the async
41 // information is provided through the ModelChanged event.
42 const int kPageInfoSlideDuration = 300;
43 
44 // A section contains an image that shows a status (good or bad), a title, an
45 // optional head-line (in bold) and a description.
46 class Section : public views::View,
47                 public views::LinkController {
48  public:
49   Section(PageInfoBubbleView* owner,
50           const PageInfoModel::SectionInfo& section_info,
51           const SkBitmap* status_icon,
52           bool show_cert);
53   virtual ~Section();
54 
55   // views::View methods:
56   virtual int GetHeightForWidth(int w);
57   virtual void Layout();
58 
59   // views::LinkController methods:
60   virtual void LinkActivated(views::Link* source, int event_flags);
61 
62  private:
63   // Calculate the layout if |compute_bounds_only|, otherwise does Layout also.
64   gfx::Size LayoutItems(bool compute_bounds_only, int width);
65 
66   // The view that owns this Section object.
67   PageInfoBubbleView* owner_;
68 
69   // The information this view represents.
70   PageInfoModel::SectionInfo info_;
71 
72   views::ImageView* status_image_;
73   views::Label* headline_label_;
74   views::Label* description_label_;
75   views::Link* link_;
76 
77   DISALLOW_COPY_AND_ASSIGN(Section);
78 };
79 
80 }  // namespace
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 // PageInfoBubbleView
84 
85 Bubble* PageInfoBubbleView::bubble_ = NULL;
86 
PageInfoBubbleView(gfx::NativeWindow parent_window,Profile * profile,const GURL & url,const NavigationEntry::SSLStatus & ssl,bool show_history)87 PageInfoBubbleView::PageInfoBubbleView(gfx::NativeWindow parent_window,
88                                        Profile* profile,
89                                        const GURL& url,
90                                        const NavigationEntry::SSLStatus& ssl,
91                                        bool show_history)
92     : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl,
93                                             show_history, this)),
94       parent_window_(parent_window),
95       cert_id_(ssl.cert_id()),
96       help_center_link_(NULL),
97       ALLOW_THIS_IN_INITIALIZER_LIST(resize_animation_(this)),
98       animation_start_height_(0) {
99   if (bubble_)
100     bubble_->Close();
101   if (cert_id_ > 0) {
102     scoped_refptr<net::X509Certificate> cert;
103     CertStore::GetInstance()->RetrieveCert(cert_id_, &cert);
104     // When running with fake certificate (Chrome Frame), we have no os
105     // certificate, so there is no cert to show. Don't bother showing the cert
106     // info link in that case.
107     if (!cert.get() || !cert->os_cert_handle())
108       cert_id_ = 0;
109   }
110   LayoutSections();
111 }
112 
~PageInfoBubbleView()113 PageInfoBubbleView::~PageInfoBubbleView() {
114 }
115 
ShowCertDialog()116 void PageInfoBubbleView::ShowCertDialog() {
117   ShowCertificateViewerByID(parent_window_, cert_id_);
118 }
119 
LayoutSections()120 void PageInfoBubbleView::LayoutSections() {
121   // Remove all the existing sections.
122   RemoveAllChildViews(true);
123 
124   views::GridLayout* layout = new views::GridLayout(this);
125   SetLayoutManager(layout);
126   views::ColumnSet* columns = layout->AddColumnSet(0);
127   columns->AddColumn(views::GridLayout::FILL,  // Horizontal resize.
128                      views::GridLayout::FILL,  // Vertical resize.
129                      1,   // Resize weight.
130                      views::GridLayout::USE_PREF,  // Size type.
131                      0,   // Ignored for USE_PREF.
132                      0);  // Minimum size.
133   // Add a column set for aligning the text when it has no icons (such as the
134   // help center link).
135   columns = layout->AddColumnSet(1);
136   columns->AddPaddingColumn(
137       0, kHGapToBorder + kIconHorizontalOffset + kHGapImageToDescription);
138   columns->AddColumn(views::GridLayout::LEADING,  // Horizontal resize.
139                      views::GridLayout::FILL,     // Vertical resize.
140                      1,   // Resize weight.
141                      views::GridLayout::USE_PREF,  // Size type.
142                      0,   // Ignored for USE_PREF.
143                      0);  // Minimum size.
144 
145   int count = model_.GetSectionCount();
146   for (int i = 0; i < count; ++i) {
147     PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
148     layout->StartRow(0, 0);
149     const SkBitmap* icon = *model_.GetIconImage(info.icon_id);
150     layout->AddView(new Section(this, info, icon, cert_id_ > 0));
151 
152     // Add separator after all sections.
153     layout->AddPaddingRow(0, kPaddingAboveSeparator);
154     layout->StartRow(0, 0);
155     layout->AddView(new views::Separator());
156     layout->AddPaddingRow(0, kPaddingBelowSeparator);
157   }
158 
159   // Then add the help center link at the bottom.
160   layout->StartRow(0, 1);
161   help_center_link_ = new views::Link(
162       UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGE_INFO_HELP_CENTER_LINK)));
163   help_center_link_->SetController(this);
164   layout->AddView(help_center_link_);
165 }
166 
GetPreferredSize()167 gfx::Size PageInfoBubbleView::GetPreferredSize() {
168   gfx::Size size(views::Window::GetLocalizedContentsSize(
169       IDS_PAGEINFOBUBBLE_WIDTH_CHARS, IDS_PAGEINFOBUBBLE_HEIGHT_LINES));
170   size.set_height(0);
171 
172   int count = model_.GetSectionCount();
173   for (int i = 0; i < count; ++i) {
174     PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
175     const SkBitmap* icon = *model_.GetIconImage(info.icon_id);
176     Section section(this, info, icon, cert_id_ > 0);
177     size.Enlarge(0, section.GetHeightForWidth(size.width()));
178   }
179 
180   // Calculate how much space the separators take up (with padding).
181   views::Separator separator;
182   gfx::Size separator_size = separator.GetPreferredSize();
183   gfx::Size separator_plus_padding(0, separator_size.height() +
184                                       kPaddingAboveSeparator +
185                                       kPaddingBelowSeparator);
186 
187   // Account for the separators and padding within sections.
188   size.Enlarge(0, (count - 1) * separator_plus_padding.height());
189 
190   // Account for the Help Center link and the separator above it.
191   gfx::Size link_size = help_center_link_->GetPreferredSize();
192   size.Enlarge(0, separator_plus_padding.height() +
193                   link_size.height());
194 
195   if (!resize_animation_.is_animating())
196     return size;
197 
198   // We are animating from animation_start_height_ to size.
199   int target_height = animation_start_height_ + static_cast<int>(
200       (size.height() - animation_start_height_) *
201       resize_animation_.GetCurrentValue());
202   size.set_height(target_height);
203   return size;
204 }
205 
ModelChanged()206 void PageInfoBubbleView::ModelChanged() {
207   animation_start_height_ = bounds().height();
208   LayoutSections();
209   resize_animation_.SetSlideDuration(kPageInfoSlideDuration);
210   resize_animation_.Show();
211 }
212 
BubbleClosing(Bubble * bubble,bool closed_by_escape)213 void PageInfoBubbleView::BubbleClosing(Bubble* bubble, bool closed_by_escape) {
214   resize_animation_.Reset();
215   bubble_ = NULL;
216 }
217 
CloseOnEscape()218 bool PageInfoBubbleView::CloseOnEscape() {
219   return true;
220 }
221 
FadeInOnShow()222 bool PageInfoBubbleView::FadeInOnShow() {
223   return false;
224 }
225 
accessible_name()226 std::wstring PageInfoBubbleView::accessible_name() {
227   return L"PageInfoBubble";
228 }
229 
LinkActivated(views::Link * source,int event_flags)230 void PageInfoBubbleView::LinkActivated(views::Link* source, int event_flags) {
231   // We want to make sure the info bubble closes once the link is activated.  So
232   // we close it explicitly rather than relying on a side-effect of opening a
233   // new tab (see http://crosbug.com/10186).
234   bubble_->Close();
235 
236   GURL url = google_util::AppendGoogleLocaleParam(
237       GURL(chrome::kPageInfoHelpCenterURL));
238   Browser* browser = BrowserList::GetLastActive();
239   browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
240 }
241 
AnimationEnded(const ui::Animation * animation)242 void PageInfoBubbleView::AnimationEnded(const ui::Animation* animation) {
243   bubble_->SizeToContents();
244 }
245 
AnimationProgressed(const ui::Animation * animation)246 void PageInfoBubbleView::AnimationProgressed(const ui::Animation* animation) {
247   bubble_->SizeToContents();
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 // Section
252 
Section(PageInfoBubbleView * owner,const PageInfoModel::SectionInfo & section_info,const SkBitmap * state_icon,bool show_cert)253 Section::Section(PageInfoBubbleView* owner,
254                  const PageInfoModel::SectionInfo& section_info,
255                  const SkBitmap* state_icon,
256                  bool show_cert)
257     : owner_(owner),
258       info_(section_info),
259       status_image_(NULL),
260       link_(NULL) {
261   if (state_icon) {
262     status_image_ = new views::ImageView();
263     status_image_->SetImage(*state_icon);
264     AddChildView(status_image_);
265   }
266 
267   headline_label_ = new views::Label(UTF16ToWideHack(info_.headline));
268   headline_label_->SetFont(
269       headline_label_->font().DeriveFont(0, gfx::Font::BOLD));
270   headline_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
271   AddChildView(headline_label_);
272 
273   description_label_ = new views::Label(UTF16ToWideHack(info_.description));
274   description_label_->SetMultiLine(true);
275   description_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
276   // Allow linebreaking in the middle of words if necessary, so that extremely
277   // long hostnames (longer than one line) will still be completely shown.
278   description_label_->SetAllowCharacterBreak(true);
279   AddChildView(description_label_);
280 
281   if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && show_cert) {
282     link_ = new views::Link(
283         UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)));
284     link_->SetController(this);
285     AddChildView(link_);
286   }
287 }
288 
~Section()289 Section::~Section() {
290 }
291 
GetHeightForWidth(int width)292 int Section::GetHeightForWidth(int width) {
293   return LayoutItems(true, width).height();
294 }
295 
Layout()296 void Section::Layout() {
297   LayoutItems(false, width());
298 }
299 
LinkActivated(views::Link * source,int event_flags)300 void Section::LinkActivated(views::Link* source, int event_flags) {
301   owner_->ShowCertDialog();
302 }
303 
LayoutItems(bool compute_bounds_only,int width)304 gfx::Size Section::LayoutItems(bool compute_bounds_only, int width) {
305   int x = kHGapToBorder;
306   int y = kVGapToImage;
307 
308   // Layout the image, head-line and description.
309   gfx::Size size;
310   if (status_image_) {
311       size = status_image_->GetPreferredSize();
312     if (!compute_bounds_only)
313       status_image_->SetBounds(x, y, size.width(), size.height());
314   }
315   int image_height = y + size.height();
316   x += size.width() + kHGapImageToDescription;
317   int w = width - x - kTextPaddingRight;
318   y = kVGapToHeadline;
319   if (!headline_label_->GetText().empty()) {
320     size = headline_label_->GetPreferredSize();
321     if (!compute_bounds_only)
322       headline_label_->SetBounds(x, y, w > 0 ? w : 0, size.height());
323     y += size.height();
324   } else {
325     if (!compute_bounds_only)
326       headline_label_->SetBounds(x, y, 0, 0);
327   }
328   if (w > 0) {
329     int height = description_label_->GetHeightForWidth(w);
330     if (!compute_bounds_only)
331       description_label_->SetBounds(x, y, w, height);
332     y += height;
333   } else {
334     if (!compute_bounds_only)
335       description_label_->SetBounds(x, y, 0, 0);
336   }
337   if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && link_) {
338     size = link_->GetPreferredSize();
339     if (!compute_bounds_only)
340       link_->SetBounds(x, y, size.width(), size.height());
341     y += size.height();
342   }
343 
344   // Make sure the image is not truncated if the text doesn't contain much.
345   y = std::max(y, image_height);
346   return gfx::Size(width, y);
347 }
348 
349 namespace browser {
350 
ShowPageInfoBubble(gfx::NativeWindow parent,Profile * profile,const GURL & url,const NavigationEntry::SSLStatus & ssl,bool show_history)351 void ShowPageInfoBubble(gfx::NativeWindow parent,
352                         Profile* profile,
353                         const GURL& url,
354                         const NavigationEntry::SSLStatus& ssl,
355                         bool show_history) {
356   // Find where to point the bubble at.
357   BrowserView* browser_view =
358       BrowserView::GetBrowserViewForNativeWindow(parent);
359   gfx::Point point;
360   if (base::i18n::IsRTL()) {
361     int width = browser_view->toolbar()->location_bar()->width();
362     point = gfx::Point(width - kIconHorizontalOffset, 0);
363   }
364   point.Offset(0, kIconVerticalOffset);
365   views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(),
366                                     &point);
367   gfx::Rect bounds = browser_view->toolbar()->location_bar()->bounds();
368   bounds.set_origin(point);
369   bounds.set_width(kIconHorizontalOffset);
370 
371   // Show the bubble. If the bubble already exist - it will be closed first.
372   PageInfoBubbleView* page_info_bubble =
373       new PageInfoBubbleView(parent, profile, url, ssl, show_history);
374   Bubble* bubble =
375       Bubble::Show(browser_view->GetWidget(), bounds,
376                    BubbleBorder::TOP_LEFT,
377                    page_info_bubble, page_info_bubble);
378   page_info_bubble->set_bubble(bubble);
379 }
380 
381 }
382