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/browser_non_client_frame_view_ash.h"
      6 
      7 #include "ash/shell_delegate.h"
      8 #include "ash/wm/frame_painter.h"
      9 #include "ash/wm/workspace/frame_maximize_button.h"
     10 #include "chrome/browser/themes/theme_properties.h"
     11 #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
     12 #include "chrome/browser/ui/browser.h"
     13 #include "chrome/browser/ui/immersive_fullscreen_configuration.h"
     14 #include "chrome/browser/ui/views/avatar_menu_button.h"
     15 #include "chrome/browser/ui/views/frame/browser_frame.h"
     16 #include "chrome/browser/ui/views/frame/browser_view.h"
     17 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
     18 #include "chrome/browser/ui/views/tab_icon_view.h"
     19 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "grit/ash_resources.h"
     22 #include "grit/generated_resources.h"  // Accessibility names
     23 #include "grit/theme_resources.h"
     24 #include "ui/aura/client/aura_constants.h"
     25 #include "ui/aura/window.h"
     26 #include "ui/base/accessibility/accessible_view_state.h"
     27 #include "ui/base/hit_test.h"
     28 #include "ui/base/l10n/l10n_util.h"
     29 #include "ui/base/layout.h"
     30 #include "ui/base/resource/resource_bundle.h"
     31 #include "ui/base/theme_provider.h"
     32 #include "ui/compositor/layer_animator.h"
     33 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     34 #include "ui/gfx/canvas.h"
     35 #include "ui/gfx/image/image_skia.h"
     36 #include "ui/views/controls/button/image_button.h"
     37 #include "ui/views/controls/label.h"
     38 #include "ui/views/layout/layout_constants.h"
     39 #include "ui/views/widget/widget.h"
     40 #include "ui/views/widget/widget_delegate.h"
     41 
     42 namespace {
     43 
     44 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
     45 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
     46 // user).
     47 const int kAvatarBottomSpacing = 2;
     48 // There are 2 px on each side of the avatar (between the frame border and
     49 // it on the left, and between it and the tabstrip on the right).
     50 const int kAvatarSideSpacing = 2;
     51 // Space between left edge of window and tabstrip.
     52 const int kTabstripLeftSpacing = 0;
     53 // Space between right edge of tabstrip and maximize button.
     54 const int kTabstripRightSpacing = 10;
     55 // Height of the shadow of the content area, at the top of the toolbar.
     56 const int kContentShadowHeight = 1;
     57 // Space between top of window and top of tabstrip for tall headers, such as
     58 // for restored windows, apps, etc.
     59 const int kTabstripTopSpacingTall = 7;
     60 // Space between top of window and top of tabstrip for short headers, such as
     61 // for maximized windows, pop-ups, etc.
     62 const int kTabstripTopSpacingShort = 0;
     63 // Height of the shadow in the tab image, used to ensure clicks in the shadow
     64 // area still drag restored windows.  This keeps the clickable area large enough
     65 // to hit easily.
     66 const int kTabShadowHeight = 4;
     67 
     68 }  // namespace
     69 
     70 ///////////////////////////////////////////////////////////////////////////////
     71 // BrowserNonClientFrameViewAsh, public:
     72 
     73 // static
     74 const char BrowserNonClientFrameViewAsh::kViewClassName[] =
     75     "BrowserNonClientFrameViewAsh";
     76 
     77 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
     78     BrowserFrame* frame, BrowserView* browser_view)
     79     : BrowserNonClientFrameView(frame, browser_view),
     80       size_button_(NULL),
     81       close_button_(NULL),
     82       window_icon_(NULL),
     83       frame_painter_(new ash::FramePainter),
     84       size_button_minimizes_(false) {
     85 }
     86 
     87 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
     88 }
     89 
     90 void BrowserNonClientFrameViewAsh::Init() {
     91   // Panels only minimize.
     92   ash::FramePainter::SizeButtonBehavior size_button_behavior;
     93   size_button_ = new ash::FrameMaximizeButton(this, this);
     94   size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES;
     95   size_button_->SetAccessibleName(
     96       l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
     97   AddChildView(size_button_);
     98   close_button_ = new views::ImageButton(this);
     99   close_button_->SetAccessibleName(
    100       l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
    101   AddChildView(close_button_);
    102 
    103   // Initializing the TabIconView is expensive, so only do it if we need to.
    104   if (browser_view()->ShouldShowWindowIcon()) {
    105     window_icon_ = new TabIconView(this);
    106     window_icon_->set_is_light(true);
    107     AddChildView(window_icon_);
    108     window_icon_->Update();
    109   }
    110 
    111   // Create incognito icon if necessary.
    112   UpdateAvatarInfo();
    113 
    114   // Frame painter handles layout of these buttons.
    115   frame_painter_->Init(frame(), window_icon_, size_button_, close_button_,
    116                        size_button_behavior);
    117 }
    118 
    119 ///////////////////////////////////////////////////////////////////////////////
    120 // BrowserNonClientFrameView overrides:
    121 
    122 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
    123     views::View* tabstrip) const {
    124   if (!tabstrip)
    125     return gfx::Rect();
    126 
    127   // When the tab strip is painted in the immersive fullscreen light bar style,
    128   // the caption buttons and the avatar button are not visible. However, their
    129   // bounds are still used to compute the tab strip bounds so that the tabs have
    130   // the same horizontal position when the tab strip is painted in the immersive
    131   // light bar style as when the top-of-window views are revealed.
    132   TabStripInsets insets(GetTabStripInsets(false));
    133   return gfx::Rect(insets.left, insets.top,
    134                    std::max(0, width() - insets.left - insets.right),
    135                    tabstrip->GetPreferredSize().height());
    136 }
    137 
    138 BrowserNonClientFrameView::TabStripInsets
    139 BrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const {
    140   int left = avatar_button() ? kAvatarSideSpacing +
    141       browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
    142       kTabstripLeftSpacing;
    143   int right = frame_painter_->GetRightInset() + kTabstripRightSpacing;
    144   return TabStripInsets(NonClientTopBorderHeight(force_restored), left, right);
    145 }
    146 
    147 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
    148   return frame_painter_->GetThemeBackgroundXInset();
    149 }
    150 
    151 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
    152   if (window_icon_)
    153     window_icon_->Update();
    154 }
    155 
    156 ///////////////////////////////////////////////////////////////////////////////
    157 // views::NonClientFrameView overrides:
    158 
    159 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
    160   int top_height = NonClientTopBorderHeight(false);
    161   return frame_painter_->GetBoundsForClientView(top_height, bounds());
    162 }
    163 
    164 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
    165     const gfx::Rect& client_bounds) const {
    166   int top_height = NonClientTopBorderHeight(false);
    167   return frame_painter_->GetWindowBoundsForClientBounds(top_height,
    168                                                         client_bounds);
    169 }
    170 
    171 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
    172   int hit_test = frame_painter_->NonClientHitTest(this, point);
    173   // When the window is restored we want a large click target above the tabs
    174   // to drag the window, so redirect clicks in the tab's shadow to caption.
    175   if (hit_test == HTCLIENT &&
    176       !(frame()->IsMaximized() || frame()->IsFullscreen())) {
    177     // Convert point to client coordinates.
    178     gfx::Point client_point(point);
    179     View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
    180     // Report hits in shadow at top of tabstrip as caption.
    181     gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
    182     if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
    183       hit_test = HTCAPTION;
    184   }
    185   return hit_test;
    186 }
    187 
    188 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
    189                                                   gfx::Path* window_mask) {
    190   // Aura does not use window masks.
    191 }
    192 
    193 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
    194   // Hide the caption buttons in immersive fullscreen when the tab light bar
    195   // is visible because it's confusing when the user hovers or clicks in the
    196   // top-right of the screen and hits one.
    197   bool button_visibility = !UseImmersiveLightbarHeaderStyle();
    198   size_button_->SetVisible(button_visibility);
    199   close_button_->SetVisible(button_visibility);
    200 
    201   size_button_->SetState(views::CustomButton::STATE_NORMAL);
    202   // The close button isn't affected by this constraint.
    203 }
    204 
    205 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
    206   if (window_icon_)
    207     window_icon_->SchedulePaint();
    208 }
    209 
    210 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
    211   if (!frame()->IsFullscreen())
    212     frame_painter_->SchedulePaintForTitle(BrowserFrame::GetTitleFont());
    213 }
    214 
    215 ///////////////////////////////////////////////////////////////////////////////
    216 // views::View overrides:
    217 
    218 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
    219   if (!ShouldPaint())
    220     return;
    221 
    222   if (UseImmersiveLightbarHeaderStyle()) {
    223     PaintImmersiveLightbarStyleHeader(canvas);
    224     return;
    225   }
    226 
    227   // The primary header image changes based on window activation state and
    228   // theme, so we look it up for each paint.
    229   int theme_frame_image_id = GetThemeFrameImageId();
    230   int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId();
    231 
    232   ui::ThemeProvider* theme_provider = GetThemeProvider();
    233   ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO;
    234   if (theme_provider->HasCustomImage(theme_frame_image_id) ||
    235       (theme_frame_overlay_image_id != 0 &&
    236        theme_provider->HasCustomImage(theme_frame_overlay_image_id))) {
    237     header_themed = ash::FramePainter::THEMED_YES;
    238   }
    239 
    240   if (frame_painter_->ShouldUseMinimalHeaderStyle(header_themed))
    241     theme_frame_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
    242 
    243   frame_painter_->PaintHeader(
    244       this,
    245       canvas,
    246       ShouldPaintAsActive() ?
    247           ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE,
    248       theme_frame_image_id,
    249       theme_frame_overlay_image_id);
    250   if (browser_view()->ShouldShowWindowTitle())
    251     frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont());
    252   if (browser_view()->IsToolbarVisible())
    253     PaintToolbarBackground(canvas);
    254   else
    255     PaintContentEdge(canvas);
    256 }
    257 
    258 void BrowserNonClientFrameViewAsh::Layout() {
    259   frame_painter_->LayoutHeader(this, UseShortHeader());
    260   if (avatar_button())
    261     LayoutAvatar();
    262   BrowserNonClientFrameView::Layout();
    263 }
    264 
    265 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
    266   return kViewClassName;
    267 }
    268 
    269 bool BrowserNonClientFrameViewAsh::HitTestRect(const gfx::Rect& rect) const {
    270   if (!views::View::HitTestRect(rect)) {
    271     // |rect| is outside BrowserNonClientFrameViewAsh's bounds.
    272     return false;
    273   }
    274   // If the rect is outside the bounds of the client area, claim it.
    275   // TODO(tdanderson): Implement View::ConvertRectToTarget().
    276   gfx::Point rect_in_client_view_coords_origin(rect.origin());
    277   View::ConvertPointToTarget(this, frame()->client_view(),
    278       &rect_in_client_view_coords_origin);
    279   gfx::Rect rect_in_client_view_coords(
    280       rect_in_client_view_coords_origin, rect.size());
    281   if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords))
    282     return true;
    283 
    284   // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
    285   // a non-tab portion.
    286   TabStrip* tabstrip = browser_view()->tabstrip();
    287   if (!tabstrip || !browser_view()->IsTabStripVisible())
    288     return false;
    289 
    290   gfx::Point rect_in_tabstrip_coords_origin(rect.origin());
    291   View::ConvertPointToTarget(this, tabstrip,
    292       &rect_in_tabstrip_coords_origin);
    293   gfx::Rect rect_in_tabstrip_coords(rect_in_tabstrip_coords_origin,
    294       rect.size());
    295 
    296   if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) {
    297     // |rect| is below the tabstrip.
    298     return false;
    299   }
    300 
    301   if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) {
    302     // Claim |rect| if it is in a non-tab portion of the tabstrip.
    303     // TODO(tdanderson): Pass |rect_in_tabstrip_coords| instead of its center
    304     // point to TabStrip::IsPositionInWindowCaption() once
    305     // GetEventHandlerForRect() is implemented.
    306     return tabstrip->IsPositionInWindowCaption(
    307         rect_in_tabstrip_coords.CenterPoint());
    308   }
    309 
    310   // We claim |rect| because it is above the bottom of the tabstrip, but
    311   // not in the tabstrip. In particular, the window controls are right of
    312   // the tabstrip.
    313   return true;
    314 }
    315 
    316 void BrowserNonClientFrameViewAsh::GetAccessibleState(
    317     ui::AccessibleViewState* state) {
    318   state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
    319 }
    320 
    321 gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() {
    322   return frame_painter_->GetMinimumSize(this);
    323 }
    324 
    325 void BrowserNonClientFrameViewAsh::OnThemeChanged() {
    326   BrowserNonClientFrameView::OnThemeChanged();
    327   frame_painter_->OnThemeChanged();
    328 }
    329 
    330 ///////////////////////////////////////////////////////////////////////////////
    331 // views::ButtonListener overrides:
    332 
    333 void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
    334                                                  const ui::Event& event) {
    335   // When shift-clicking slow down animations for visual debugging.
    336   // We used to do this via an event filter that looked for the shift key being
    337   // pressed but this interfered with several normal keyboard shortcuts.
    338   scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
    339   if (event.IsShiftDown()) {
    340     slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
    341         ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
    342   }
    343 
    344   ash::UserMetricsAction action =
    345       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
    346 
    347   if (sender == size_button_) {
    348     // The maximize button may move out from under the cursor.
    349     ResetWindowControls();
    350     if (size_button_minimizes_) {
    351       frame()->Minimize();
    352       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
    353     } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode.
    354       frame()->SetFullscreen(false);
    355       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
    356     } else if (frame()->IsMaximized()) {
    357       frame()->Restore();
    358       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
    359     } else {
    360       frame()->Maximize();
    361     }
    362     // |this| may be deleted - some windows delete their frames on maximize.
    363   } else if (sender == close_button_) {
    364     frame()->Close();
    365     action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
    366   } else {
    367     return;
    368   }
    369   ChromeShellDelegate::instance()->RecordUserMetricsAction(action);
    370 }
    371 
    372 ///////////////////////////////////////////////////////////////////////////////
    373 // chrome::TabIconViewModel overrides:
    374 
    375 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
    376   // This function is queried during the creation of the window as the
    377   // TabIconView we host is initialized, so we need to NULL check the selected
    378   // WebContents because in this condition there is not yet a selected tab.
    379   content::WebContents* current_tab = browser_view()->GetActiveWebContents();
    380   return current_tab ? current_tab->IsLoading() : false;
    381 }
    382 
    383 gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
    384   views::WidgetDelegate* delegate = frame()->widget_delegate();
    385   if (!delegate)
    386     return gfx::ImageSkia();
    387   return delegate->GetWindowIcon();
    388 }
    389 
    390 ///////////////////////////////////////////////////////////////////////////////
    391 // BrowserNonClientFrameViewAsh, private:
    392 
    393 
    394 int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight(
    395     bool force_restored) const {
    396   if (force_restored)
    397     return kTabstripTopSpacingTall;
    398   if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
    399     return 0;
    400   // Windows with tab strips need a smaller non-client area.
    401   if (browser_view()->IsTabStripVisible()) {
    402     if (UseShortHeader())
    403       return kTabstripTopSpacingShort;
    404     return kTabstripTopSpacingTall;
    405   }
    406   // For windows without a tab strip (popups, etc.) ensure we have enough space
    407   // to see the window caption buttons.
    408   return close_button_->bounds().bottom() - kContentShadowHeight;
    409 }
    410 
    411 bool BrowserNonClientFrameViewAsh::UseShortHeader() const {
    412   // Restored browser -> tall header
    413   // Maximized browser -> short header
    414   // Fullscreen browser, no immersive reveal -> hidden or super short light bar
    415   // Fullscreen browser, immersive reveal -> short header
    416   // Popup&App window -> tall header
    417   // Panel -> short header
    418   // Dialogs use short header and are handled via CustomFrameViewAsh.
    419   Browser* browser = browser_view()->browser();
    420   switch (browser->type()) {
    421     case Browser::TYPE_TABBED:
    422       return frame()->IsMaximized() || frame()->IsFullscreen();
    423     case Browser::TYPE_POPUP:
    424       return false;
    425     default:
    426       NOTREACHED();
    427       return false;
    428   }
    429 }
    430 
    431 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
    432   ImmersiveModeController* immersive_controller =
    433       browser_view()->immersive_mode_controller();
    434   return immersive_controller->IsEnabled() &&
    435       !immersive_controller->IsRevealed() &&
    436       !immersive_controller->ShouldHideTabIndicators();
    437 }
    438 
    439 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
    440   DCHECK(avatar_button());
    441   gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
    442 
    443   int avatar_bottom = GetTabStripInsets(false).top +
    444       browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
    445   int avatar_restored_y = avatar_bottom - incognito_icon.height();
    446   int avatar_y = (frame()->IsMaximized() || frame()->IsFullscreen()) ?
    447       NonClientTopBorderHeight(false) + kContentShadowHeight :
    448       avatar_restored_y;
    449 
    450   // Hide the incognito icon in immersive fullscreen when the tab light bar is
    451   // visible because the header is too short for the icognito icon to be
    452   // recognizable.
    453   bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
    454   int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
    455 
    456   gfx::Rect avatar_bounds(kAvatarSideSpacing,
    457                           avatar_y,
    458                           incognito_icon.width(),
    459                           avatar_height);
    460   avatar_button()->SetBoundsRect(avatar_bounds);
    461   avatar_button()->SetVisible(avatar_visible);
    462 }
    463 
    464 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
    465   if (!frame()->IsFullscreen())
    466     return true;
    467 
    468   // There is nothing to paint for traditional (tab) fullscreen.
    469   ImmersiveModeController* immersive_controller =
    470       browser_view()->immersive_mode_controller();
    471   if (!immersive_controller->IsEnabled())
    472     return false;
    473 
    474   // Need to paint during an immersive fullscreen reveal or when the immersive
    475   // light bar is visible.
    476   return immersive_controller->IsRevealed() ||
    477       !immersive_controller->ShouldHideTabIndicators();
    478 }
    479 
    480 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
    481     gfx::Canvas* canvas) {
    482   // The light bar header is not themed because theming it does not look good.
    483   gfx::ImageSkia* frame_image = GetThemeProvider()->GetImageSkiaNamed(
    484       IDR_AURA_WINDOW_HEADER_BASE_MINIMAL);
    485   canvas->TileImageInt(*frame_image, 0, 0, width(), frame_image->height());
    486 }
    487 
    488 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
    489   gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
    490   if (toolbar_bounds.IsEmpty())
    491     return;
    492   gfx::Point toolbar_origin(toolbar_bounds.origin());
    493   View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
    494   toolbar_bounds.set_origin(toolbar_origin);
    495 
    496   int x = toolbar_bounds.x();
    497   int w = toolbar_bounds.width();
    498   int y = toolbar_bounds.y();
    499   int h = toolbar_bounds.height();
    500 
    501   // Gross hack: We split the toolbar images into two pieces, since sometimes
    502   // (popup mode) the toolbar isn't tall enough to show the whole image.  The
    503   // split happens between the top shadow section and the bottom gradient
    504   // section so that we never break the gradient.
    505   int split_point = kFrameShadowThickness * 2;
    506   int bottom_y = y + split_point;
    507   ui::ThemeProvider* tp = GetThemeProvider();
    508   int bottom_edge_height = h - split_point;
    509 
    510   canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
    511                    tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
    512 
    513   // Paint the main toolbar image.  Since this image is also used to draw the
    514   // tab background, we must use the tab strip offset to compute the image
    515   // source y position.  If you have to debug this code use an image editor
    516   // to paint a diagonal line through the toolbar image and ensure it lines up
    517   // across the tab and toolbar.
    518   gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
    519   canvas->TileImageInt(
    520       *theme_toolbar,
    521       x + GetThemeBackgroundXInset(),
    522       bottom_y - GetTabStripInsets(false).top,
    523       x, bottom_y,
    524       w, theme_toolbar->height());
    525 
    526   // The content area line has a shadow that extends a couple of pixels above
    527   // the toolbar bounds.
    528   const int kContentShadowHeight = 2;
    529   gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
    530   canvas->TileImageInt(*toolbar_top,
    531                        0, 0,
    532                        x, y - kContentShadowHeight,
    533                        w, split_point + kContentShadowHeight + 1);
    534 
    535   // Draw the "lightening" shade line around the edges of the toolbar.
    536   gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
    537   canvas->TileImageInt(*toolbar_left,
    538                        0, 0,
    539                        x + kClientEdgeThickness,
    540                        y + kClientEdgeThickness + kContentShadowHeight,
    541                        toolbar_left->width(), theme_toolbar->height());
    542   gfx::ImageSkia* toolbar_right =
    543       tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
    544   canvas->TileImageInt(*toolbar_right,
    545                        0, 0,
    546                        w - toolbar_right->width() - 2 * kClientEdgeThickness,
    547                        y + kClientEdgeThickness + kContentShadowHeight,
    548                        toolbar_right->width(), theme_toolbar->height());
    549 
    550   // Draw the content/toolbar separator.
    551   canvas->FillRect(
    552       gfx::Rect(x + kClientEdgeThickness,
    553                 toolbar_bounds.bottom() - kClientEdgeThickness,
    554                 w - (2 * kClientEdgeThickness),
    555                 kClientEdgeThickness),
    556       ThemeProperties::GetDefaultColor(
    557           ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
    558 }
    559 
    560 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
    561   canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(),
    562                              width(), kClientEdgeThickness),
    563       ThemeProperties::GetDefaultColor(
    564           ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
    565 }
    566 
    567 int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const {
    568   bool is_incognito = browser_view()->IsOffTheRecord() &&
    569                       !browser_view()->IsGuestSession();
    570   if (browser_view()->IsBrowserTypeNormal()) {
    571     // Use the standard resource ids to allow users to theme the frames.
    572     if (ShouldPaintAsActive()) {
    573       return is_incognito ?
    574           IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
    575     }
    576     return is_incognito ?
    577         IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
    578   }
    579   // Never theme app and popup windows.
    580   if (ShouldPaintAsActive()) {
    581     return is_incognito ?
    582         IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE :
    583         IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
    584   }
    585   return is_incognito ?
    586       IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE :
    587       IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
    588 }
    589 
    590 int BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImageId() const {
    591   ui::ThemeProvider* tp = GetThemeProvider();
    592   if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
    593       browser_view()->IsBrowserTypeNormal() &&
    594       !browser_view()->IsOffTheRecord()) {
    595     return ShouldPaintAsActive() ?
    596         IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE;
    597   }
    598   return 0;
    599 }
    600