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