Home | History | Annotate | Download | only in frame
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/themes/theme_service.h"
     10 #include "chrome/browser/ui/views/frame/browser_frame.h"
     11 #include "chrome/browser/ui/views/frame/browser_view.h"
     12 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     13 #include "chrome/browser/ui/views/toolbar_view.h"
     14 #include "content/browser/tab_contents/tab_contents.h"
     15 #include "grit/app_resources.h"
     16 #include "grit/chromium_strings.h"
     17 #include "grit/generated_resources.h"
     18 #include "grit/theme_resources.h"
     19 #include "ui/base/accessibility/accessible_view_state.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "ui/base/theme_provider.h"
     23 #include "ui/gfx/canvas_skia.h"
     24 #include "ui/gfx/font.h"
     25 #include "ui/gfx/path.h"
     26 #include "views/controls/button/image_button.h"
     27 #include "views/controls/image_view.h"
     28 #include "views/widget/root_view.h"
     29 #include "views/window/window.h"
     30 #include "views/window/window_resources.h"
     31 #include "views/window/window_shape.h"
     32 
     33 #if defined(OS_LINUX)
     34 #include "views/window/hit_test.h"
     35 #endif
     36 
     37 namespace {
     38 // The frame border is only visible in restored mode and is hardcoded to 4 px on
     39 // each side regardless of the system window border size.
     40 const int kFrameBorderThickness = 4;
     41 // Besides the frame border, there's another 11 px of empty space atop the
     42 // window in restored mode, to use to drag the window around.
     43 const int kNonClientRestoredExtraThickness = 11;
     44 // While resize areas on Windows are normally the same size as the window
     45 // borders, our top area is shrunk by 1 px to make it easier to move the window
     46 // around with our thinner top grabbable strip.  (Incidentally, our side and
     47 // bottom resize areas don't match the frame border thickness either -- they
     48 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
     49 const int kTopResizeAdjust = 1;
     50 // In the window corners, the resize areas don't actually expand bigger, but the
     51 // 16 px at the end of each edge triggers diagonal resizing.
     52 const int kResizeAreaCornerSize = 16;
     53 // The titlebar never shrinks too short to show the caption button plus some
     54 // padding below it.
     55 const int kCaptionButtonHeightWithPadding = 19;
     56 // The content left/right images have a shadow built into them.
     57 const int kContentEdgeShadowThickness = 2;
     58 // The titlebar has a 2 px 3D edge along the top and bottom.
     59 const int kTitlebarTopAndBottomEdgeThickness = 2;
     60 // The icon is inset 2 px from the left frame border.
     61 const int kIconLeftSpacing = 2;
     62 // The icon never shrinks below 16 px on a side.
     63 const int kIconMinimumSize = 16;
     64 // There is a 4 px gap between the icon and the title text.
     65 const int kIconTitleSpacing = 4;
     66 // There is a 5 px gap between the title text and the caption buttons.
     67 const int kTitleLogoSpacing = 5;
     68 // The OTR avatar ends 2 px above the bottom of the tabstrip (which, given the
     69 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
     70 // user).
     71 const int kOTRBottomSpacing = 2;
     72 // There are 2 px on each side of the OTR avatar (between the frame border and
     73 // it on the left, and between it and the tabstrip on the right).
     74 const int kOTRSideSpacing = 2;
     75 // The top 1 px of the tabstrip is shadow; in maximized mode we push this off
     76 // the top of the screen so the tabs appear flush against the screen edge.
     77 const int kTabstripTopShadowThickness = 1;
     78 // In restored mode, the New Tab button isn't at the same height as the caption
     79 // buttons, but the space will look cluttered if it actually slides under them,
     80 // so we stop it when the gap between the two is down to 5 px.
     81 const int kNewTabCaptionRestoredSpacing = 5;
     82 // In maximized mode, where the New Tab button and the caption buttons are at
     83 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid
     84 // looking too cluttered.
     85 const int kNewTabCaptionMaximizedSpacing = 16;
     86 // How far to indent the tabstrip from the left side of the screen when there
     87 // is no OTR icon.
     88 const int kTabStripIndent = 1;
     89 // Inset from the top of the toolbar/tabstrip to the shadow. Used only for
     90 // vertical tabs.
     91 const int kVerticalTabBorderInset = 3;
     92 }
     93 
     94 ///////////////////////////////////////////////////////////////////////////////
     95 // OpaqueBrowserFrameView, public:
     96 
     97 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
     98                                                BrowserView* browser_view)
     99     : BrowserNonClientFrameView(),
    100       ALLOW_THIS_IN_INITIALIZER_LIST(
    101           minimize_button_(new views::ImageButton(this))),
    102       ALLOW_THIS_IN_INITIALIZER_LIST(
    103           maximize_button_(new views::ImageButton(this))),
    104       ALLOW_THIS_IN_INITIALIZER_LIST(
    105           restore_button_(new views::ImageButton(this))),
    106       ALLOW_THIS_IN_INITIALIZER_LIST(
    107           close_button_(new views::ImageButton(this))),
    108       window_icon_(NULL),
    109       frame_(frame),
    110       browser_view_(browser_view) {
    111   ui::ThemeProvider* tp = frame_->GetThemeProviderForFrame();
    112   SkColor color = tp->GetColor(ThemeService::COLOR_BUTTON_BACKGROUND);
    113   SkBitmap* background =
    114       tp->GetBitmapNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND);
    115   minimize_button_->SetImage(views::CustomButton::BS_NORMAL,
    116                              tp->GetBitmapNamed(IDR_MINIMIZE));
    117   minimize_button_->SetImage(views::CustomButton::BS_HOT,
    118                              tp->GetBitmapNamed(IDR_MINIMIZE_H));
    119   minimize_button_->SetImage(views::CustomButton::BS_PUSHED,
    120                              tp->GetBitmapNamed(IDR_MINIMIZE_P));
    121   if (browser_view_->IsBrowserTypeNormal()) {
    122     minimize_button_->SetBackground(color, background,
    123         tp->GetBitmapNamed(IDR_MINIMIZE_BUTTON_MASK));
    124   }
    125   minimize_button_->SetAccessibleName(
    126       l10n_util::GetStringUTF16(IDS_ACCNAME_MINIMIZE));
    127   AddChildView(minimize_button_);
    128 
    129   maximize_button_->SetImage(views::CustomButton::BS_NORMAL,
    130                              tp->GetBitmapNamed(IDR_MAXIMIZE));
    131   maximize_button_->SetImage(views::CustomButton::BS_HOT,
    132                              tp->GetBitmapNamed(IDR_MAXIMIZE_H));
    133   maximize_button_->SetImage(views::CustomButton::BS_PUSHED,
    134                              tp->GetBitmapNamed(IDR_MAXIMIZE_P));
    135   if (browser_view_->IsBrowserTypeNormal()) {
    136     maximize_button_->SetBackground(color, background,
    137         tp->GetBitmapNamed(IDR_MAXIMIZE_BUTTON_MASK));
    138   }
    139   maximize_button_->SetAccessibleName(
    140       l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
    141   AddChildView(maximize_button_);
    142 
    143   restore_button_->SetImage(views::CustomButton::BS_NORMAL,
    144                             tp->GetBitmapNamed(IDR_RESTORE));
    145   restore_button_->SetImage(views::CustomButton::BS_HOT,
    146                             tp->GetBitmapNamed(IDR_RESTORE_H));
    147   restore_button_->SetImage(views::CustomButton::BS_PUSHED,
    148                             tp->GetBitmapNamed(IDR_RESTORE_P));
    149   if (browser_view_->IsBrowserTypeNormal()) {
    150     restore_button_->SetBackground(color, background,
    151         tp->GetBitmapNamed(IDR_RESTORE_BUTTON_MASK));
    152   }
    153   restore_button_->SetAccessibleName(
    154       l10n_util::GetStringUTF16(IDS_ACCNAME_RESTORE));
    155   AddChildView(restore_button_);
    156 
    157   close_button_->SetImage(views::CustomButton::BS_NORMAL,
    158                           tp->GetBitmapNamed(IDR_CLOSE));
    159   close_button_->SetImage(views::CustomButton::BS_HOT,
    160                           tp->GetBitmapNamed(IDR_CLOSE_H));
    161   close_button_->SetImage(views::CustomButton::BS_PUSHED,
    162                           tp->GetBitmapNamed(IDR_CLOSE_P));
    163   if (browser_view_->IsBrowserTypeNormal()) {
    164     close_button_->SetBackground(color, background,
    165         tp->GetBitmapNamed(IDR_CLOSE_BUTTON_MASK));
    166   }
    167   close_button_->SetAccessibleName(
    168       l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
    169   AddChildView(close_button_);
    170 
    171   // Initializing the TabIconView is expensive, so only do it if we need to.
    172   if (browser_view_->ShouldShowWindowIcon()) {
    173     window_icon_ = new TabIconView(this);
    174     window_icon_->set_is_light(true);
    175     AddChildView(window_icon_);
    176     window_icon_->Update();
    177   }
    178 }
    179 
    180 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
    181 }
    182 
    183 ///////////////////////////////////////////////////////////////////////////////
    184 // OpaqueBrowserFrameView, protected:
    185 
    186 int OpaqueBrowserFrameView::GetReservedHeight() const {
    187   return 0;
    188 }
    189 
    190 gfx::Rect OpaqueBrowserFrameView::GetBoundsForReservedArea() const {
    191   gfx::Rect client_view_bounds = CalculateClientAreaBounds(width(), height());
    192   return gfx::Rect(
    193       client_view_bounds.x(),
    194       client_view_bounds.y() + client_view_bounds.height(),
    195       client_view_bounds.width(),
    196       GetReservedHeight());
    197 }
    198 
    199 int OpaqueBrowserFrameView::NonClientTopBorderHeight(
    200     bool restored,
    201     bool ignore_vertical_tabs) const {
    202   views::Window* window = frame_->GetWindow();
    203   views::WindowDelegate* delegate = window->window_delegate();
    204   // |delegate| may be NULL if called from callback of InputMethodChanged while
    205   // a window is being destroyed.
    206   // See more discussion at http://crosbug.com/8958
    207   if ((delegate && delegate->ShouldShowWindowTitle()) ||
    208       (browser_view_->IsTabStripVisible() && !ignore_vertical_tabs &&
    209        browser_view_->UseVerticalTabs())) {
    210     return std::max(FrameBorderThickness(restored) + IconSize(),
    211         CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) +
    212         TitlebarBottomThickness(restored);
    213   }
    214 
    215   return FrameBorderThickness(restored) -
    216       ((browser_view_->IsTabStripVisible() && !restored &&
    217       window->IsMaximized()) ? kTabstripTopShadowThickness : 0);
    218 }
    219 
    220 ///////////////////////////////////////////////////////////////////////////////
    221 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
    222 
    223 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
    224     views::View* tabstrip) const {
    225   if (!tabstrip)
    226     return gfx::Rect();
    227 
    228   if (browser_view_->UseVerticalTabs()) {
    229     gfx::Size ps = tabstrip->GetPreferredSize();
    230     return gfx::Rect(NonClientBorderThickness(),
    231         NonClientTopBorderHeight(false, false), ps.width(),
    232         browser_view_->height());
    233   }
    234 
    235   int tabstrip_x = browser_view_->ShouldShowOffTheRecordAvatar() ?
    236       (otr_avatar_bounds_.right() + kOTRSideSpacing) :
    237       NonClientBorderThickness() + kTabStripIndent;
    238 
    239   int tabstrip_width = minimize_button_->x() - tabstrip_x -
    240       (frame_->GetWindow()->IsMaximized() ?
    241       kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing);
    242   return gfx::Rect(tabstrip_x, GetHorizontalTabStripVerticalOffset(false),
    243       std::max(0, tabstrip_width), tabstrip->GetPreferredSize().height());
    244 }
    245 
    246 int OpaqueBrowserFrameView::GetHorizontalTabStripVerticalOffset(
    247     bool restored) const {
    248   return NonClientTopBorderHeight(restored, true) + ((!restored &&
    249       (frame_->GetWindow()->IsMaximized() ||
    250       frame_->GetWindow()->IsFullscreen())) ?
    251       0 : kNonClientRestoredExtraThickness);
    252 }
    253 
    254 void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
    255   if (window_icon_)
    256     window_icon_->Update();
    257 }
    258 
    259 gfx::Size OpaqueBrowserFrameView::GetMinimumSize() {
    260   gfx::Size min_size(browser_view_->GetMinimumSize());
    261   int border_thickness = NonClientBorderThickness();
    262   min_size.Enlarge(2 * border_thickness,
    263                    NonClientTopBorderHeight(false, false) + border_thickness);
    264 
    265   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
    266   int min_titlebar_width = (2 * FrameBorderThickness(false)) +
    267       kIconLeftSpacing +
    268       (delegate && delegate->ShouldShowWindowIcon() ?
    269        (IconSize() + kTitleLogoSpacing) : 0);
    270 #if !defined(OS_CHROMEOS)
    271   min_titlebar_width +=
    272       minimize_button_->GetMinimumSize().width() +
    273       restore_button_->GetMinimumSize().width() +
    274       close_button_->GetMinimumSize().width();
    275 #endif
    276   min_size.set_width(std::max(min_size.width(), min_titlebar_width));
    277   return min_size;
    278 }
    279 
    280 ///////////////////////////////////////////////////////////////////////////////
    281 // OpaqueBrowserFrameView, views::NonClientFrameView implementation:
    282 
    283 gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
    284   return client_view_bounds_;
    285 }
    286 
    287 bool OpaqueBrowserFrameView::AlwaysUseNativeFrame() const {
    288   return frame_->AlwaysUseNativeFrame();
    289 }
    290 
    291 bool OpaqueBrowserFrameView::AlwaysUseCustomFrame() const {
    292   return true;
    293 }
    294 
    295 gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
    296     const gfx::Rect& client_bounds) const {
    297   int top_height = NonClientTopBorderHeight(false, false);
    298   int border_thickness = NonClientBorderThickness();
    299   return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
    300                    std::max(0, client_bounds.y() - top_height),
    301                    client_bounds.width() + (2 * border_thickness),
    302                    client_bounds.height() + top_height + border_thickness);
    303 }
    304 
    305 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
    306   if (!bounds().Contains(point))
    307     return HTNOWHERE;
    308 
    309   int frame_component =
    310       frame_->GetWindow()->client_view()->NonClientHitTest(point);
    311 
    312   // See if we're in the sysmenu region.  We still have to check the tabstrip
    313   // first so that clicks in a tab don't get treated as sysmenu clicks.
    314   gfx::Rect sysmenu_rect(IconBounds());
    315   // In maximized mode we extend the rect to the screen corner to take advantage
    316   // of Fitts' Law.
    317   if (frame_->GetWindow()->IsMaximized())
    318     sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
    319   sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
    320   if (sysmenu_rect.Contains(point))
    321     return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
    322 
    323   if (frame_component != HTNOWHERE)
    324     return frame_component;
    325 
    326   // Then see if the point is within any of the window controls.
    327   if (close_button_->IsVisible() &&
    328       close_button_->GetMirroredBounds().Contains(point))
    329     return HTCLOSE;
    330   if (restore_button_->IsVisible() &&
    331       restore_button_->GetMirroredBounds().Contains(point))
    332     return HTMAXBUTTON;
    333   if (maximize_button_->IsVisible() &&
    334       maximize_button_->GetMirroredBounds().Contains(point))
    335     return HTMAXBUTTON;
    336   if (minimize_button_->IsVisible() &&
    337       minimize_button_->GetMirroredBounds().Contains(point))
    338     return HTMINBUTTON;
    339 
    340   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
    341   if (!delegate) {
    342     LOG(WARNING) << "delegate is NULL, returning safe default.";
    343     return HTCAPTION;
    344   }
    345   int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
    346       NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
    347       delegate->CanResize());
    348   // Fall back to the caption if no other component matches.
    349   return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
    350 }
    351 
    352 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
    353                                            gfx::Path* window_mask) {
    354   DCHECK(window_mask);
    355 
    356   if (frame_->GetWindow()->IsMaximized() || frame_->GetWindow()->IsFullscreen())
    357     return;
    358 
    359   views::GetDefaultWindowMask(size, window_mask);
    360 }
    361 
    362 void OpaqueBrowserFrameView::EnableClose(bool enable) {
    363   close_button_->SetEnabled(enable);
    364 }
    365 
    366 void OpaqueBrowserFrameView::ResetWindowControls() {
    367   restore_button_->SetState(views::CustomButton::BS_NORMAL);
    368   minimize_button_->SetState(views::CustomButton::BS_NORMAL);
    369   maximize_button_->SetState(views::CustomButton::BS_NORMAL);
    370   // The close button isn't affected by this constraint.
    371 }
    372 
    373 void OpaqueBrowserFrameView::UpdateWindowIcon() {
    374   window_icon_->SchedulePaint();
    375 }
    376 
    377 ///////////////////////////////////////////////////////////////////////////////
    378 // OpaqueBrowserFrameView, views::View overrides:
    379 
    380 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
    381   views::Window* window = frame_->GetWindow();
    382   if (window->IsFullscreen())
    383     return;  // Nothing is visible, so don't bother to paint.
    384 
    385   if (window->IsMaximized())
    386     PaintMaximizedFrameBorder(canvas);
    387   else
    388     PaintRestoredFrameBorder(canvas);
    389   PaintTitleBar(canvas);
    390   if (browser_view_->IsToolbarVisible())
    391     PaintToolbarBackground(canvas);
    392   if (browser_view_->ShouldShowOffTheRecordAvatar())
    393     PaintOTRAvatar(canvas);
    394   if (!window->IsMaximized())
    395     PaintRestoredClientEdge(canvas);
    396 }
    397 
    398 void OpaqueBrowserFrameView::Layout() {
    399   LayoutWindowControls();
    400   LayoutTitleBar();
    401   LayoutOTRAvatar();
    402   client_view_bounds_ = CalculateClientAreaBounds(width(), height());
    403 }
    404 
    405 bool OpaqueBrowserFrameView::HitTest(const gfx::Point& l) const {
    406   // If the point is outside the bounds of the client area, claim it.
    407   bool in_nonclient = NonClientFrameView::HitTest(l);
    408   if (in_nonclient)
    409     return in_nonclient;
    410 
    411   // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
    412   if (!browser_view_->tabstrip())
    413     return false;
    414   gfx::Rect tabstrip_bounds(browser_view_->tabstrip()->bounds());
    415   gfx::Point tabstrip_origin(tabstrip_bounds.origin());
    416   View::ConvertPointToView(frame_->GetWindow()->client_view(),
    417                            this, &tabstrip_origin);
    418   tabstrip_bounds.set_origin(tabstrip_origin);
    419   if (browser_view_->UseVerticalTabs() ?
    420       (l.x() > tabstrip_bounds.right()) : (l.y() > tabstrip_bounds.bottom()))
    421     return false;
    422 
    423   // We convert from our parent's coordinates since we assume we fill its bounds
    424   // completely. We need to do this since we're not a parent of the tabstrip,
    425   // meaning ConvertPointToView would otherwise return something bogus.
    426   gfx::Point browser_view_point(l);
    427   View::ConvertPointToView(parent(), browser_view_, &browser_view_point);
    428   return browser_view_->IsPositionInWindowCaption(browser_view_point);
    429 }
    430 
    431 void OpaqueBrowserFrameView::GetAccessibleState(
    432     ui::AccessibleViewState* state) {
    433   state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
    434 }
    435 
    436 ///////////////////////////////////////////////////////////////////////////////
    437 // OpaqueBrowserFrameView, views::ButtonListener implementation:
    438 
    439 void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
    440                                            const views::Event& event) {
    441   views::Window* window = frame_->GetWindow();
    442   if (sender == minimize_button_)
    443     window->Minimize();
    444   else if (sender == maximize_button_)
    445     window->Maximize();
    446   else if (sender == restore_button_)
    447     window->Restore();
    448   else if (sender == close_button_)
    449     window->CloseWindow();
    450 }
    451 
    452 ///////////////////////////////////////////////////////////////////////////////
    453 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
    454 
    455 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
    456   // This function is queried during the creation of the window as the
    457   // TabIconView we host is initialized, so we need to NULL check the selected
    458   // TabContents because in this condition there is not yet a selected tab.
    459   TabContents* current_tab = browser_view_->GetSelectedTabContents();
    460   return current_tab ? current_tab->is_loading() : false;
    461 }
    462 
    463 SkBitmap OpaqueBrowserFrameView::GetFaviconForTabIconView() {
    464   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
    465   if (!delegate) {
    466     LOG(WARNING) << "delegate is NULL, returning safe default.";
    467     return SkBitmap();
    468   }
    469   return delegate->GetWindowIcon();
    470 }
    471 
    472 ///////////////////////////////////////////////////////////////////////////////
    473 // OpaqueBrowserFrameView, private:
    474 
    475 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
    476   views::Window* window = frame_->GetWindow();
    477   return (!restored && (window->IsMaximized() || window->IsFullscreen())) ?
    478       0 : kFrameBorderThickness;
    479 }
    480 
    481 int OpaqueBrowserFrameView::TopResizeHeight() const {
    482   return FrameBorderThickness(false) - kTopResizeAdjust;
    483 }
    484 
    485 int OpaqueBrowserFrameView::NonClientBorderThickness() const {
    486   // When we fill the screen, we don't show a client edge.
    487   views::Window* window = frame_->GetWindow();
    488   return FrameBorderThickness(false) +
    489       ((window->IsMaximized() || window->IsFullscreen()) ?
    490        0 : kClientEdgeThickness);
    491 }
    492 
    493 void OpaqueBrowserFrameView::ModifyMaximizedFramePainting(
    494     int* theme_offset, SkBitmap** left_corner, SkBitmap** right_corner) {
    495 }
    496 
    497 int OpaqueBrowserFrameView::CaptionButtonY(bool restored) const {
    498   // Maximized buttons start at window top so that even if their images aren't
    499   // drawn flush with the screen edge, they still obey Fitts' Law.
    500   return (!restored && frame_->GetWindow()->IsMaximized()) ?
    501       FrameBorderThickness(false) : kFrameShadowThickness;
    502 }
    503 
    504 int OpaqueBrowserFrameView::TitlebarBottomThickness(bool restored) const {
    505   return kTitlebarTopAndBottomEdgeThickness +
    506       ((!restored && frame_->GetWindow()->IsMaximized()) ?
    507       0 : kClientEdgeThickness);
    508 }
    509 
    510 int OpaqueBrowserFrameView::IconSize() const {
    511 #if defined(OS_WIN)
    512   // This metric scales up if either the titlebar height or the titlebar font
    513   // size are increased.
    514   return GetSystemMetrics(SM_CYSMICON);
    515 #else
    516   return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize);
    517 #endif
    518 }
    519 
    520 gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
    521   int size = IconSize();
    522   int frame_thickness = FrameBorderThickness(false);
    523   int y;
    524   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
    525   if (delegate && (delegate->ShouldShowWindowIcon() ||
    526                    delegate->ShouldShowWindowTitle())) {
    527     // Our frame border has a different "3D look" than Windows'.  Theirs has a
    528     // more complex gradient on the top that they push their icon/title below;
    529     // then the maximized window cuts this off and the icon/title are centered
    530     // in the remaining space.  Because the apparent shape of our border is
    531     // simpler, using the same positioning makes things look slightly uncentered
    532     // with restored windows, so when the window is restored, instead of
    533     // calculating the remaining space from below the frame border, we calculate
    534     // from below the 3D edge.
    535     int unavailable_px_at_top = frame_->GetWindow()->IsMaximized() ?
    536         frame_thickness : kTitlebarTopAndBottomEdgeThickness;
    537     // When the icon is shorter than the minimum space we reserve for the
    538     // caption button, we vertically center it.  We want to bias rounding to put
    539     // extra space above the icon, since the 3D edge (+ client edge, for
    540     // restored windows) below looks (to the eye) more like additional space
    541     // than does the 3D edge (or nothing at all, for maximized windows) above;
    542     // hence the +1.
    543     y = unavailable_px_at_top + (NonClientTopBorderHeight(false, false) -
    544         unavailable_px_at_top - size - TitlebarBottomThickness(false) + 1) / 2;
    545   } else {
    546     // For "browser mode" windows, we use the native positioning, which is just
    547     // below the top frame border.
    548     y = frame_thickness;
    549   }
    550   return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
    551 }
    552 
    553 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
    554   ui::ThemeProvider* tp = GetThemeProvider();
    555 
    556   SkBitmap* top_left_corner = tp->GetBitmapNamed(IDR_WINDOW_TOP_LEFT_CORNER);
    557   SkBitmap* top_right_corner =
    558       tp->GetBitmapNamed(IDR_WINDOW_TOP_RIGHT_CORNER);
    559   SkBitmap* top_edge = tp->GetBitmapNamed(IDR_WINDOW_TOP_CENTER);
    560   SkBitmap* right_edge = tp->GetBitmapNamed(IDR_WINDOW_RIGHT_SIDE);
    561   SkBitmap* left_edge = tp->GetBitmapNamed(IDR_WINDOW_LEFT_SIDE);
    562   SkBitmap* bottom_left_corner =
    563       tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER);
    564   SkBitmap* bottom_right_corner =
    565       tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER);
    566   SkBitmap* bottom_edge = tp->GetBitmapNamed(IDR_WINDOW_BOTTOM_CENTER);
    567 
    568   // Window frame mode and color.
    569   SkBitmap* theme_frame;
    570   SkColor frame_color;
    571   if (!browser_view_->IsBrowserTypeNormal()) {
    572     // Never theme app and popup windows.
    573     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    574     bool is_incognito = browser_view_->IsOffTheRecord();
    575     if (ShouldPaintAsActive()) {
    576       theme_frame = rb.GetBitmapNamed(is_incognito ?
    577                                       IDR_THEME_FRAME_INCOGNITO : IDR_FRAME);
    578       frame_color = is_incognito ?
    579                     ResourceBundle::frame_color_incognito :
    580                     ResourceBundle::frame_color;
    581     } else {
    582       theme_frame = rb.GetBitmapNamed(is_incognito ?
    583                                       IDR_THEME_FRAME_INCOGNITO_INACTIVE :
    584                                       IDR_THEME_FRAME_INACTIVE);
    585       frame_color = is_incognito ?
    586                     ResourceBundle::frame_color_incognito_inactive :
    587                     ResourceBundle::frame_color_inactive;
    588     }
    589   } else if (!browser_view_->IsOffTheRecord()) {
    590     if (ShouldPaintAsActive()) {
    591       theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME);
    592       frame_color = tp->GetColor(ThemeService::COLOR_FRAME);
    593     } else {
    594       theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INACTIVE);
    595       frame_color = tp->GetColor(ThemeService::COLOR_FRAME_INACTIVE);
    596     }
    597   } else if (ShouldPaintAsActive()) {
    598     theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INCOGNITO);
    599     frame_color = tp->GetColor(ThemeService::COLOR_FRAME_INCOGNITO);
    600   } else {
    601     theme_frame = tp->GetBitmapNamed(IDR_THEME_FRAME_INCOGNITO_INACTIVE);
    602     frame_color = tp->GetColor(
    603         ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE);
    604   }
    605 
    606   // Fill with the frame color first so we have a constant background for
    607   // areas not covered by the theme image.
    608   canvas->FillRectInt(frame_color, 0, 0, width(), theme_frame->height());
    609   // Now fill down the sides.
    610   canvas->FillRectInt(frame_color, 0, theme_frame->height(), left_edge->width(),
    611                       height() - theme_frame->height());
    612   canvas->FillRectInt(frame_color, width() - right_edge->width(),
    613                       theme_frame->height(), right_edge->width(),
    614                       height() - theme_frame->height());
    615   // Now fill the bottom area.
    616   canvas->FillRectInt(frame_color, left_edge->width(),
    617                       height() - bottom_edge->height(),
    618                       width() - left_edge->width() - right_edge->width(),
    619                       bottom_edge->height());
    620 
    621   // Draw the theme frame.
    622   canvas->TileImageInt(*theme_frame, 0, 0, width(), theme_frame->height());
    623 
    624   // Draw the theme frame overlay.
    625   if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
    626       browser_view_->IsBrowserTypeNormal() &&
    627       !browser_view_->IsOffTheRecord()) {
    628     canvas->DrawBitmapInt(*tp->GetBitmapNamed(ShouldPaintAsActive() ?
    629         IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE), 0, 0);
    630   }
    631 
    632   // Top.
    633   int top_left_height = std::min(top_left_corner->height(),
    634                                  height() - bottom_left_corner->height());
    635   canvas->DrawBitmapInt(*top_left_corner, 0, 0, top_left_corner->width(),
    636       top_left_height, 0, 0, top_left_corner->width(), top_left_height, false);
    637   canvas->TileImageInt(*top_edge, top_left_corner->width(), 0,
    638                        width() - top_right_corner->width(), top_edge->height());
    639   int top_right_height = std::min(top_right_corner->height(),
    640                                   height() - bottom_right_corner->height());
    641   canvas->DrawBitmapInt(*top_right_corner, 0, 0, top_right_corner->width(),
    642       top_right_height, width() - top_right_corner->width(), 0,
    643       top_right_corner->width(), top_right_height, false);
    644   // Note: When we don't have a toolbar, we need to draw some kind of bottom
    645   // edge here.  Because the App Window graphics we use for this have an
    646   // attached client edge and their sizing algorithm is a little involved, we do
    647   // all this in PaintRestoredClientEdge().
    648 
    649   // Right.
    650   canvas->TileImageInt(*right_edge, width() - right_edge->width(),
    651       top_right_height, right_edge->width(),
    652       height() - top_right_height - bottom_right_corner->height());
    653 
    654   // Bottom.
    655   canvas->DrawBitmapInt(*bottom_right_corner,
    656                         width() - bottom_right_corner->width(),
    657                         height() - bottom_right_corner->height());
    658   canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(),
    659       height() - bottom_edge->height(),
    660       width() - bottom_left_corner->width() - bottom_right_corner->width(),
    661       bottom_edge->height());
    662   canvas->DrawBitmapInt(*bottom_left_corner, 0,
    663                         height() - bottom_left_corner->height());
    664 
    665   // Left.
    666   canvas->TileImageInt(*left_edge, 0, top_left_height, left_edge->width(),
    667       height() - top_left_height - bottom_left_corner->height());
    668 }
    669 
    670 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
    671   ui::ThemeProvider* tp = GetThemeProvider();
    672   views::Window* window = frame_->GetWindow();
    673 
    674   // Window frame mode and color
    675   SkBitmap* theme_frame;
    676 
    677   // Allow customization of these attributes.
    678   SkBitmap* left = NULL;
    679   SkBitmap* right = NULL;
    680   int top_offset = 0;
    681   ModifyMaximizedFramePainting(&top_offset, &left, &right);
    682 
    683   // Never theme app and popup windows.
    684   if (!browser_view_->IsBrowserTypeNormal()) {
    685     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    686     theme_frame = rb.GetBitmapNamed(ShouldPaintAsActive() ?
    687         IDR_FRAME : IDR_FRAME_INACTIVE);
    688   } else if (!browser_view_->IsOffTheRecord()) {
    689     theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ?
    690         IDR_THEME_FRAME : IDR_THEME_FRAME_INACTIVE);
    691   } else {
    692     theme_frame = tp->GetBitmapNamed(ShouldPaintAsActive() ?
    693         IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME_INCOGNITO_INACTIVE);
    694   }
    695   // Draw the theme frame.  It must be aligned with the tabstrip as if we were
    696   // in restored mode.  Note that the top of the tabstrip is
    697   // kTabstripTopShadowThickness px off the top of the screen.
    698   int theme_background_y = -(GetHorizontalTabStripVerticalOffset(true) +
    699       kTabstripTopShadowThickness);
    700   int left_offset = 0, right_offset = 0;
    701 
    702   if (left || right) {
    703     // If we have either a left or right we should have both.
    704     DCHECK(left && right);
    705     left_offset = left->width();
    706     right_offset = right->width();
    707     canvas->DrawBitmapInt(*left, 0, 0);
    708     canvas->DrawBitmapInt(*right, width() - right_offset, 0);
    709   }
    710 
    711   canvas->TileImageInt(*theme_frame,
    712                        left_offset,
    713                        top_offset,
    714                        width() - (left_offset + right_offset),
    715                        theme_frame->height());
    716   // Draw the theme frame overlay
    717   if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
    718       browser_view_->IsBrowserTypeNormal()) {
    719     SkBitmap* theme_overlay = tp->GetBitmapNamed(ShouldPaintAsActive() ?
    720         IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
    721     canvas->DrawBitmapInt(*theme_overlay, 0, theme_background_y);
    722   }
    723 
    724   if (!browser_view_->IsToolbarVisible()) {
    725     // There's no toolbar to edge the frame border, so we need to draw a bottom
    726     // edge.  The graphic we use for this has a built in client edge, so we clip
    727     // it off the bottom.
    728     SkBitmap* top_center =
    729         tp->GetBitmapNamed(IDR_APP_TOP_CENTER);
    730     int edge_height = top_center->height() - kClientEdgeThickness;
    731     canvas->TileImageInt(*top_center, 0,
    732         window->client_view()->y() - edge_height, width(), edge_height);
    733   }
    734 }
    735 
    736 void OpaqueBrowserFrameView::PaintTitleBar(gfx::Canvas* canvas) {
    737   // The window icon is painted by the TabIconView.
    738   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
    739   if (!delegate) {
    740     LOG(WARNING) << "delegate is NULL";
    741     return;
    742   }
    743   if (delegate->ShouldShowWindowTitle()) {
    744     canvas->DrawStringInt(WideToUTF16Hack(delegate->GetWindowTitle()),
    745                           BrowserFrame::GetTitleFont(),
    746         SK_ColorWHITE, GetMirroredXForRect(title_bounds_),
    747         title_bounds_.y(), title_bounds_.width(), title_bounds_.height());
    748     /* TODO(pkasting):  If this window is active, we should also draw a drop
    749      * shadow on the title.  This is tricky, because we don't want to hardcode a
    750      * shadow color (since we want to work with various themes), but we can't
    751      * alpha-blend either (since the Windows text APIs don't really do this).
    752      * So we'd need to sample the background color at the right location and
    753      * synthesize a good shadow color. */
    754   }
    755 }
    756 
    757 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
    758   gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds());
    759   if (toolbar_bounds.IsEmpty())
    760     return;
    761   gfx::Point toolbar_origin(toolbar_bounds.origin());
    762   ConvertPointToView(browser_view_, this, &toolbar_origin);
    763   toolbar_bounds.set_origin(toolbar_origin);
    764 
    765   int x = toolbar_bounds.x();
    766   int w = toolbar_bounds.width();
    767   int y, h;
    768   if (browser_view_->UseVerticalTabs()) {
    769     gfx::Point tabstrip_origin(browser_view_->tabstrip()->bounds().origin());
    770     ConvertPointToView(browser_view_, this, &tabstrip_origin);
    771     y = tabstrip_origin.y() - kVerticalTabBorderInset;
    772     h = toolbar_bounds.bottom() - y;
    773   } else {
    774     y = toolbar_bounds.y();
    775     h = toolbar_bounds.bottom();
    776   }
    777 
    778   // Gross hack: We split the toolbar images into two pieces, since sometimes
    779   // (popup mode) the toolbar isn't tall enough to show the whole image.  The
    780   // split happens between the top shadow section and the bottom gradient
    781   // section so that we never break the gradient.
    782   int split_point = kFrameShadowThickness * 2;
    783   int bottom_y = y + split_point;
    784   ui::ThemeProvider* tp = GetThemeProvider();
    785   SkBitmap* toolbar_left = tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER);
    786   int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
    787 
    788   // Split our canvas out so we can mask out the corners of the toolbar
    789   // without masking out the frame.
    790   canvas->SaveLayerAlpha(
    791       255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
    792                      h));
    793   canvas->AsCanvasSkia()->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode);
    794 
    795   SkColor theme_toolbar_color =
    796       tp->GetColor(ThemeService::COLOR_TOOLBAR);
    797   canvas->FillRectInt(theme_toolbar_color, x, bottom_y, w, bottom_edge_height);
    798 
    799   // Tile the toolbar image starting at the frame edge on the left and where the
    800   // horizontal tabstrip is (or would be) on the top.
    801   SkBitmap* theme_toolbar = tp->GetBitmapNamed(IDR_THEME_TOOLBAR);
    802   canvas->TileImageInt(*theme_toolbar, x,
    803                        bottom_y - GetHorizontalTabStripVerticalOffset(false), x,
    804                        bottom_y, w, theme_toolbar->height());
    805 
    806   // Draw rounded corners for the tab.
    807   SkBitmap* toolbar_left_mask =
    808       tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
    809   SkBitmap* toolbar_right_mask =
    810       tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
    811 
    812   // We mask out the corners by using the DestinationIn transfer mode,
    813   // which keeps the RGB pixels from the destination and the alpha from
    814   // the source.
    815   SkPaint paint;
    816   paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
    817 
    818   // Mask the left edge.
    819   int left_x = x - kContentEdgeShadowThickness;
    820   canvas->DrawBitmapInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
    821                         split_point, left_x, y, toolbar_left_mask->width(),
    822                         split_point, false, paint);
    823   canvas->DrawBitmapInt(*toolbar_left_mask, 0,
    824       toolbar_left_mask->height() - bottom_edge_height,
    825       toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
    826       toolbar_left_mask->width(), bottom_edge_height, false, paint);
    827 
    828   // Mask the right edge.
    829   int right_x =
    830       x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
    831   canvas->DrawBitmapInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
    832                         split_point, right_x, y, toolbar_right_mask->width(),
    833                         split_point, false, paint);
    834   canvas->DrawBitmapInt(*toolbar_right_mask, 0,
    835       toolbar_right_mask->height() - bottom_edge_height,
    836       toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
    837       toolbar_right_mask->width(), bottom_edge_height, false, paint);
    838   canvas->Restore();
    839 
    840   canvas->DrawBitmapInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
    841                         left_x, y, toolbar_left->width(), split_point, false);
    842   canvas->DrawBitmapInt(*toolbar_left, 0,
    843       toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
    844       bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
    845       bottom_edge_height, false);
    846 
    847   SkBitmap* toolbar_center =
    848       tp->GetBitmapNamed(IDR_CONTENT_TOP_CENTER);
    849   canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
    850       y, right_x - (left_x + toolbar_left->width()),
    851       split_point);
    852 
    853   SkBitmap* toolbar_right = tp->GetBitmapNamed(IDR_CONTENT_TOP_RIGHT_CORNER);
    854   canvas->DrawBitmapInt(*toolbar_right, 0, 0, toolbar_right->width(),
    855       split_point, right_x, y, toolbar_right->width(), split_point, false);
    856   canvas->DrawBitmapInt(*toolbar_right, 0,
    857       toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
    858       bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
    859       bottom_edge_height, false);
    860 
    861   // Draw the content/toolbar separator.
    862   canvas->FillRectInt(ResourceBundle::toolbar_separator_color,
    863       x + kClientEdgeThickness, toolbar_bounds.bottom() - kClientEdgeThickness,
    864       w - (2 * kClientEdgeThickness), kClientEdgeThickness);
    865 }
    866 
    867 void OpaqueBrowserFrameView::PaintOTRAvatar(gfx::Canvas* canvas) {
    868   // In RTL mode, the avatar icon should be looking the opposite direction.
    869   canvas->Save();
    870   if (base::i18n::IsRTL()) {
    871     canvas->TranslateInt(width(), 0);
    872     canvas->ScaleInt(-1, 1);
    873   }
    874 
    875   SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon();
    876   int w = otr_avatar_bounds_.width();
    877   int h = otr_avatar_bounds_.height();
    878   canvas->DrawBitmapInt(otr_avatar_icon, 0,
    879       // Bias the rounding to select a region that's lower rather than higher,
    880       // as the shadows at the image top mean the apparent center is below the
    881       // real center.
    882       ((otr_avatar_icon.height() - otr_avatar_bounds_.height()) + 1) / 2, w, h,
    883       otr_avatar_bounds_.x(), otr_avatar_bounds_.y(), w, h, false);
    884 
    885   canvas->Restore();
    886 }
    887 
    888 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
    889   ui::ThemeProvider* tp = GetThemeProvider();
    890   int client_area_top = frame_->GetWindow()->client_view()->y();
    891   int image_top = client_area_top;
    892 
    893   gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height());
    894   SkColor toolbar_color = tp->GetColor(ThemeService::COLOR_TOOLBAR);
    895 
    896   if (browser_view_->IsToolbarVisible()) {
    897     // The client edge images always start below the toolbar corner images.  The
    898     // client edge filled rects start there or at the bottom of the tooolbar,
    899     // whichever is shorter.
    900     gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds());
    901     image_top += toolbar_bounds.y() +
    902         tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height();
    903     client_area_top = std::min(image_top,
    904         client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
    905     if (browser_view_->UseVerticalTabs()) {
    906       client_area_top -= kVerticalTabBorderInset;
    907       image_top -= kVerticalTabBorderInset;
    908     }
    909   } else if (!browser_view_->IsTabStripVisible()) {
    910     // The toolbar isn't going to draw a client edge for us, so draw one
    911     // ourselves.
    912     SkBitmap* top_left = tp->GetBitmapNamed(IDR_APP_TOP_LEFT);
    913     SkBitmap* top_center = tp->GetBitmapNamed(IDR_APP_TOP_CENTER);
    914     SkBitmap* top_right = tp->GetBitmapNamed(IDR_APP_TOP_RIGHT);
    915     int top_edge_y = client_area_top - top_center->height();
    916     int height = client_area_top - top_edge_y;
    917 
    918     canvas->DrawBitmapInt(*top_left, 0, 0, top_left->width(), height,
    919         client_area_bounds.x() - top_left->width(), top_edge_y,
    920         top_left->width(), height, false);
    921     canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
    922       client_area_bounds.width(), std::min(height, top_center->height()));
    923     canvas->DrawBitmapInt(*top_right, 0, 0, top_right->width(), height,
    924         client_area_bounds.right(), top_edge_y,
    925         top_right->width(), height, false);
    926 
    927     // Draw the toolbar color across the top edge.
    928     canvas->FillRectInt(toolbar_color,
    929         client_area_bounds.x() - kClientEdgeThickness,
    930         client_area_top - kClientEdgeThickness,
    931         client_area_bounds.width() + (2 * kClientEdgeThickness),
    932         kClientEdgeThickness);
    933   }
    934 
    935   int client_area_bottom =
    936       std::max(client_area_top, height() - NonClientBorderThickness());
    937   int image_height = client_area_bottom - image_top;
    938 
    939   // Draw the client edge images.
    940   // Draw the client edge images.
    941   SkBitmap* right = tp->GetBitmapNamed(IDR_CONTENT_RIGHT_SIDE);
    942   canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
    943                        right->width(), image_height);
    944   canvas->DrawBitmapInt(
    945       *tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
    946       client_area_bounds.right(), client_area_bottom);
    947   SkBitmap* bottom = tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_CENTER);
    948   canvas->TileImageInt(*bottom, client_area_bounds.x(),
    949       client_area_bottom, client_area_bounds.width(),
    950       bottom->height());
    951   SkBitmap* bottom_left =
    952       tp->GetBitmapNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
    953   canvas->DrawBitmapInt(*bottom_left,
    954       client_area_bounds.x() - bottom_left->width(), client_area_bottom);
    955   SkBitmap* left = tp->GetBitmapNamed(IDR_CONTENT_LEFT_SIDE);
    956   canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
    957                        image_top, left->width(), image_height);
    958 
    959   // Draw the toolbar color so that the client edges show the right color even
    960   // where not covered by the toolbar image.  NOTE: We do this after drawing the
    961   // images because the images are meant to alpha-blend atop the frame whereas
    962   // these rects are meant to be fully opaque, without anything overlaid.
    963   canvas->FillRectInt(toolbar_color,
    964       client_area_bounds.x() - kClientEdgeThickness, client_area_top,
    965       kClientEdgeThickness,
    966       client_area_bottom + kClientEdgeThickness - client_area_top);
    967   canvas->FillRectInt(toolbar_color, client_area_bounds.x(), client_area_bottom,
    968                       client_area_bounds.width(), kClientEdgeThickness);
    969   canvas->FillRectInt(toolbar_color, client_area_bounds.right(),
    970       client_area_top, kClientEdgeThickness,
    971       client_area_bottom + kClientEdgeThickness - client_area_top);
    972 }
    973 
    974 void OpaqueBrowserFrameView::LayoutWindowControls() {
    975   bool is_maximized = frame_->GetWindow()->IsMaximized();
    976   close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
    977                                    views::ImageButton::ALIGN_BOTTOM);
    978   int caption_y = CaptionButtonY(false);
    979   // There should always be the same number of non-shadow pixels visible to the
    980   // side of the caption buttons.  In maximized mode we extend the rightmost
    981   // button to the screen corner to obey Fitts' Law.
    982   int right_extra_width = is_maximized ?
    983       (kFrameBorderThickness - kFrameShadowThickness) : 0;
    984   gfx::Size close_button_size = close_button_->GetPreferredSize();
    985   close_button_->SetBounds(width() - FrameBorderThickness(false) -
    986       right_extra_width - close_button_size.width(), caption_y,
    987       close_button_size.width() + right_extra_width,
    988       close_button_size.height());
    989 
    990 #if defined(OS_CHROMEOS)
    991   // LayoutWindowControls could be triggered from WindowGtk::UpdateWindowTitle,
    992   // which could happen when user navigates in fullscreen mode. And because
    993   // BrowserFrameChromeos::IsMaximized return false for fullscreen mode, we
    994   // explicitly test fullscreen mode here and make it use the same code path
    995   // as maximized mode.
    996   // TODO(oshima): Optimize the relayout logic to defer the frame view's
    997   // relayout until it is necessary, i.e when it becomes visible.
    998   if (is_maximized || frame_->GetWindow()->IsFullscreen()) {
    999     minimize_button_->SetVisible(false);
   1000     restore_button_->SetVisible(false);
   1001     maximize_button_->SetVisible(false);
   1002 
   1003     if (browser_view_->browser()->type() == Browser::TYPE_DEVTOOLS) {
   1004       close_button_->SetVisible(true);
   1005       minimize_button_->SetBounds(close_button_->bounds().x(), 0, 0, 0);
   1006     } else {
   1007       close_button_->SetVisible(false);
   1008       // Set the bounds of the minimize button so that we don't have to change
   1009       // other places that rely on the bounds. Put it slightly to the right
   1010       // of the edge of the view, so that when we remove the spacing it lines
   1011       // up with the edge.
   1012       minimize_button_->SetBounds(width() - FrameBorderThickness(false) +
   1013           kNewTabCaptionMaximizedSpacing, 0, 0, 0);
   1014     }
   1015 
   1016     return;
   1017   } else {
   1018     close_button_->SetVisible(true);
   1019   }
   1020 #endif
   1021 
   1022   // When the window is restored, we show a maximized button; otherwise, we show
   1023   // a restore button.
   1024   bool is_restored = !is_maximized && !frame_->GetWindow()->IsMinimized();
   1025   views::ImageButton* invisible_button = is_restored ?
   1026       restore_button_ : maximize_button_;
   1027   invisible_button->SetVisible(false);
   1028 
   1029   views::ImageButton* visible_button = is_restored ?
   1030       maximize_button_ : restore_button_;
   1031   visible_button->SetVisible(true);
   1032   visible_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
   1033                                     views::ImageButton::ALIGN_BOTTOM);
   1034   gfx::Size visible_button_size = visible_button->GetPreferredSize();
   1035   visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
   1036                             caption_y, visible_button_size.width(),
   1037                             visible_button_size.height());
   1038 
   1039   minimize_button_->SetVisible(true);
   1040   minimize_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
   1041                                       views::ImageButton::ALIGN_BOTTOM);
   1042   gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
   1043   minimize_button_->SetBounds(
   1044       visible_button->x() - minimize_button_size.width(), caption_y,
   1045       minimize_button_size.width(),
   1046       minimize_button_size.height());
   1047 }
   1048 
   1049 void OpaqueBrowserFrameView::LayoutTitleBar() {
   1050   // The window title is based on the calculated icon position, even when there
   1051   // is no icon.
   1052   gfx::Rect icon_bounds(IconBounds());
   1053   views::WindowDelegate* delegate = frame_->GetWindow()->window_delegate();
   1054   if (delegate && delegate->ShouldShowWindowIcon())
   1055     window_icon_->SetBoundsRect(icon_bounds);
   1056 
   1057   // Size the title, if visible.
   1058   if (delegate && delegate->ShouldShowWindowTitle()) {
   1059     int title_x = delegate->ShouldShowWindowIcon() ?
   1060         icon_bounds.right() + kIconTitleSpacing : icon_bounds.x();
   1061     int title_height = BrowserFrame::GetTitleFont().GetHeight();
   1062     // We bias the title position so that when the difference between the icon
   1063     // and title heights is odd, the extra pixel of the title is above the
   1064     // vertical midline rather than below.  This compensates for how the icon is
   1065     // already biased downwards (see IconBounds()) and helps prevent descenders
   1066     // on the title from overlapping the 3D edge at the bottom of the titlebar.
   1067     title_bounds_.SetRect(title_x,
   1068         icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2),
   1069         std::max(0, minimize_button_->x() - kTitleLogoSpacing - title_x),
   1070         title_height);
   1071   }
   1072 }
   1073 
   1074 void OpaqueBrowserFrameView::LayoutOTRAvatar() {
   1075   SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon();
   1076   int otr_bottom, otr_restored_y;
   1077   if (browser_view_->UseVerticalTabs()) {
   1078     otr_bottom = NonClientTopBorderHeight(false, false) - kOTRBottomSpacing;
   1079     otr_restored_y = kFrameShadowThickness;
   1080   } else {
   1081     otr_bottom = GetHorizontalTabStripVerticalOffset(false) +
   1082         browser_view_->GetTabStripHeight() - kOTRBottomSpacing;
   1083     otr_restored_y = otr_bottom - otr_avatar_icon.height();
   1084   }
   1085   int otr_y = frame_->GetWindow()->IsMaximized() ?
   1086       (NonClientTopBorderHeight(false, true) + kTabstripTopShadowThickness) :
   1087       otr_restored_y;
   1088   otr_avatar_bounds_.SetRect(NonClientBorderThickness() + kOTRSideSpacing,
   1089       otr_y, otr_avatar_icon.width(),
   1090       browser_view_->ShouldShowOffTheRecordAvatar() ? (otr_bottom - otr_y) : 0);
   1091 }
   1092 
   1093 gfx::Rect OpaqueBrowserFrameView::CalculateClientAreaBounds(int width,
   1094                                                             int height) const {
   1095   int top_height = NonClientTopBorderHeight(false, false);
   1096   int border_thickness = NonClientBorderThickness();
   1097   return gfx::Rect(border_thickness, top_height,
   1098                    std::max(0, width - (2 * border_thickness)),
   1099                    std::max(0, height - GetReservedHeight() -
   1100                        top_height - border_thickness));
   1101 }
   1102