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