• 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/frame/browser_view_layout.h"
6 
7 #include "chrome/browser/sidebar/sidebar_manager.h"
8 #include "chrome/browser/ui/find_bar/find_bar.h"
9 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
10 #include "chrome/browser/ui/view_ids.h"
11 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
12 #include "chrome/browser/ui/views/download/download_shelf_view.h"
13 #include "chrome/browser/ui/views/frame/browser_frame.h"
14 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/frame/contents_container.h"
16 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
17 #include "chrome/browser/ui/views/tab_contents/tab_contents_container.h"
18 #include "chrome/browser/ui/views/tabs/abstract_tab_strip_view.h"
19 #include "chrome/browser/ui/views/toolbar_view.h"
20 #include "ui/gfx/point.h"
21 #include "ui/gfx/scrollbar_size.h"
22 #include "ui/gfx/size.h"
23 #include "views/controls/single_split_view.h"
24 #include "views/window/window.h"
25 
26 #if defined(OS_LINUX)
27 #include "views/window/hit_test.h"
28 #endif
29 
30 namespace {
31 
32 // The visible height of the shadow above the tabs. Clicks in this area are
33 // treated as clicks to the frame, rather than clicks to the tab.
34 const int kTabShadowSize = 2;
35 // The vertical overlap between the TabStrip and the Toolbar.
36 const int kToolbarTabStripVerticalOverlap = 3;
37 
38 }  // namespace
39 
40 ////////////////////////////////////////////////////////////////////////////////
41 // BrowserViewLayout, public:
42 
BrowserViewLayout()43 BrowserViewLayout::BrowserViewLayout()
44     : tabstrip_(NULL),
45       toolbar_(NULL),
46       contents_split_(NULL),
47       contents_container_(NULL),
48       infobar_container_(NULL),
49       download_shelf_(NULL),
50       active_bookmark_bar_(NULL),
51       browser_view_(NULL),
52       find_bar_y_(0) {
53 }
54 
~BrowserViewLayout()55 BrowserViewLayout::~BrowserViewLayout() {
56 }
57 
GetMinimumSize()58 gfx::Size BrowserViewLayout::GetMinimumSize() {
59   // TODO(noname): In theory the tabstrip width should probably be
60   // (OTR + tabstrip + caption buttons) width.
61   gfx::Size tabstrip_size(
62       browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
63       tabstrip_->GetMinimumSize() : gfx::Size());
64   gfx::Size toolbar_size(
65       (browser()->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
66        browser()->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) ?
67       toolbar_->GetMinimumSize() : gfx::Size());
68   if (tabstrip_size.height() && toolbar_size.height())
69     toolbar_size.Enlarge(0, -kToolbarTabStripVerticalOverlap);
70   gfx::Size bookmark_bar_size;
71   if (active_bookmark_bar_ &&
72       browser()->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR)) {
73     bookmark_bar_size = active_bookmark_bar_->GetMinimumSize();
74     bookmark_bar_size.Enlarge(0,
75         -(views::NonClientFrameView::kClientEdgeThickness +
76             active_bookmark_bar_->GetToolbarOverlap(true)));
77   }
78   gfx::Size contents_size(contents_split_->GetMinimumSize());
79 
80   int min_height = tabstrip_size.height() + toolbar_size.height() +
81       bookmark_bar_size.height() + contents_size.height();
82   int widths[] = { tabstrip_size.width(), toolbar_size.width(),
83                    bookmark_bar_size.width(), contents_size.width() };
84   int min_width = *std::max_element(&widths[0], &widths[arraysize(widths)]);
85   return gfx::Size(min_width, min_height);
86 }
87 
GetFindBarBoundingBox() const88 gfx::Rect BrowserViewLayout::GetFindBarBoundingBox() const {
89   // This function returns the area the Find Bar can be laid out
90   // within. This basically implies the "user-perceived content
91   // area" of the browser window excluding the vertical
92   // scrollbar. This is not quite so straightforward as positioning
93   // based on the TabContentsContainer since the BookmarkBarView may
94   // be visible but not persistent (in the New Tab case) and we
95   // position the Find Bar over the top of it in that case since the
96   // BookmarkBarView is not _visually_ connected to the Toolbar.
97 
98   // First determine the bounding box of the content area in Widget
99   // coordinates.
100   gfx::Rect bounding_box(contents_container_->bounds());
101 
102   gfx::Point topleft;
103   views::View::ConvertPointToWidget(contents_container_, &topleft);
104   bounding_box.set_origin(topleft);
105 
106   // Adjust the position and size of the bounding box by the find bar offset
107   // calculated during the last Layout.
108   int height_delta = find_bar_y_ - bounding_box.y();
109   bounding_box.set_y(find_bar_y_);
110   bounding_box.set_height(std::max(0, bounding_box.height() + height_delta));
111 
112   // Finally decrease the width of the bounding box by the width of
113   // the vertical scroll bar.
114   int scrollbar_width = gfx::scrollbar_size();
115   bounding_box.set_width(std::max(0, bounding_box.width() - scrollbar_width));
116   if (base::i18n::IsRTL())
117     bounding_box.set_x(bounding_box.x() + scrollbar_width);
118 
119   return bounding_box;
120 }
121 
IsPositionInWindowCaption(const gfx::Point & point)122 bool BrowserViewLayout::IsPositionInWindowCaption(
123     const gfx::Point& point) {
124   gfx::Point tabstrip_point(point);
125   views::View::ConvertPointToView(browser_view_, tabstrip_, &tabstrip_point);
126   return tabstrip_->IsPositionInWindowCaption(tabstrip_point);
127 }
128 
NonClientHitTest(const gfx::Point & point)129 int BrowserViewLayout::NonClientHitTest(
130     const gfx::Point& point) {
131   // Since the TabStrip only renders in some parts of the top of the window,
132   // the un-obscured area is considered to be part of the non-client caption
133   // area of the window. So we need to treat hit-tests in these regions as
134   // hit-tests of the titlebar.
135 
136   views::View* parent = browser_view_->parent();
137 
138   gfx::Point point_in_browser_view_coords(point);
139   views::View::ConvertPointToView(
140       parent, browser_view_, &point_in_browser_view_coords);
141 
142   // Determine if the TabStrip exists and is capable of being clicked on. We
143   // might be a popup window without a TabStrip.
144   if (browser_view_->IsTabStripVisible()) {
145     // See if the mouse pointer is within the bounds of the TabStrip.
146     gfx::Point point_in_tabstrip_coords(point);
147     views::View::ConvertPointToView(parent, tabstrip_,
148                                     &point_in_tabstrip_coords);
149     if (tabstrip_->HitTest(point_in_tabstrip_coords)) {
150       if (tabstrip_->IsPositionInWindowCaption(point_in_tabstrip_coords))
151         return HTCAPTION;
152       return HTCLIENT;
153     }
154 
155     // The top few pixels of the TabStrip are a drop-shadow - as we're pretty
156     // starved of dragable area, let's give it to window dragging (this also
157     // makes sense visually).
158     if (!browser_view_->IsMaximized() &&
159         (point_in_browser_view_coords.y() <
160          (tabstrip_->y() + kTabShadowSize))) {
161       // We return HTNOWHERE as this is a signal to our containing
162       // NonClientView that it should figure out what the correct hit-test
163       // code is given the mouse position...
164       return HTNOWHERE;
165     }
166   }
167 
168   // If the point's y coordinate is below the top of the toolbar and otherwise
169   // within the bounds of this view, the point is considered to be within the
170   // client area.
171   gfx::Rect bv_bounds = browser_view_->bounds();
172   bv_bounds.Offset(0, toolbar_->y());
173   bv_bounds.set_height(bv_bounds.height() - toolbar_->y());
174   if (bv_bounds.Contains(point))
175     return HTCLIENT;
176 
177   // If the point's y coordinate is above the top of the toolbar, but not in
178   // the tabstrip (per previous checking in this function), then we consider it
179   // in the window caption (e.g. the area to the right of the tabstrip
180   // underneath the window controls). However, note that we DO NOT return
181   // HTCAPTION here, because when the window is maximized the window controls
182   // will fall into this space (since the BrowserView is sized to entire size
183   // of the window at that point), and the HTCAPTION value will cause the
184   // window controls not to work. So we return HTNOWHERE so that the caller
185   // will hit-test the window controls before finally falling back to
186   // HTCAPTION.
187   bv_bounds = browser_view_->bounds();
188   bv_bounds.set_height(toolbar_->y());
189   if (bv_bounds.Contains(point))
190     return HTNOWHERE;
191 
192   // If the point is somewhere else, delegate to the default implementation.
193   return browser_view_->views::ClientView::NonClientHitTest(point);
194 }
195 
196 //////////////////////////////////////////////////////////////////////////////
197 // BrowserViewLayout, views::LayoutManager implementation:
198 
Installed(views::View * host)199 void BrowserViewLayout::Installed(views::View* host) {
200   toolbar_ = NULL;
201   contents_split_ = NULL;
202   contents_container_ = NULL;
203   infobar_container_ = NULL;
204   download_shelf_ = NULL;
205   active_bookmark_bar_ = NULL;
206   tabstrip_ = NULL;
207   browser_view_ = static_cast<BrowserView*>(host);
208 }
209 
Uninstalled(views::View * host)210 void BrowserViewLayout::Uninstalled(views::View* host) {}
211 
ViewAdded(views::View * host,views::View * view)212 void BrowserViewLayout::ViewAdded(views::View* host, views::View* view) {
213   switch (view->GetID()) {
214     case VIEW_ID_CONTENTS_SPLIT: {
215       contents_split_ = static_cast<views::SingleSplitView*>(view);
216       // We're installed as the LayoutManager before BrowserView creates the
217       // contents, so we have to set contents_container_ here rather than in
218       // Installed.
219       contents_container_ = browser_view_->contents_;
220       break;
221     }
222     case VIEW_ID_INFO_BAR_CONTAINER:
223       infobar_container_ = view;
224       break;
225     case VIEW_ID_DOWNLOAD_SHELF:
226       download_shelf_ = static_cast<DownloadShelfView*>(view);
227       break;
228     case VIEW_ID_BOOKMARK_BAR:
229       active_bookmark_bar_ = static_cast<BookmarkBarView*>(view);
230       break;
231     case VIEW_ID_TOOLBAR:
232       toolbar_ = static_cast<ToolbarView*>(view);
233       break;
234     case VIEW_ID_TAB_STRIP:
235       tabstrip_ = static_cast<AbstractTabStripView*>(view);
236       break;
237   }
238 }
239 
ViewRemoved(views::View * host,views::View * view)240 void BrowserViewLayout::ViewRemoved(views::View* host, views::View* view) {
241   switch (view->GetID()) {
242     case VIEW_ID_BOOKMARK_BAR:
243       active_bookmark_bar_ = NULL;
244       break;
245   }
246 }
247 
Layout(views::View * host)248 void BrowserViewLayout::Layout(views::View* host) {
249   vertical_layout_rect_ = browser_view_->GetLocalBounds();
250   int top = LayoutTabStrip();
251   if (browser_view_->IsTabStripVisible() && !browser_view_->UseVerticalTabs()) {
252     tabstrip_->SetBackgroundOffset(gfx::Point(
253         tabstrip_->GetMirroredX() + browser_view_->GetMirroredX(),
254         browser_view_->frame()->GetHorizontalTabStripVerticalOffset(false)));
255   }
256   top = LayoutToolbar(top);
257   top = LayoutBookmarkAndInfoBars(top);
258   int bottom = LayoutDownloadShelf(browser_view_->height());
259   int active_top_margin = GetTopMarginForActiveContent();
260   top -= active_top_margin;
261   contents_container_->SetActiveTopMargin(active_top_margin);
262   LayoutTabContents(top, bottom);
263   // This must be done _after_ we lay out the TabContents since this
264   // code calls back into us to find the bounding box the find bar
265   // must be laid out within, and that code depends on the
266   // TabContentsContainer's bounds being up to date.
267   if (browser()->HasFindBarController()) {
268     browser()->GetFindBarController()->find_bar()->MoveWindowIfNecessary(
269         gfx::Rect(), true);
270   }
271 }
272 
273 // Return the preferred size which is the size required to give each
274 // children their respective preferred size.
GetPreferredSize(views::View * host)275 gfx::Size BrowserViewLayout::GetPreferredSize(views::View* host) {
276   return gfx::Size();
277 }
278 
279 //////////////////////////////////////////////////////////////////////////////
280 // BrowserViewLayout, private:
281 
browser()282 Browser* BrowserViewLayout::browser() {
283   return browser_view_->browser();
284 }
285 
browser() const286 const Browser* BrowserViewLayout::browser() const {
287   return browser_view_->browser();
288 }
289 
LayoutTabStrip()290 int BrowserViewLayout::LayoutTabStrip() {
291   if (!browser_view_->IsTabStripVisible()) {
292     tabstrip_->SetVisible(false);
293     tabstrip_->SetBounds(0, 0, 0, 0);
294     return 0;
295   }
296 
297   gfx::Rect tabstrip_bounds(
298       browser_view_->frame()->GetBoundsForTabStrip(tabstrip_));
299   gfx::Point tabstrip_origin(tabstrip_bounds.origin());
300   views::View::ConvertPointToView(browser_view_->parent(), browser_view_,
301                                   &tabstrip_origin);
302   tabstrip_bounds.set_origin(tabstrip_origin);
303 
304   if (browser_view_->UseVerticalTabs())
305     vertical_layout_rect_.Inset(tabstrip_bounds.width(), 0, 0, 0);
306 
307   tabstrip_->SetVisible(true);
308   tabstrip_->SetBoundsRect(tabstrip_bounds);
309   return browser_view_->UseVerticalTabs() ?
310       tabstrip_bounds.y() : tabstrip_bounds.bottom();
311 }
312 
LayoutToolbar(int top)313 int BrowserViewLayout::LayoutToolbar(int top) {
314   int browser_view_width = vertical_layout_rect_.width();
315   bool visible = browser_view_->IsToolbarVisible();
316   toolbar_->location_bar()->SetFocusable(visible);
317   int y = top;
318   if (!browser_view_->UseVerticalTabs()) {
319     y -= ((visible && browser_view_->IsTabStripVisible()) ?
320           kToolbarTabStripVerticalOverlap : 0);
321   }
322   int height = visible ? toolbar_->GetPreferredSize().height() : 0;
323   toolbar_->SetVisible(visible);
324   toolbar_->SetBounds(vertical_layout_rect_.x(), y, browser_view_width, height);
325   return y + height;
326 }
327 
LayoutBookmarkAndInfoBars(int top)328 int BrowserViewLayout::LayoutBookmarkAndInfoBars(int top) {
329   find_bar_y_ = top + browser_view_->y() - 1;
330   if (active_bookmark_bar_) {
331     // If we're showing the Bookmark bar in detached style, then we
332     // need to show any Info bar _above_ the Bookmark bar, since the
333     // Bookmark bar is styled to look like it's part of the page.
334     if (active_bookmark_bar_->IsDetached())
335       return LayoutBookmarkBar(LayoutInfoBar(top));
336     // Otherwise, Bookmark bar first, Info bar second.
337     top = std::max(toolbar_->bounds().bottom(), LayoutBookmarkBar(top));
338   }
339   find_bar_y_ = top + browser_view_->y() - 1;
340   return LayoutInfoBar(top);
341 }
342 
LayoutBookmarkBar(int top)343 int BrowserViewLayout::LayoutBookmarkBar(int top) {
344   DCHECK(active_bookmark_bar_);
345   int y = top;
346   if (!browser_view_->IsBookmarkBarVisible()) {
347     active_bookmark_bar_->SetVisible(false);
348     active_bookmark_bar_->SetBounds(0, y, browser_view_->width(), 0);
349     return y;
350   }
351 
352   active_bookmark_bar_->set_infobar_visible(InfobarVisible());
353   int bookmark_bar_height = active_bookmark_bar_->GetPreferredSize().height();
354   y -= views::NonClientFrameView::kClientEdgeThickness +
355       active_bookmark_bar_->GetToolbarOverlap(false);
356   active_bookmark_bar_->SetVisible(true);
357   active_bookmark_bar_->SetBounds(vertical_layout_rect_.x(), y,
358                                   vertical_layout_rect_.width(),
359                                   bookmark_bar_height);
360   return y + bookmark_bar_height;
361 }
362 
LayoutInfoBar(int top)363 int BrowserViewLayout::LayoutInfoBar(int top) {
364   // Raise the |infobar_container_| by its vertical overlap.
365   infobar_container_->SetVisible(InfobarVisible());
366   int height;
367   int overlapped_top = top -
368       static_cast<InfoBarContainerView*>(infobar_container_)->
369           GetVerticalOverlap(&height);
370   infobar_container_->SetBounds(vertical_layout_rect_.x(),
371                                 overlapped_top,
372                                 vertical_layout_rect_.width(),
373                                 height);
374   return overlapped_top + height;
375 }
376 
377 // |browser_reserved_rect| is in browser_view_ coordinates.
378 // |future_source_bounds| is in |source|'s parent coordinates.
379 // |future_parent_offset| is required, since parent view is not moved yet.
380 // Note that |future_parent_offset| is relative to browser_view_, not to
381 // the parent view.
UpdateReservedContentsRect(const gfx::Rect & browser_reserved_rect,TabContentsContainer * source,const gfx::Rect & future_source_bounds,const gfx::Point & future_parent_offset)382 void BrowserViewLayout::UpdateReservedContentsRect(
383     const gfx::Rect& browser_reserved_rect,
384     TabContentsContainer* source,
385     const gfx::Rect& future_source_bounds,
386     const gfx::Point& future_parent_offset) {
387   gfx::Point resize_corner_origin(browser_reserved_rect.origin());
388   // Convert |resize_corner_origin| from browser_view_ to source's parent
389   // coordinates.
390   views::View::ConvertPointToView(browser_view_, source->parent(),
391                                   &resize_corner_origin);
392   // Create |reserved_rect| in source's parent coordinates.
393   gfx::Rect reserved_rect(resize_corner_origin, browser_reserved_rect.size());
394   // Apply source's parent future offset to it.
395   reserved_rect.Offset(-future_parent_offset.x(), -future_parent_offset.y());
396   if (future_source_bounds.Intersects(reserved_rect)) {
397     // |source| is not properly positioned yet to use ConvertPointToView,
398     // so convert it into |source|'s coordinates manually.
399     reserved_rect.Offset(-future_source_bounds.x(), -future_source_bounds.y());
400   } else {
401     reserved_rect = gfx::Rect();
402   }
403 
404   source->SetReservedContentsRect(reserved_rect);
405 }
406 
LayoutTabContents(int top,int bottom)407 void BrowserViewLayout::LayoutTabContents(int top, int bottom) {
408   // The ultimate idea is to calcualte bounds and reserved areas for all
409   // contents views first and then resize them all, so every view
410   // (and its contents) is resized and laid out only once.
411 
412   // The views hierarcy (see browser_view.h for more details):
413   // 1) Sidebar is not allowed:
414   //     contents_split_ -> [contents_container_ | devtools]
415   // 2) Sidebar is allowed:
416   //     contents_split_ ->
417   //         [sidebar_split -> [contents_container_ | sidebar]] | devtools
418 
419   gfx::Rect sidebar_split_bounds;
420   gfx::Rect contents_bounds;
421   gfx::Rect sidebar_bounds;
422   gfx::Rect devtools_bounds;
423 
424   gfx::Rect contents_split_bounds(vertical_layout_rect_.x(), top,
425                                   vertical_layout_rect_.width(),
426                                   std::max(0, bottom - top));
427   contents_split_->CalculateChildrenBounds(
428       contents_split_bounds, &sidebar_split_bounds, &devtools_bounds);
429   gfx::Point contents_split_offset(
430       contents_split_bounds.x() - contents_split_->bounds().x(),
431       contents_split_bounds.y() - contents_split_->bounds().y());
432   gfx::Point sidebar_split_offset(contents_split_offset);
433   sidebar_split_offset.Offset(sidebar_split_bounds.x(),
434                               sidebar_split_bounds.y());
435 
436   views::SingleSplitView* sidebar_split = browser_view_->sidebar_split_;
437   if (sidebar_split) {
438     DCHECK(sidebar_split == contents_split_->GetChildViewAt(0));
439     sidebar_split->CalculateChildrenBounds(
440         sidebar_split_bounds, &contents_bounds, &sidebar_bounds);
441   } else {
442     contents_bounds = sidebar_split_bounds;
443   }
444 
445   // Layout resize corner, sidebar mini tabs and calculate reserved contents
446   // rects here as all contents view bounds are already determined, but not yet
447   // set at this point, so contents will be laid out once at most.
448   // TODO(alekseys): layout sidebar minitabs and adjust reserved rect
449   // accordingly.
450   gfx::Rect browser_reserved_rect;
451   if (!browser_view_->frame_->GetWindow()->IsMaximized() &&
452       !browser_view_->frame_->GetWindow()->IsFullscreen()) {
453     gfx::Size resize_corner_size = browser_view_->GetResizeCornerSize();
454     if (!resize_corner_size.IsEmpty()) {
455       gfx::Rect bounds = browser_view_->GetContentsBounds();
456       gfx::Point resize_corner_origin(
457           bounds.right() - resize_corner_size.width(),
458           bounds.bottom() - resize_corner_size.height());
459       browser_reserved_rect =
460           gfx::Rect(resize_corner_origin, resize_corner_size);
461     }
462   }
463 
464   UpdateReservedContentsRect(browser_reserved_rect,
465                              browser_view_->contents_container_,
466                              contents_bounds,
467                              sidebar_split_offset);
468   if (sidebar_split) {
469     UpdateReservedContentsRect(browser_reserved_rect,
470                                browser_view_->sidebar_container_,
471                                sidebar_bounds,
472                                sidebar_split_offset);
473   }
474   UpdateReservedContentsRect(browser_reserved_rect,
475                              browser_view_->devtools_container_,
476                              devtools_bounds,
477                              contents_split_offset);
478 
479   // Now it's safe to actually resize all contents views in the hierarchy.
480   contents_split_->SetBoundsRect(contents_split_bounds);
481   if (sidebar_split)
482     sidebar_split->SetBoundsRect(sidebar_split_bounds);
483 }
484 
GetTopMarginForActiveContent()485 int BrowserViewLayout::GetTopMarginForActiveContent() {
486   if (!active_bookmark_bar_ || !browser_view_->IsBookmarkBarVisible() ||
487       !active_bookmark_bar_->IsDetached()) {
488     return 0;
489   }
490 
491   if (contents_split_->GetChildViewAt(1) &&
492       contents_split_->GetChildViewAt(1)->IsVisible())
493     return 0;
494 
495   if (SidebarManager::IsSidebarAllowed()) {
496     views::View* sidebar_split = contents_split_->GetChildViewAt(0);
497     if (sidebar_split->GetChildViewAt(1) &&
498         sidebar_split->GetChildViewAt(1)->IsVisible())
499       return 0;
500   }
501 
502   // Adjust for separator.
503   return active_bookmark_bar_->height() -
504       views::NonClientFrameView::kClientEdgeThickness;
505 }
506 
LayoutDownloadShelf(int bottom)507 int BrowserViewLayout::LayoutDownloadShelf(int bottom) {
508   // Re-layout the shelf either if it is visible or if it's close animation
509   // is currently running.
510   if (browser_view_->IsDownloadShelfVisible() ||
511       (download_shelf_ && download_shelf_->IsClosing())) {
512     bool visible = browser()->SupportsWindowFeature(
513         Browser::FEATURE_DOWNLOADSHELF);
514     DCHECK(download_shelf_);
515     int height = visible ? download_shelf_->GetPreferredSize().height() : 0;
516     download_shelf_->SetVisible(visible);
517     download_shelf_->SetBounds(vertical_layout_rect_.x(), bottom - height,
518                                vertical_layout_rect_.width(), height);
519     download_shelf_->Layout();
520     bottom -= height;
521   }
522   return bottom;
523 }
524 
InfobarVisible() const525 bool BrowserViewLayout::InfobarVisible() const {
526   // NOTE: Can't check if the size IsEmpty() since it's always 0-width.
527   return browser()->SupportsWindowFeature(Browser::FEATURE_INFOBAR) &&
528       (infobar_container_->GetPreferredSize().height() != 0);
529 }
530