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