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