- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / frame / browser_view_layout.cc
1 // Copyright 2012 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 "base/observer_list.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/browser_window.h"
12 #include "chrome/browser/ui/find_bar/find_bar.h"
13 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
14 #include "chrome/browser/ui/search/search_model.h"
15 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
16 #include "chrome/browser/ui/views/download/download_shelf_view.h"
17 #include "chrome/browser/ui/views/frame/browser_frame.h"
18 #include "chrome/browser/ui/views/frame/browser_view.h"
19 #include "chrome/browser/ui/views/frame/browser_view_layout_delegate.h"
20 #include "chrome/browser/ui/views/frame/contents_container.h"
21 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
22 #include "chrome/browser/ui/views/frame/top_container_view.h"
23 #include "chrome/browser/ui/views/fullscreen_exit_bubble_views.h"
24 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
25 #include "chrome/browser/ui/views/tabs/tab_strip.h"
26 #include "components/web_modal/web_contents_modal_dialog_host.h"
27 #include "ui/base/hit_test.h"
28 #include "ui/gfx/point.h"
29 #include "ui/gfx/scrollbar_size.h"
30 #include "ui/gfx/size.h"
31 #include "ui/views/controls/webview/webview.h"
32
33 using views::View;
34 using web_modal::WebContentsModalDialogHost;
35 using web_modal::ModalDialogHostObserver;
36
37 namespace {
38
39 // The visible height of the shadow above the tabs. Clicks in this area are
40 // treated as clicks to the frame, rather than clicks to the tab.
41 const int kTabShadowSize = 2;
42 // The number of pixels the metro switcher is offset from the right edge.
43 const int kWindowSwitcherOffsetX = 7;
44 // The number of pixels the constrained window should overlap the bottom
45 // of the omnibox.
46 const int kConstrainedWindowOverlap = 3;
47
48 // Combines View::ConvertPointToTarget and View::HitTest for a given |point|.
49 // Converts |point| from |src| to |dst| and hit tests it against |dst|. The
50 // converted |point| can then be retrieved and used for additional tests.
51 bool ConvertedHitTest(views::View* src, views::View* dst, gfx::Point* point) {
52   DCHECK(src);
53   DCHECK(dst);
54   DCHECK(point);
55   views::View::ConvertPointToTarget(src, dst, point);
56   return dst->HitTestPoint(*point);
57 }
58
59 }  // namespace
60
61 class BrowserViewLayout::WebContentsModalDialogHostViews
62     : public WebContentsModalDialogHost {
63  public:
64   explicit WebContentsModalDialogHostViews(
65       BrowserViewLayout* browser_view_layout)
66           : browser_view_layout_(browser_view_layout) {
67   }
68
69   virtual ~WebContentsModalDialogHostViews() {
70     FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
71                       observer_list_,
72                       OnHostDestroying());
73   }
74
75   void NotifyPositionRequiresUpdate() {
76     FOR_EACH_OBSERVER(ModalDialogHostObserver,
77                       observer_list_,
78                       OnPositionRequiresUpdate());
79   }
80
81  private:
82   virtual gfx::NativeView GetHostView() const OVERRIDE {
83     gfx::NativeWindow native_window =
84         browser_view_layout_->browser()->window()->GetNativeWindow();
85     return views::Widget::GetWidgetForNativeWindow(native_window)->
86         GetNativeView();
87   }
88
89   // Center horizontally over the content area, with the top overlapping the
90   // browser chrome.
91   virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE {
92     int top_y = browser_view_layout_->web_contents_modal_dialog_top_y_;
93     gfx::Rect content_area =
94         browser_view_layout_->browser_view_->GetClientAreaBounds();
95     int middle_x = content_area.x() + content_area.width() / 2;
96     return gfx::Point(middle_x - size.width() / 2, top_y);
97   }
98
99   virtual gfx::Size GetMaximumDialogSize() OVERRIDE {
100     gfx::Rect content_area =
101         browser_view_layout_->contents_container_->ConvertRectToWidget(
102             browser_view_layout_->contents_container_->GetLocalBounds());
103
104     gfx::Size max_dialog_size = content_area.size();
105     // Adjust for difference in alignment between the dialog top and the top of
106     // the content area.
107     int height_offset = content_area.y() -
108         browser_view_layout_->web_contents_modal_dialog_top_y_;
109     max_dialog_size.Enlarge(0, height_offset);
110     return max_dialog_size;
111   }
112
113   // Add/remove observer.
114   virtual void AddObserver(
115       ModalDialogHostObserver* observer) OVERRIDE {
116     observer_list_.AddObserver(observer);
117   }
118   virtual void RemoveObserver(
119       ModalDialogHostObserver* observer) OVERRIDE {
120     observer_list_.RemoveObserver(observer);
121   }
122
123   BrowserViewLayout* const browser_view_layout_;
124
125   ObserverList<ModalDialogHostObserver> observer_list_;
126
127   DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogHostViews);
128 };
129
130 // static
131 const int BrowserViewLayout::kToolbarTabStripVerticalOverlap = 3;
132
133 ////////////////////////////////////////////////////////////////////////////////
134 // BrowserViewLayout, public:
135
136 BrowserViewLayout::BrowserViewLayout()
137     : browser_(NULL),
138       browser_view_(NULL),
139       top_container_(NULL),
140       tab_strip_(NULL),
141       toolbar_(NULL),
142       bookmark_bar_(NULL),
143       infobar_container_(NULL),
144       contents_split_(NULL),
145       contents_container_(NULL),
146       download_shelf_(NULL),
147       immersive_mode_controller_(NULL),
148       dialog_host_(new WebContentsModalDialogHostViews(this)),
149       web_contents_modal_dialog_top_y_(-1) {}
150
151 BrowserViewLayout::~BrowserViewLayout() {
152 }
153
154 void BrowserViewLayout::Init(
155     BrowserViewLayoutDelegate* delegate,
156     Browser* browser,
157     BrowserView* browser_view,
158     views::View* top_container,
159     TabStrip* tab_strip,
160     views::View* toolbar,
161     InfoBarContainerView* infobar_container,
162     views::View* contents_split,
163     ContentsContainer* contents_container,
164     ImmersiveModeController* immersive_mode_controller) {
165   delegate_.reset(delegate);
166   browser_ = browser;
167   browser_view_ = browser_view;
168   top_container_ = top_container;
169   tab_strip_ = tab_strip;
170   toolbar_ = toolbar;
171   infobar_container_ = infobar_container;
172   contents_split_ = contents_split;
173   contents_container_ = contents_container;
174   immersive_mode_controller_ = immersive_mode_controller;
175 }
176
177 WebContentsModalDialogHost*
178     BrowserViewLayout::GetWebContentsModalDialogHost() {
179   return dialog_host_.get();
180 }
181
182 gfx::Size BrowserViewLayout::GetMinimumSize() {
183   gfx::Size tabstrip_size(
184       browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
185       tab_strip_->GetMinimumSize() : gfx::Size());
186   BrowserNonClientFrameView::TabStripInsets tab_strip_insets(
187       browser_view_->frame()->GetTabStripInsets(false));
188   gfx::Size toolbar_size(
189       (browser()->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
190        browser()->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) ?
191            toolbar_->GetMinimumSize() : gfx::Size());
192   if (tabstrip_size.height() && toolbar_size.height())
193     toolbar_size.Enlarge(0, -kToolbarTabStripVerticalOverlap);
194   gfx::Size bookmark_bar_size;
195   if (bookmark_bar_ &&
196       bookmark_bar_->visible() &&
197       browser()->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR)) {
198     bookmark_bar_size = bookmark_bar_->GetMinimumSize();
199     bookmark_bar_size.Enlarge(0, -bookmark_bar_->GetToolbarOverlap());
200   }
201   // TODO: Adjust the minimum height for the find bar.
202
203   gfx::Size contents_size(contents_split_->GetMinimumSize());
204
205   int min_height = tabstrip_size.height() + toolbar_size.height() +
206       bookmark_bar_size.height() + contents_size.height();
207   int widths[] = {
208         tabstrip_size.width() + tab_strip_insets.left + tab_strip_insets.right,
209         toolbar_size.width(),
210         bookmark_bar_size.width(),
211         contents_size.width() };
212   int min_width = *std::max_element(&widths[0], &widths[arraysize(widths)]);
213   return gfx::Size(min_width, min_height);
214 }
215
216 gfx::Rect BrowserViewLayout::GetFindBarBoundingBox() const {
217   // This function returns the area the Find Bar can be laid out within. This
218   // basically implies the "user-perceived content area" of the browser
219   // window excluding the vertical scrollbar. The "user-perceived content area"
220   // excludes the detached bookmark bar (in the New Tab case) and any infobars
221   // since they are not _visually_ connected to the Toolbar.
222
223   // First determine the bounding box of the content area in Widget
224   // coordinates.
225   gfx::Rect bounding_box = contents_container_->ConvertRectToWidget(
226       contents_container_->GetLocalBounds());
227
228   gfx::Rect top_container_bounds = top_container_->ConvertRectToWidget(
229       top_container_->GetLocalBounds());
230
231   int find_bar_y = 0;
232   if (immersive_mode_controller_->IsEnabled() &&
233       !immersive_mode_controller_->IsRevealed()) {
234     // Position the find bar exactly below the top container. In immersive
235     // fullscreen, when the top-of-window views are not revealed, only the
236     // miniature immersive style tab strip is visible. Do not overlap the
237     // find bar and the tab strip.
238     find_bar_y = top_container_bounds.bottom();
239   } else {
240     // Position the find bar 1 pixel above the bottom of the top container
241     // so that it occludes the border between the content area and the top
242     // container and looks connected to the top container.
243     find_bar_y = top_container_bounds.bottom() - 1;
244   }
245
246   // Grow the height of |bounding_box| by the height of any elements between
247   // the top container and |contents_container_| such as the detached bookmark
248   // bar and any infobars.
249   int height_delta = bounding_box.y() - find_bar_y;
250   bounding_box.set_y(find_bar_y);
251   bounding_box.set_height(std::max(0, bounding_box.height() + height_delta));
252
253   // Finally decrease the width of the bounding box by the width of
254   // the vertical scroll bar.
255   int scrollbar_width = gfx::scrollbar_size();
256   bounding_box.set_width(std::max(0, bounding_box.width() - scrollbar_width));
257   if (base::i18n::IsRTL())
258     bounding_box.set_x(bounding_box.x() + scrollbar_width);
259
260   return bounding_box;
261 }
262
263 int BrowserViewLayout::NonClientHitTest(const gfx::Point& point) {
264   // Since the TabStrip only renders in some parts of the top of the window,
265   // the un-obscured area is considered to be part of the non-client caption
266   // area of the window. So we need to treat hit-tests in these regions as
267   // hit-tests of the titlebar.
268
269   views::View* parent = browser_view_->parent();
270
271   gfx::Point point_in_browser_view_coords(point);
272   views::View::ConvertPointToTarget(
273       parent, browser_view_, &point_in_browser_view_coords);
274   gfx::Point test_point(point);
275
276   // Determine if the TabStrip exists and is capable of being clicked on. We
277   // might be a popup window without a TabStrip.
278   if (browser_view_->IsTabStripVisible()) {
279     // See if the mouse pointer is within the bounds of the TabStrip.
280     if (ConvertedHitTest(parent, tab_strip_, &test_point)) {
281       if (tab_strip_->IsPositionInWindowCaption(test_point))
282         return HTCAPTION;
283       return HTCLIENT;
284     }
285
286     // The top few pixels of the TabStrip are a drop-shadow - as we're pretty
287     // starved of dragable area, let's give it to window dragging (this also
288     // makes sense visually).
289     if (!(browser_view_->IsMaximized() || browser_view_->IsFullscreen()) &&
290         (point_in_browser_view_coords.y() <
291             (tab_strip_->y() + kTabShadowSize))) {
292       // We return HTNOWHERE as this is a signal to our containing
293       // NonClientView that it should figure out what the correct hit-test
294       // code is given the mouse position...
295       return HTNOWHERE;
296     }
297   }
298
299   // If the point's y coordinate is below the top of the toolbar and otherwise
300   // within the bounds of this view, the point is considered to be within the
301   // client area.
302   gfx::Rect bv_bounds = browser_view_->bounds();
303   bv_bounds.Offset(0, toolbar_->y());
304   bv_bounds.set_height(bv_bounds.height() - toolbar_->y());
305   if (bv_bounds.Contains(point))
306     return HTCLIENT;
307
308   // If the point is within the bounds of the window switcher button, the point
309   // is considered to be within the client area.
310   views::View* window_switcher_button = delegate_->GetWindowSwitcherButton();
311   if (window_switcher_button && window_switcher_button->visible()) {
312     gfx::Point window_switcher_point(point_in_browser_view_coords);
313     views::View::ConvertPointToTarget(browser_view_, window_switcher_button,
314                                       &window_switcher_point);
315     if (window_switcher_button->HitTestPoint(window_switcher_point))
316       return HTCLIENT;
317   }
318
319   // If the point's y coordinate is above the top of the toolbar, but neither
320   // over the tabstrip nor over the window switcher button (per previous
321   // checking in this function), then we consider it in the window caption
322   // (e.g. the area to the right of the tabstrip underneath the window
323   // controls). However, note that we DO NOT return HTCAPTION here, because
324   // when the window is maximized the window controls will fall into this
325   // space (since the BrowserView is sized to entire size of the window at that
326   // point), and the HTCAPTION value will cause the window controls not to work.
327   // So we return HTNOWHERE so that the caller will hit-test the window controls
328   // before finally falling back to HTCAPTION.
329   bv_bounds = browser_view_->bounds();
330   bv_bounds.set_height(toolbar_->y());
331   if (bv_bounds.Contains(point))
332     return HTNOWHERE;
333
334   // If the point is somewhere else, delegate to the default implementation.
335   return browser_view_->views::ClientView::NonClientHitTest(point);
336 }
337
338 //////////////////////////////////////////////////////////////////////////////
339 // BrowserViewLayout, views::LayoutManager implementation:
340
341 void BrowserViewLayout::Layout(views::View* browser_view) {
342   vertical_layout_rect_ = browser_view->GetLocalBounds();
343   int top = LayoutTabStripRegion(browser_view);
344   if (delegate_->IsTabStripVisible()) {
345     int x = tab_strip_->GetMirroredX() +
346         browser_view_->GetMirroredX() +
347         browser_view_->frame()->GetThemeBackgroundXInset();
348     tab_strip_->SetBackgroundOffset(
349         gfx::Point(x, browser_view_->frame()->GetTabStripInsets(false).top));
350   }
351   top = LayoutToolbar(top);
352
353   top = LayoutBookmarkAndInfoBars(top, browser_view->y());
354
355   // Top container requires updated toolbar and bookmark bar to compute bounds.
356   UpdateTopContainerBounds();
357
358   int bottom = LayoutDownloadShelf(browser_view->height());
359   // Treat a detached bookmark bar as if the web contents container is shifted
360   // upwards and overlaps it.
361   int active_top_margin = GetContentsOffsetForBookmarkBar();
362   contents_container_->SetActiveTopMargin(active_top_margin);
363   top -= active_top_margin;
364   LayoutContentsSplitView(top, bottom);
365
366   // This must be done _after_ we lay out the WebContents since this
367   // code calls back into us to find the bounding box the find bar
368   // must be laid out within, and that code depends on the
369   // TabContentsContainer's bounds being up to date.
370   if (browser()->HasFindBarController()) {
371     browser()->GetFindBarController()->find_bar()->MoveWindowIfNecessary(
372         gfx::Rect(), true);
373   }
374
375   // Adjust the fullscreen exit bubble bounds for |top_container_|'s new bounds.
376   // This makes the fullscreen exit bubble look like it animates with
377   // |top_container_| in immersive fullscreen.
378   FullscreenExitBubbleViews* fullscreen_exit_bubble =
379       delegate_->GetFullscreenExitBubble();
380   if (fullscreen_exit_bubble)
381     fullscreen_exit_bubble->RepositionIfVisible();
382
383   // Adjust any web contents modal dialogs.
384   dialog_host_->NotifyPositionRequiresUpdate();
385 }
386
387 // Return the preferred size which is the size required to give each
388 // children their respective preferred size.
389 gfx::Size BrowserViewLayout::GetPreferredSize(views::View* host) {
390   return gfx::Size();
391 }
392
393 //////////////////////////////////////////////////////////////////////////////
394 // BrowserViewLayout, private:
395
396 int BrowserViewLayout::LayoutTabStripRegion(views::View* browser_view) {
397   if (!delegate_->IsTabStripVisible()) {
398     tab_strip_->SetVisible(false);
399     tab_strip_->SetBounds(0, 0, 0, 0);
400     return 0;
401   }
402   // This retrieves the bounds for the tab strip based on whether or not we show
403   // anything to the left of it, like the incognito avatar.
404   gfx::Rect tabstrip_bounds(delegate_->GetBoundsForTabStrip(tab_strip_));
405   gfx::Point tabstrip_origin(tabstrip_bounds.origin());
406   views::View::ConvertPointToTarget(
407       browser_view->parent(), browser_view, &tabstrip_origin);
408   tabstrip_bounds.set_origin(tabstrip_origin);
409
410   tab_strip_->SetVisible(true);
411   tab_strip_->SetBoundsRect(tabstrip_bounds);
412   int bottom = tabstrip_bounds.bottom();
413
414   // The metro window switcher sits at the far right edge of the tabstrip
415   // a |kWindowSwitcherOffsetX| pixels from the right edge.
416   // Only visible if there is more than one type of window to switch between.
417   // TODO(mad): update this code when more window types than just incognito
418   // and regular are available.
419   views::View* switcher_button = delegate_->GetWindowSwitcherButton();
420   if (switcher_button) {
421     if (browser()->profile()->HasOffTheRecordProfile() &&
422         chrome::FindBrowserWithProfile(
423             browser()->profile()->GetOriginalProfile(),
424             browser()->host_desktop_type()) != NULL) {
425       switcher_button->SetVisible(true);
426       int width = browser_view->width();
427       gfx::Size ps = switcher_button->GetPreferredSize();
428       if (width > ps.width()) {
429         switcher_button->SetBounds(width - ps.width() - kWindowSwitcherOffsetX,
430                                    0,
431                                    ps.width(),
432                                    ps.height());
433       }
434     } else {
435       // We hide the button if the incognito profile is not alive.
436       // Note that Layout() is not called to all browser windows automatically
437       // when a profile goes away but we rely in the metro_driver.dll to call
438       // ::SetWindowPos( , .. SWP_SHOWWINDOW) which causes this function to
439       // be called again. This works both in showing or hidding the button.
440       switcher_button->SetVisible(false);
441     }
442   }
443
444   return bottom;
445 }
446
447 int BrowserViewLayout::LayoutToolbar(int top) {
448   int browser_view_width = vertical_layout_rect_.width();
449   bool toolbar_visible = delegate_->IsToolbarVisible();
450   int y = top;
451   y -= (toolbar_visible && delegate_->IsTabStripVisible()) ?
452         kToolbarTabStripVerticalOverlap : 0;
453   int height = toolbar_visible ? toolbar_->GetPreferredSize().height() : 0;
454   toolbar_->SetVisible(toolbar_visible);
455   toolbar_->SetBounds(vertical_layout_rect_.x(), y, browser_view_width, height);
456
457   return y + height;
458 }
459
460 int BrowserViewLayout::LayoutBookmarkAndInfoBars(int top, int browser_view_y) {
461   web_contents_modal_dialog_top_y_ =
462       top + browser_view_y - kConstrainedWindowOverlap;
463
464   if (bookmark_bar_) {
465     // If we're showing the Bookmark bar in detached style, then we
466     // need to show any Info bar _above_ the Bookmark bar, since the
467     // Bookmark bar is styled to look like it's part of the page.
468     if (bookmark_bar_->IsDetached()) {
469       web_contents_modal_dialog_top_y_ =
470           top + browser_view_y - kConstrainedWindowOverlap;
471       return LayoutBookmarkBar(LayoutInfoBar(top));
472     }
473     // Otherwise, Bookmark bar first, Info bar second.
474     top = std::max(toolbar_->bounds().bottom(), LayoutBookmarkBar(top));
475   }
476
477   return LayoutInfoBar(top);
478 }
479
480 int BrowserViewLayout::LayoutBookmarkBar(int top) {
481   int y = top;
482   if (!delegate_->IsBookmarkBarVisible()) {
483     bookmark_bar_->SetVisible(false);
484     // TODO(jamescook): Don't change the bookmark bar height when it is
485     // invisible, so we can use its height for layout even in that state.
486     bookmark_bar_->SetBounds(0, y, browser_view_->width(), 0);
487     return y;
488   }
489
490   bookmark_bar_->set_infobar_visible(InfobarVisible());
491   int bookmark_bar_height = bookmark_bar_->GetPreferredSize().height();
492   y -= bookmark_bar_->GetToolbarOverlap();
493   bookmark_bar_->SetBounds(vertical_layout_rect_.x(),
494                            y,
495                            vertical_layout_rect_.width(),
496                            bookmark_bar_height);
497   // Set visibility after setting bounds, as the visibility update uses the
498   // bounds to determine if the mouse is hovering over a button.
499   bookmark_bar_->SetVisible(true);
500   return y + bookmark_bar_height;
501 }
502
503 int BrowserViewLayout::LayoutInfoBar(int top) {
504   // In immersive fullscreen, the infobar always starts near the top of the
505   // screen, just under the "light bar" rectangular stripes.
506   if (immersive_mode_controller_->IsEnabled()) {
507     top = immersive_mode_controller_->ShouldHideTabIndicators()
508               ? browser_view_->y()
509               : browser_view_->y() + TabStrip::GetImmersiveHeight();
510   }
511   // Raise the |infobar_container_| by its vertical overlap.
512   infobar_container_->SetVisible(InfobarVisible());
513   int height;
514   int overlapped_top = top - infobar_container_->GetVerticalOverlap(&height);
515   infobar_container_->SetBounds(vertical_layout_rect_.x(),
516                                 overlapped_top,
517                                 vertical_layout_rect_.width(),
518                                 height);
519   return overlapped_top + height;
520 }
521
522 void BrowserViewLayout::LayoutContentsSplitView(int top, int bottom) {
523   // |contents_split_| contains web page contents and devtools.
524   // See browser_view.h for details.
525   gfx::Rect contents_split_bounds(vertical_layout_rect_.x(),
526                                   top,
527                                   vertical_layout_rect_.width(),
528                                   std::max(0, bottom - top));
529   contents_split_->SetBoundsRect(contents_split_bounds);
530 }
531
532 void BrowserViewLayout::UpdateTopContainerBounds() {
533   gfx::Rect top_container_bounds(top_container_->GetPreferredSize());
534
535   // If the immersive mode controller is animating the top-of-window views,
536   // part of the top container may be offscreen.
537   top_container_bounds.set_y(
538       immersive_mode_controller_->GetTopContainerVerticalOffset(
539           top_container_bounds.size()));
540   top_container_->SetBoundsRect(top_container_bounds);
541 }
542
543 int BrowserViewLayout::GetContentsOffsetForBookmarkBar() {
544   // If the bookmark bar is hidden or attached to the omnibox the web contents
545   // will appear directly underneath it and does not need an offset.
546   if (!bookmark_bar_ ||
547       !browser_view_->IsBookmarkBarVisible() ||
548       !bookmark_bar_->IsDetached()) {
549     return 0;
550   }
551
552   // Dev tools.
553   if (contents_split_->child_at(1) && contents_split_->child_at(1)->visible())
554     return 0;
555
556   // Offset for the detached bookmark bar.
557   return bookmark_bar_->height() -
558       bookmark_bar_->GetFullyDetachedToolbarOverlap();
559 }
560
561 int BrowserViewLayout::LayoutDownloadShelf(int bottom) {
562   if (delegate_->DownloadShelfNeedsLayout()) {
563     bool visible = browser()->SupportsWindowFeature(
564         Browser::FEATURE_DOWNLOADSHELF);
565     DCHECK(download_shelf_);
566     int height = visible ? download_shelf_->GetPreferredSize().height() : 0;
567     download_shelf_->SetVisible(visible);
568     download_shelf_->SetBounds(vertical_layout_rect_.x(), bottom - height,
569                                vertical_layout_rect_.width(), height);
570     download_shelf_->Layout();
571     bottom -= height;
572   }
573   return bottom;
574 }
575
576 bool BrowserViewLayout::InfobarVisible() const {
577   // Cast to a views::View to access GetPreferredSize().
578   views::View* infobar_container = infobar_container_;
579   // NOTE: Can't check if the size IsEmpty() since it's always 0-width.
580   return browser_->SupportsWindowFeature(Browser::FEATURE_INFOBAR) &&
581       (infobar_container->GetPreferredSize().height() != 0);
582 }