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 <algorithm>
      8 
      9 #include "ash/frame/caption_buttons/frame_caption_button.h"
     10 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
     11 #include "ash/frame/default_header_painter.h"
     12 #include "ash/frame/frame_border_hit_test_controller.h"
     13 #include "ash/frame/header_painter_util.h"
     14 #include "ash/shell.h"
     15 #include "chrome/app/chrome_command_ids.h"
     16 #include "chrome/browser/extensions/extension_util.h"
     17 #include "chrome/browser/themes/theme_properties.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/browser/ui/browser_commands.h"
     20 #include "chrome/browser/ui/views/frame/browser_frame.h"
     21 #include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
     22 #include "chrome/browser/ui/views/frame/browser_view.h"
     23 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
     24 #include "chrome/browser/ui/views/profiles/avatar_label.h"
     25 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
     26 #include "chrome/browser/ui/views/tab_icon_view.h"
     27 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     28 #include "content/public/browser/web_contents.h"
     29 #include "grit/ash_resources.h"
     30 #include "grit/theme_resources.h"
     31 #include "ui/accessibility/ax_view_state.h"
     32 #include "ui/aura/client/aura_constants.h"
     33 #include "ui/aura/window.h"
     34 #include "ui/base/hit_test.h"
     35 #include "ui/base/layout.h"
     36 #include "ui/base/resource/resource_bundle.h"
     37 #include "ui/base/theme_provider.h"
     38 #include "ui/compositor/layer_animator.h"
     39 #include "ui/gfx/canvas.h"
     40 #include "ui/gfx/image/image_skia.h"
     41 #include "ui/gfx/rect_conversions.h"
     42 #include "ui/views/controls/label.h"
     43 #include "ui/views/layout/layout_constants.h"
     44 #include "ui/views/widget/widget.h"
     45 #include "ui/views/widget/widget_delegate.h"
     46 
     47 namespace {
     48 
     49 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
     50 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
     51 // user).
     52 const int kAvatarBottomSpacing = 2;
     53 // There are 2 px on each side of the avatar (between the frame border and
     54 // it on the left, and between it and the tabstrip on the right).
     55 const int kAvatarSideSpacing = 2;
     56 // Space between left edge of window and tabstrip.
     57 const int kTabstripLeftSpacing = 0;
     58 // Space between right edge of tabstrip and maximize button.
     59 const int kTabstripRightSpacing = 10;
     60 // Height of the shadow of the content area, at the top of the toolbar.
     61 const int kContentShadowHeight = 1;
     62 // Space between top of window and top of tabstrip for tall headers, such as
     63 // for restored windows, apps, etc.
     64 const int kTabstripTopSpacingTall = 7;
     65 // Space between top of window and top of tabstrip for short headers, such as
     66 // for maximized windows, pop-ups, etc.
     67 const int kTabstripTopSpacingShort = 0;
     68 // Height of the shadow in the tab image, used to ensure clicks in the shadow
     69 // area still drag restored windows.  This keeps the clickable area large enough
     70 // to hit easily.
     71 const int kTabShadowHeight = 4;
     72 
     73 // Combines View::ConvertPointToTarget() and View::HitTest() for a given
     74 // |point|. Converts |point| from |src| to |dst| and hit tests it against |dst|.
     75 bool ConvertedHitTest(views::View* src,
     76                       views::View* dst,
     77                       const gfx::Point& point) {
     78   DCHECK(src);
     79   DCHECK(dst);
     80   gfx::Point converted_point(point);
     81   views::View::ConvertPointToTarget(src, dst, &converted_point);
     82   return dst->HitTestPoint(converted_point);
     83 }
     84 
     85 }  // namespace
     86 
     87 ///////////////////////////////////////////////////////////////////////////////
     88 // BrowserNonClientFrameViewAsh, public:
     89 
     90 // static
     91 const char BrowserNonClientFrameViewAsh::kViewClassName[] =
     92     "BrowserNonClientFrameViewAsh";
     93 
     94 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
     95     BrowserFrame* frame,
     96     BrowserView* browser_view)
     97     : BrowserNonClientFrameView(frame, browser_view),
     98       caption_button_container_(NULL),
     99       web_app_back_button_(NULL),
    100       window_icon_(NULL),
    101       frame_border_hit_test_controller_(
    102           new ash::FrameBorderHitTestController(frame)) {
    103   ash::Shell::GetInstance()->AddShellObserver(this);
    104 }
    105 
    106 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
    107   ash::Shell::GetInstance()->RemoveShellObserver(this);
    108   // browser_view() outlives the frame, as destruction of sibling views happens
    109   // in the same order as creation - see BrowserView::CreateBrowserWindow.
    110   chrome::RemoveCommandObserver(browser_view()->browser(), IDC_BACK, this);
    111 }
    112 
    113 void BrowserNonClientFrameViewAsh::Init() {
    114   caption_button_container_ = new ash::FrameCaptionButtonContainerView(frame(),
    115       ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
    116   caption_button_container_->UpdateSizeButtonVisibility();
    117   AddChildView(caption_button_container_);
    118 
    119   // Initializing the TabIconView is expensive, so only do it if we need to.
    120   if (browser_view()->ShouldShowWindowIcon()) {
    121     window_icon_ = new TabIconView(this, NULL);
    122     window_icon_->set_is_light(true);
    123     AddChildView(window_icon_);
    124     window_icon_->Update();
    125   }
    126 
    127   // Create incognito icon if necessary.
    128   UpdateAvatarInfo();
    129 
    130   // HeaderPainter handles layout.
    131   if (UsePackagedAppHeaderStyle()) {
    132     ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
    133     header_painter_.reset(header_painter);
    134     header_painter->Init(frame(), this, caption_button_container_);
    135     if (window_icon_) {
    136       header_painter->UpdateLeftHeaderView(window_icon_);
    137     }
    138   } else if (UseWebAppHeaderStyle()) {
    139     web_app_back_button_ =
    140         new ash::FrameCaptionButton(this, ash::CAPTION_BUTTON_ICON_BACK);
    141     web_app_back_button_->SetImages(ash::CAPTION_BUTTON_ICON_BACK,
    142                                     ash::FrameCaptionButton::ANIMATE_NO,
    143                                     IDR_AURA_WINDOW_CONTROL_ICON_BACK,
    144                                     IDR_AURA_WINDOW_CONTROL_ICON_BACK_I,
    145                                     IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
    146                                     IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
    147 
    148     UpdateBackButtonState(true);
    149     chrome::AddCommandObserver(browser_view()->browser(), IDC_BACK, this);
    150     AddChildView(web_app_back_button_);
    151 
    152     ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
    153     header_painter_.reset(header_painter);
    154     header_painter->Init(frame(), this, caption_button_container_);
    155     header_painter->UpdateLeftHeaderView(web_app_back_button_);
    156   } else {
    157     BrowserHeaderPainterAsh* header_painter = new BrowserHeaderPainterAsh;
    158     header_painter_.reset(header_painter);
    159     header_painter->Init(frame(), browser_view(), this, window_icon_,
    160         caption_button_container_);
    161   }
    162 }
    163 
    164 ///////////////////////////////////////////////////////////////////////////////
    165 // BrowserNonClientFrameView:
    166 
    167 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
    168     views::View* tabstrip) const {
    169   if (!tabstrip)
    170     return gfx::Rect();
    171 
    172   // When the tab strip is painted in the immersive fullscreen light bar style,
    173   // the caption buttons and the avatar button are not visible. However, their
    174   // bounds are still used to compute the tab strip bounds so that the tabs have
    175   // the same horizontal position when the tab strip is painted in the immersive
    176   // light bar style as when the top-of-window views are revealed.
    177   int left_inset = GetTabStripLeftInset();
    178   int right_inset = GetTabStripRightInset();
    179   return gfx::Rect(left_inset,
    180                    GetTopInset(),
    181                    std::max(0, width() - left_inset - right_inset),
    182                    tabstrip->GetPreferredSize().height());
    183 }
    184 
    185 int BrowserNonClientFrameViewAsh::GetTopInset() const {
    186   if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
    187     return 0;
    188 
    189   if (browser_view()->IsTabStripVisible()) {
    190     if (frame()->IsMaximized() || frame()->IsFullscreen())
    191       return kTabstripTopSpacingShort;
    192     else
    193       return kTabstripTopSpacingTall;
    194   }
    195 
    196   if (UsePackagedAppHeaderStyle() || UseWebAppHeaderStyle())
    197     return header_painter_->GetHeaderHeightForPainting();
    198 
    199   int caption_buttons_bottom = caption_button_container_->bounds().bottom();
    200 
    201   // The toolbar partially overlaps the caption buttons.
    202   if (browser_view()->IsToolbarVisible())
    203     return caption_buttons_bottom - kContentShadowHeight;
    204 
    205   return caption_buttons_bottom + kClientEdgeThickness;
    206 }
    207 
    208 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
    209   return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
    210 }
    211 
    212 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
    213   if (window_icon_)
    214     window_icon_->Update();
    215 }
    216 
    217 ///////////////////////////////////////////////////////////////////////////////
    218 // views::NonClientFrameView:
    219 
    220 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
    221   // The ClientView must be flush with the top edge of the widget so that the
    222   // web contents can take up the entire screen in immersive fullscreen (with
    223   // or without the top-of-window views revealed). When in immersive fullscreen
    224   // and the top-of-window views are revealed, the TopContainerView paints the
    225   // window header by redirecting paints from its background to
    226   // BrowserNonClientFrameViewAsh.
    227   return bounds();
    228 }
    229 
    230 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
    231     const gfx::Rect& client_bounds) const {
    232   return client_bounds;
    233 }
    234 
    235 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
    236   int hit_test = ash::FrameBorderHitTestController::NonClientHitTest(this,
    237       caption_button_container_, point);
    238 
    239   // See if the point is actually within the avatar menu button.
    240   if (hit_test == HTCAPTION && avatar_button() &&
    241       ConvertedHitTest(this, avatar_button(), point)) {
    242     return HTCLIENT;
    243   }
    244 
    245   // See if the point is actually within the web app back button.
    246   if (hit_test == HTCAPTION && web_app_back_button_ &&
    247       ConvertedHitTest(this, web_app_back_button_, point)) {
    248     return HTCLIENT;
    249   }
    250 
    251   // When the window is restored we want a large click target above the tabs
    252   // to drag the window, so redirect clicks in the tab's shadow to caption.
    253   if (hit_test == HTCLIENT &&
    254       !(frame()->IsMaximized() || frame()->IsFullscreen())) {
    255     // Convert point to client coordinates.
    256     gfx::Point client_point(point);
    257     View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
    258     // Report hits in shadow at top of tabstrip as caption.
    259     gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
    260     if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
    261       hit_test = HTCAPTION;
    262   }
    263   return hit_test;
    264 }
    265 
    266 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
    267                                                  gfx::Path* window_mask) {
    268   // Aura does not use window masks.
    269 }
    270 
    271 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
    272   // Hide the caption buttons in immersive fullscreen when the tab light bar
    273   // is visible because it's confusing when the user hovers or clicks in the
    274   // top-right of the screen and hits one.
    275   bool button_visibility = !UseImmersiveLightbarHeaderStyle();
    276   caption_button_container_->SetVisible(button_visibility);
    277 
    278   caption_button_container_->ResetWindowControls();
    279 }
    280 
    281 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
    282   if (window_icon_)
    283     window_icon_->SchedulePaint();
    284 }
    285 
    286 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
    287   if (!frame()->IsFullscreen())
    288     header_painter_->SchedulePaintForTitle();
    289 }
    290 
    291 void BrowserNonClientFrameViewAsh::SizeConstraintsChanged() {
    292 }
    293 
    294 ///////////////////////////////////////////////////////////////////////////////
    295 // views::View:
    296 
    297 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
    298   if (!ShouldPaint())
    299     return;
    300 
    301   if (UseImmersiveLightbarHeaderStyle()) {
    302     PaintImmersiveLightbarStyleHeader(canvas);
    303     return;
    304   }
    305 
    306   caption_button_container_->SetPaintAsActive(ShouldPaintAsActive());
    307   if (web_app_back_button_) {
    308     // TODO(benwells): Check that the disabled and inactive states should be
    309     // drawn in the same way.
    310     web_app_back_button_->set_paint_as_active(
    311         ShouldPaintAsActive() &&
    312         chrome::IsCommandEnabled(browser_view()->browser(), IDC_BACK));
    313   }
    314 
    315   ash::HeaderPainter::Mode header_mode = ShouldPaintAsActive() ?
    316       ash::HeaderPainter::MODE_ACTIVE : ash::HeaderPainter::MODE_INACTIVE;
    317   header_painter_->PaintHeader(canvas, header_mode);
    318   if (browser_view()->IsToolbarVisible())
    319     PaintToolbarBackground(canvas);
    320   else if (!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle())
    321     PaintContentEdge(canvas);
    322 }
    323 
    324 void BrowserNonClientFrameViewAsh::Layout() {
    325   // The header must be laid out before computing |painted_height| because the
    326   // computation of |painted_height| for app and popup windows depends on the
    327   // position of the window controls.
    328   header_painter_->LayoutHeader();
    329 
    330   int painted_height = 0;
    331   if (browser_view()->IsTabStripVisible()) {
    332     painted_height = GetTopInset() +
    333         browser_view()->tabstrip()->GetPreferredSize().height();
    334   } else if (browser_view()->IsToolbarVisible()) {
    335     // Paint the header so that it overlaps with the top few pixels of the
    336     // toolbar because the top few pixels of the toolbar are not opaque.
    337     painted_height = GetTopInset() + kFrameShadowThickness * 2;
    338   } else {
    339     painted_height = GetTopInset();
    340   }
    341   header_painter_->SetHeaderHeightForPainting(painted_height);
    342   if (avatar_button()) {
    343     LayoutAvatar();
    344     header_painter_->UpdateLeftViewXInset(avatar_button()->bounds().right());
    345   } else {
    346     header_painter_->UpdateLeftViewXInset(
    347         ash::HeaderPainterUtil::GetDefaultLeftViewXInset());
    348   }
    349   BrowserNonClientFrameView::Layout();
    350 }
    351 
    352 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
    353   return kViewClassName;
    354 }
    355 
    356 void BrowserNonClientFrameViewAsh::GetAccessibleState(
    357     ui::AXViewState* state) {
    358   state->role = ui::AX_ROLE_TITLE_BAR;
    359 }
    360 
    361 gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() const {
    362   gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize());
    363   int min_width = std::max(header_painter_->GetMinimumHeaderWidth(),
    364                            min_client_view_size.width());
    365   if (browser_view()->IsTabStripVisible()) {
    366     // Ensure that the minimum width is enough to hold a minimum width tab strip
    367     // at its usual insets.
    368     int min_tabstrip_width =
    369         browser_view()->tabstrip()->GetMinimumSize().width();
    370     min_width = std::max(min_width,
    371         min_tabstrip_width + GetTabStripLeftInset() + GetTabStripRightInset());
    372   }
    373   return gfx::Size(min_width, min_client_view_size.height());
    374 }
    375 
    376 void BrowserNonClientFrameViewAsh::
    377   ChildPreferredSizeChanged(views::View* child) {
    378   // FrameCaptionButtonContainerView animates the visibility changes in
    379   // UpdateSizeButtonVisibility(false). Due to this a new size is not available
    380   // until the completion of the animation. Layout in response to the preferred
    381   // size changes.
    382   if (child != caption_button_container_)
    383     return;
    384   frame()->GetRootView()->Layout();
    385 }
    386 
    387 ///////////////////////////////////////////////////////////////////////////////
    388 // ash::ShellObserver:
    389 
    390 void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
    391   caption_button_container_->UpdateSizeButtonVisibility();
    392   InvalidateLayout();
    393   frame()->client_view()->InvalidateLayout();
    394   frame()->GetRootView()->Layout();
    395 }
    396 
    397 void BrowserNonClientFrameViewAsh::OnMaximizeModeEnded() {
    398   caption_button_container_->UpdateSizeButtonVisibility();
    399   InvalidateLayout();
    400   frame()->client_view()->InvalidateLayout();
    401   frame()->GetRootView()->Layout();
    402 }
    403 
    404 ///////////////////////////////////////////////////////////////////////////////
    405 // chrome::TabIconViewModel:
    406 
    407 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
    408   // This function is queried during the creation of the window as the
    409   // TabIconView we host is initialized, so we need to NULL check the selected
    410   // WebContents because in this condition there is not yet a selected tab.
    411   content::WebContents* current_tab = browser_view()->GetActiveWebContents();
    412   return current_tab ? current_tab->IsLoading() : false;
    413 }
    414 
    415 gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
    416   views::WidgetDelegate* delegate = frame()->widget_delegate();
    417   if (!delegate)
    418     return gfx::ImageSkia();
    419   return delegate->GetWindowIcon();
    420 }
    421 
    422 ///////////////////////////////////////////////////////////////////////////////
    423 // CommandObserver:
    424 
    425 void BrowserNonClientFrameViewAsh::EnabledStateChangedForCommand(int id,
    426                                                                  bool enabled) {
    427   DCHECK_EQ(IDC_BACK, id);
    428   UpdateBackButtonState(enabled);
    429 }
    430 
    431 ///////////////////////////////////////////////////////////////////////////////
    432 // views::ButtonListener:
    433 
    434 void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
    435                                                  const ui::Event& event) {
    436   DCHECK_EQ(sender, web_app_back_button_);
    437   chrome::ExecuteCommand(browser_view()->browser(), IDC_BACK);
    438 }
    439 
    440 ///////////////////////////////////////////////////////////////////////////////
    441 // BrowserNonClientFrameViewAsh, private:
    442 
    443 // views::NonClientFrameView:
    444 bool BrowserNonClientFrameViewAsh::DoesIntersectRect(
    445     const views::View* target,
    446     const gfx::Rect& rect) const {
    447   CHECK_EQ(target, this);
    448   if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) {
    449     // |rect| is outside BrowserNonClientFrameViewAsh's bounds.
    450     return false;
    451   }
    452 
    453   TabStrip* tabstrip = browser_view()->tabstrip();
    454   if (tabstrip && browser_view()->IsTabStripVisible()) {
    455     // Claim |rect| only if it is above the bottom of the tabstrip in a non-tab
    456     // portion.
    457     gfx::RectF rect_in_tabstrip_coords_f(rect);
    458     View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
    459     gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
    460         rect_in_tabstrip_coords_f);
    461 
    462      if (rect_in_tabstrip_coords.y() > tabstrip->height())
    463        return false;
    464 
    465     return !tabstrip->HitTestRect(rect_in_tabstrip_coords) ||
    466         tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
    467   }
    468 
    469   // Claim |rect| if it is above the top of the topmost view in the client area.
    470   return rect.y() < GetTopInset();
    471 }
    472 
    473 int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
    474   return avatar_button() ? kAvatarSideSpacing +
    475       browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
    476       kTabstripLeftSpacing;
    477 }
    478 
    479 int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
    480   return caption_button_container_->GetPreferredSize().width() +
    481       kTabstripRightSpacing;
    482 }
    483 
    484 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
    485   ImmersiveModeController* immersive_controller =
    486       browser_view()->immersive_mode_controller();
    487   return immersive_controller->IsEnabled() &&
    488       !immersive_controller->IsRevealed() &&
    489       browser_view()->IsTabStripVisible();
    490 }
    491 
    492 bool BrowserNonClientFrameViewAsh::UsePackagedAppHeaderStyle() const {
    493   // Use the packaged app style for apps that aren't using the newer WebApp
    494   // style.
    495   return browser_view()->browser()->is_app() && !UseWebAppHeaderStyle();
    496 }
    497 
    498 bool BrowserNonClientFrameViewAsh::UseWebAppHeaderStyle() const {
    499   // Use of the experimental WebApp header style is guarded with the
    500   // streamlined hosted app style.
    501   return browser_view()->browser()->is_app() &&
    502          extensions::util::IsStreamlinedHostedAppsEnabled();
    503 }
    504 
    505 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
    506   DCHECK(avatar_button());
    507 #if !defined(OS_CHROMEOS)
    508   // ChromeOS shows avatar on V1 app.
    509   DCHECK(browser_view()->IsTabStripVisible());
    510 #endif
    511   gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
    512 
    513   int avatar_bottom = GetTopInset() +
    514       browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
    515   int avatar_restored_y = avatar_bottom - incognito_icon.height();
    516   int avatar_y =
    517       (browser_view()->IsTabStripVisible() &&
    518        (frame()->IsMaximized() || frame()->IsFullscreen())) ?
    519       GetTopInset() + kContentShadowHeight : avatar_restored_y;
    520 
    521   // Hide the incognito icon in immersive fullscreen when the tab light bar is
    522   // visible because the header is too short for the icognito icon to be
    523   // recognizable.
    524   bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
    525   int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
    526 
    527   gfx::Rect avatar_bounds(kAvatarSideSpacing,
    528                           avatar_y,
    529                           incognito_icon.width(),
    530                           avatar_height);
    531   avatar_button()->SetBoundsRect(avatar_bounds);
    532   avatar_button()->SetVisible(avatar_visible);
    533 }
    534 
    535 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
    536   if (!frame()->IsFullscreen())
    537     return true;
    538 
    539   // We need to paint when in immersive fullscreen and either:
    540   // - The top-of-window views are revealed.
    541   // - The lightbar style tabstrip is visible.
    542   ImmersiveModeController* immersive_mode_controller =
    543       browser_view()->immersive_mode_controller();
    544   return immersive_mode_controller->IsEnabled() &&
    545       (immersive_mode_controller->IsRevealed() ||
    546        UseImmersiveLightbarHeaderStyle());
    547 }
    548 
    549 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
    550     gfx::Canvas* canvas) {
    551   // The light bar header is not themed because theming it does not look good.
    552   canvas->FillRect(
    553       gfx::Rect(width(), header_painter_->GetHeaderHeightForPainting()),
    554       SK_ColorBLACK);
    555 }
    556 
    557 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
    558   gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
    559   if (toolbar_bounds.IsEmpty())
    560     return;
    561   gfx::Point toolbar_origin(toolbar_bounds.origin());
    562   View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
    563   toolbar_bounds.set_origin(toolbar_origin);
    564 
    565   int x = toolbar_bounds.x();
    566   int w = toolbar_bounds.width();
    567   int y = toolbar_bounds.y();
    568   int h = toolbar_bounds.height();
    569 
    570   // Gross hack: We split the toolbar images into two pieces, since sometimes
    571   // (popup mode) the toolbar isn't tall enough to show the whole image.  The
    572   // split happens between the top shadow section and the bottom gradient
    573   // section so that we never break the gradient.
    574   // NOTE(pkotwicz): If the computation for |bottom_y| is changed, Layout() must
    575   // be changed as well.
    576   int split_point = kFrameShadowThickness * 2;
    577   int bottom_y = y + split_point;
    578   ui::ThemeProvider* tp = GetThemeProvider();
    579   int bottom_edge_height = h - split_point;
    580 
    581   canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
    582                    tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
    583 
    584   // Paint the main toolbar image.  Since this image is also used to draw the
    585   // tab background, we must use the tab strip offset to compute the image
    586   // source y position.  If you have to debug this code use an image editor
    587   // to paint a diagonal line through the toolbar image and ensure it lines up
    588   // across the tab and toolbar.
    589   gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
    590   canvas->TileImageInt(
    591       *theme_toolbar,
    592       x + GetThemeBackgroundXInset(),
    593       bottom_y - GetTopInset(),
    594       x, bottom_y,
    595       w, theme_toolbar->height());
    596 
    597   // The content area line has a shadow that extends a couple of pixels above
    598   // the toolbar bounds.
    599   const int kContentShadowHeight = 2;
    600   gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
    601   canvas->TileImageInt(*toolbar_top,
    602                        0, 0,
    603                        x, y - kContentShadowHeight,
    604                        w, split_point + kContentShadowHeight + 1);
    605 
    606   // Draw the "lightening" shade line around the edges of the toolbar.
    607   gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
    608   canvas->TileImageInt(*toolbar_left,
    609                        0, 0,
    610                        x + kClientEdgeThickness,
    611                        y + kClientEdgeThickness + kContentShadowHeight,
    612                        toolbar_left->width(), theme_toolbar->height());
    613   gfx::ImageSkia* toolbar_right =
    614       tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
    615   canvas->TileImageInt(*toolbar_right,
    616                        0, 0,
    617                        w - toolbar_right->width() - 2 * kClientEdgeThickness,
    618                        y + kClientEdgeThickness + kContentShadowHeight,
    619                        toolbar_right->width(), theme_toolbar->height());
    620 
    621   // Draw the content/toolbar separator.
    622   canvas->FillRect(
    623       gfx::Rect(x + kClientEdgeThickness,
    624                 toolbar_bounds.bottom() - kClientEdgeThickness,
    625                 w - (2 * kClientEdgeThickness),
    626                 kClientEdgeThickness),
    627       ThemeProperties::GetDefaultColor(
    628           ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
    629 }
    630 
    631 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
    632   DCHECK(!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle());
    633   canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(),
    634                              width(), kClientEdgeThickness),
    635                    ThemeProperties::GetDefaultColor(
    636                        ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
    637 }
    638 
    639 void BrowserNonClientFrameViewAsh::UpdateBackButtonState(bool enabled) {
    640   web_app_back_button_->SetState(enabled ? views::Button::STATE_NORMAL
    641                                          : views::Button::STATE_DISABLED);
    642 }
    643