• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ash_switches.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 "base/command_line.h"
16 #include "chrome/browser/themes/theme_properties.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/views/frame/browser_frame.h"
19 #include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
20 #include "chrome/browser/ui/views/frame/browser_view.h"
21 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
22 #include "chrome/browser/ui/views/profiles/avatar_label.h"
23 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
24 #include "chrome/browser/ui/views/tab_icon_view.h"
25 #include "chrome/browser/ui/views/tabs/tab_strip.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "content/public/browser/web_contents.h"
28 #include "grit/ash_resources.h"
29 #include "grit/theme_resources.h"
30 #include "ui/accessibility/ax_view_state.h"
31 #include "ui/aura/client/aura_constants.h"
32 #include "ui/aura/window.h"
33 #include "ui/base/hit_test.h"
34 #include "ui/base/l10n/l10n_util.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 }  // namespace
74 
75 ///////////////////////////////////////////////////////////////////////////////
76 // BrowserNonClientFrameViewAsh, public:
77 
78 // static
79 const char BrowserNonClientFrameViewAsh::kViewClassName[] =
80     "BrowserNonClientFrameViewAsh";
81 
BrowserNonClientFrameViewAsh(BrowserFrame * frame,BrowserView * browser_view)82 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
83     BrowserFrame* frame, BrowserView* browser_view)
84     : BrowserNonClientFrameView(frame, browser_view),
85       caption_button_container_(NULL),
86       window_icon_(NULL),
87       frame_border_hit_test_controller_(
88           new ash::FrameBorderHitTestController(frame)) {
89   ash::Shell::GetInstance()->AddShellObserver(this);
90 }
91 
~BrowserNonClientFrameViewAsh()92 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
93   ash::Shell::GetInstance()->RemoveShellObserver(this);
94 }
95 
Init()96 void BrowserNonClientFrameViewAsh::Init() {
97   caption_button_container_ = new ash::FrameCaptionButtonContainerView(frame(),
98       ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
99   caption_button_container_->UpdateSizeButtonVisibility();
100   AddChildView(caption_button_container_);
101 
102   // Initializing the TabIconView is expensive, so only do it if we need to.
103   if (browser_view()->ShouldShowWindowIcon()) {
104     window_icon_ = new TabIconView(this, NULL);
105     window_icon_->set_is_light(true);
106     AddChildView(window_icon_);
107     window_icon_->Update();
108   }
109 
110   // Create incognito icon if necessary.
111   UpdateAvatarInfo();
112 
113   // HeaderPainter handles layout.
114   if (UsePackagedAppHeaderStyle()) {
115     ash::DefaultHeaderPainter* header_painter = new ash::DefaultHeaderPainter;
116     header_painter_.reset(header_painter);
117     header_painter->Init(frame(), this, window_icon_,
118         caption_button_container_);
119   } else {
120     BrowserHeaderPainterAsh* header_painter = new BrowserHeaderPainterAsh;
121     header_painter_.reset(header_painter);
122     header_painter->Init(frame(), browser_view(), this, window_icon_,
123         caption_button_container_);
124   }
125 }
126 
127 ///////////////////////////////////////////////////////////////////////////////
128 // BrowserNonClientFrameView:
129 
GetBoundsForTabStrip(views::View * tabstrip) const130 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
131     views::View* tabstrip) const {
132   if (!tabstrip)
133     return gfx::Rect();
134 
135   // When the tab strip is painted in the immersive fullscreen light bar style,
136   // the caption buttons and the avatar button are not visible. However, their
137   // bounds are still used to compute the tab strip bounds so that the tabs have
138   // the same horizontal position when the tab strip is painted in the immersive
139   // light bar style as when the top-of-window views are revealed.
140   int left_inset = GetTabStripLeftInset();
141   int right_inset = GetTabStripRightInset();
142   return gfx::Rect(left_inset,
143                    GetTopInset(),
144                    std::max(0, width() - left_inset - right_inset),
145                    tabstrip->GetPreferredSize().height());
146 }
147 
GetTopInset() const148 int BrowserNonClientFrameViewAsh::GetTopInset() const {
149   if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
150     return 0;
151 
152   if (browser_view()->IsTabStripVisible()) {
153     if (frame()->IsMaximized() || frame()->IsFullscreen())
154       return kTabstripTopSpacingShort;
155     else
156       return kTabstripTopSpacingTall;
157   }
158 
159   if (UsePackagedAppHeaderStyle())
160     return header_painter_->GetHeaderHeightForPainting();
161 
162   int caption_buttons_bottom = caption_button_container_->bounds().bottom();
163 
164   // The toolbar partially overlaps the caption buttons.
165   if (browser_view()->IsToolbarVisible())
166     return caption_buttons_bottom - kContentShadowHeight;
167 
168   return caption_buttons_bottom + kClientEdgeThickness;
169 }
170 
GetThemeBackgroundXInset() const171 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
172   return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
173 }
174 
UpdateThrobber(bool running)175 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
176   if (window_icon_)
177     window_icon_->Update();
178 }
179 
180 ///////////////////////////////////////////////////////////////////////////////
181 // views::NonClientFrameView:
182 
GetBoundsForClientView() const183 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
184   // The ClientView must be flush with the top edge of the widget so that the
185   // web contents can take up the entire screen in immersive fullscreen (with
186   // or without the top-of-window views revealed). When in immersive fullscreen
187   // and the top-of-window views are revealed, the TopContainerView paints the
188   // window header by redirecting paints from its background to
189   // BrowserNonClientFrameViewAsh.
190   return bounds();
191 }
192 
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const193 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
194     const gfx::Rect& client_bounds) const {
195   return client_bounds;
196 }
197 
NonClientHitTest(const gfx::Point & point)198 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
199   int hit_test = ash::FrameBorderHitTestController::NonClientHitTest(this,
200       caption_button_container_, point);
201 
202   // See if the point is actually within the avatar menu button or within
203   // the avatar label.
204   if (hit_test == HTCAPTION && ((avatar_button() &&
205        avatar_button()->GetMirroredBounds().Contains(point)) ||
206       (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point))))
207       return HTCLIENT;
208 
209   // When the window is restored we want a large click target above the tabs
210   // to drag the window, so redirect clicks in the tab's shadow to caption.
211   if (hit_test == HTCLIENT &&
212       !(frame()->IsMaximized() || frame()->IsFullscreen())) {
213     // Convert point to client coordinates.
214     gfx::Point client_point(point);
215     View::ConvertPointToTarget(this, frame()->client_view(), &client_point);
216     // Report hits in shadow at top of tabstrip as caption.
217     gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
218     if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight)
219       hit_test = HTCAPTION;
220   }
221   return hit_test;
222 }
223 
GetWindowMask(const gfx::Size & size,gfx::Path * window_mask)224 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
225                                                  gfx::Path* window_mask) {
226   // Aura does not use window masks.
227 }
228 
ResetWindowControls()229 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
230   // Hide the caption buttons in immersive fullscreen when the tab light bar
231   // is visible because it's confusing when the user hovers or clicks in the
232   // top-right of the screen and hits one.
233   bool button_visibility = !UseImmersiveLightbarHeaderStyle();
234   caption_button_container_->SetVisible(button_visibility);
235 
236   caption_button_container_->ResetWindowControls();
237 }
238 
UpdateWindowIcon()239 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
240   if (window_icon_)
241     window_icon_->SchedulePaint();
242 }
243 
UpdateWindowTitle()244 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
245   if (!frame()->IsFullscreen())
246     header_painter_->SchedulePaintForTitle();
247 }
248 
249 ///////////////////////////////////////////////////////////////////////////////
250 // views::View:
251 
OnPaint(gfx::Canvas * canvas)252 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
253   if (!ShouldPaint())
254     return;
255 
256   if (UseImmersiveLightbarHeaderStyle()) {
257     PaintImmersiveLightbarStyleHeader(canvas);
258     return;
259   }
260 
261   caption_button_container_->SetPaintAsActive(ShouldPaintAsActive());
262 
263   ash::HeaderPainter::Mode header_mode = ShouldPaintAsActive() ?
264       ash::HeaderPainter::MODE_ACTIVE : ash::HeaderPainter::MODE_INACTIVE;
265   header_painter_->PaintHeader(canvas, header_mode);
266   if (browser_view()->IsToolbarVisible())
267     PaintToolbarBackground(canvas);
268   else if (!UsePackagedAppHeaderStyle())
269     PaintContentEdge(canvas);
270 }
271 
Layout()272 void BrowserNonClientFrameViewAsh::Layout() {
273   // The header must be laid out before computing |painted_height| because the
274   // computation of |painted_height| for app and popup windows depends on the
275   // position of the window controls.
276   header_painter_->LayoutHeader();
277 
278   int painted_height = 0;
279   if (browser_view()->IsTabStripVisible()) {
280     painted_height = GetTopInset() +
281         browser_view()->tabstrip()->GetPreferredSize().height();
282   } else if (browser_view()->IsToolbarVisible()) {
283     // Paint the header so that it overlaps with the top few pixels of the
284     // toolbar because the top few pixels of the toolbar are not opaque.
285     painted_height = GetTopInset() + kFrameShadowThickness * 2;
286   } else {
287     painted_height = GetTopInset();
288   }
289   header_painter_->SetHeaderHeightForPainting(painted_height);
290   if (avatar_button())
291     LayoutAvatar();
292   BrowserNonClientFrameView::Layout();
293 }
294 
GetClassName() const295 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
296   return kViewClassName;
297 }
298 
HitTestRect(const gfx::Rect & rect) const299 bool BrowserNonClientFrameViewAsh::HitTestRect(const gfx::Rect& rect) const {
300   if (!views::View::HitTestRect(rect)) {
301     // |rect| is outside BrowserNonClientFrameViewAsh's bounds.
302     return false;
303   }
304 
305   TabStrip* tabstrip = browser_view()->tabstrip();
306   if (tabstrip && browser_view()->IsTabStripVisible()) {
307     // Claim |rect| only if it is above the bottom of the tabstrip in a non-tab
308     // portion.
309     gfx::RectF rect_in_tabstrip_coords_f(rect);
310     View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
311     gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
312         rect_in_tabstrip_coords_f);
313 
314      if (rect_in_tabstrip_coords.y() > tabstrip->height())
315        return false;
316 
317     return !tabstrip->HitTestRect(rect_in_tabstrip_coords) ||
318         tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
319   }
320 
321   // Claim |rect| if it is above the top of the topmost view in the client area.
322   return rect.y() < GetTopInset();
323 }
324 
GetAccessibleState(ui::AXViewState * state)325 void BrowserNonClientFrameViewAsh::GetAccessibleState(
326     ui::AXViewState* state) {
327   state->role = ui::AX_ROLE_TITLE_BAR;
328 }
329 
GetMinimumSize() const330 gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() const {
331   gfx::Size min_client_view_size(frame()->client_view()->GetMinimumSize());
332   int min_width = std::max(header_painter_->GetMinimumHeaderWidth(),
333                            min_client_view_size.width());
334   if (browser_view()->IsTabStripVisible()) {
335     // Ensure that the minimum width is enough to hold a minimum width tab strip
336     // at its usual insets.
337     int min_tabstrip_width =
338         browser_view()->tabstrip()->GetMinimumSize().width();
339     min_width = std::max(min_width,
340         min_tabstrip_width + GetTabStripLeftInset() + GetTabStripRightInset());
341   }
342   return gfx::Size(min_width, min_client_view_size.height());
343 }
344 
345 ///////////////////////////////////////////////////////////////////////////////
346 // ash::ShellObserver:
347 
OnMaximizeModeStarted()348 void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
349   caption_button_container_->UpdateSizeButtonVisibility();
350   InvalidateLayout();
351   frame()->client_view()->InvalidateLayout();
352   frame()->GetRootView()->Layout();
353 }
354 
OnMaximizeModeEnded()355 void BrowserNonClientFrameViewAsh::OnMaximizeModeEnded() {
356   caption_button_container_->UpdateSizeButtonVisibility();
357   InvalidateLayout();
358   frame()->client_view()->InvalidateLayout();
359   frame()->GetRootView()->Layout();
360 }
361 
362 ///////////////////////////////////////////////////////////////////////////////
363 // chrome::TabIconViewModel:
364 
ShouldTabIconViewAnimate() const365 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
366   // This function is queried during the creation of the window as the
367   // TabIconView we host is initialized, so we need to NULL check the selected
368   // WebContents because in this condition there is not yet a selected tab.
369   content::WebContents* current_tab = browser_view()->GetActiveWebContents();
370   return current_tab ? current_tab->IsLoading() : false;
371 }
372 
GetFaviconForTabIconView()373 gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
374   views::WidgetDelegate* delegate = frame()->widget_delegate();
375   if (!delegate)
376     return gfx::ImageSkia();
377   return delegate->GetWindowIcon();
378 }
379 
380 ///////////////////////////////////////////////////////////////////////////////
381 // BrowserNonClientFrameViewAsh, private:
382 
GetTabStripLeftInset() const383 int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
384   return avatar_button() ? kAvatarSideSpacing +
385       browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
386       kTabstripLeftSpacing;
387 }
388 
GetTabStripRightInset() const389 int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
390   return caption_button_container_->GetPreferredSize().width() +
391       kTabstripRightSpacing;
392 }
393 
UseImmersiveLightbarHeaderStyle() const394 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
395   ImmersiveModeController* immersive_controller =
396       browser_view()->immersive_mode_controller();
397   return immersive_controller->IsEnabled() &&
398       !immersive_controller->IsRevealed() &&
399       browser_view()->IsTabStripVisible();
400 }
401 
UsePackagedAppHeaderStyle() const402 bool BrowserNonClientFrameViewAsh::UsePackagedAppHeaderStyle() const {
403   // Non streamlined hosted apps do not have a toolbar or tabstrip. Their header
404   // should look the same as the header for packaged apps. Streamlined hosted
405   // apps have a toolbar so should use the browser header style.
406   return browser_view()->browser()->is_app() &&
407       !CommandLine::ForCurrentProcess()->HasSwitch(
408           switches::kEnableStreamlinedHostedApps);
409 }
410 
LayoutAvatar()411 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
412   DCHECK(avatar_button());
413 #if !defined(OS_CHROMEOS)
414   // ChromeOS shows avatar on V1 app.
415   DCHECK(browser_view()->IsTabStripVisible());
416 #endif
417   gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
418 
419   int avatar_bottom = GetTopInset() +
420       browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
421   int avatar_restored_y = avatar_bottom - incognito_icon.height();
422   int avatar_y =
423       (browser_view()->IsTabStripVisible() &&
424        (frame()->IsMaximized() || frame()->IsFullscreen())) ?
425       GetTopInset() + kContentShadowHeight : avatar_restored_y;
426 
427   // Hide the incognito icon in immersive fullscreen when the tab light bar is
428   // visible because the header is too short for the icognito icon to be
429   // recognizable.
430   bool avatar_visible = !UseImmersiveLightbarHeaderStyle();
431   int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0;
432 
433   gfx::Rect avatar_bounds(kAvatarSideSpacing,
434                           avatar_y,
435                           incognito_icon.width(),
436                           avatar_height);
437   avatar_button()->SetBoundsRect(avatar_bounds);
438   avatar_button()->SetVisible(avatar_visible);
439 }
440 
ShouldPaint() const441 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
442   if (!frame()->IsFullscreen())
443     return true;
444 
445   // We need to paint when in immersive fullscreen and either:
446   // - The top-of-window views are revealed.
447   // - The lightbar style tabstrip is visible.
448   ImmersiveModeController* immersive_mode_controller =
449       browser_view()->immersive_mode_controller();
450   return immersive_mode_controller->IsEnabled() &&
451       (immersive_mode_controller->IsRevealed() ||
452        UseImmersiveLightbarHeaderStyle());
453 }
454 
PaintImmersiveLightbarStyleHeader(gfx::Canvas * canvas)455 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
456     gfx::Canvas* canvas) {
457   // The light bar header is not themed because theming it does not look good.
458   canvas->FillRect(
459       gfx::Rect(width(), header_painter_->GetHeaderHeightForPainting()),
460       SK_ColorBLACK);
461 }
462 
PaintToolbarBackground(gfx::Canvas * canvas)463 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) {
464   gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
465   if (toolbar_bounds.IsEmpty())
466     return;
467   gfx::Point toolbar_origin(toolbar_bounds.origin());
468   View::ConvertPointToTarget(browser_view(), this, &toolbar_origin);
469   toolbar_bounds.set_origin(toolbar_origin);
470 
471   int x = toolbar_bounds.x();
472   int w = toolbar_bounds.width();
473   int y = toolbar_bounds.y();
474   int h = toolbar_bounds.height();
475 
476   // Gross hack: We split the toolbar images into two pieces, since sometimes
477   // (popup mode) the toolbar isn't tall enough to show the whole image.  The
478   // split happens between the top shadow section and the bottom gradient
479   // section so that we never break the gradient.
480   // NOTE(pkotwicz): If the computation for |bottom_y| is changed, Layout() must
481   // be changed as well.
482   int split_point = kFrameShadowThickness * 2;
483   int bottom_y = y + split_point;
484   ui::ThemeProvider* tp = GetThemeProvider();
485   int bottom_edge_height = h - split_point;
486 
487   canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
488                    tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
489 
490   // Paint the main toolbar image.  Since this image is also used to draw the
491   // tab background, we must use the tab strip offset to compute the image
492   // source y position.  If you have to debug this code use an image editor
493   // to paint a diagonal line through the toolbar image and ensure it lines up
494   // across the tab and toolbar.
495   gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
496   canvas->TileImageInt(
497       *theme_toolbar,
498       x + GetThemeBackgroundXInset(),
499       bottom_y - GetTopInset(),
500       x, bottom_y,
501       w, theme_toolbar->height());
502 
503   // The content area line has a shadow that extends a couple of pixels above
504   // the toolbar bounds.
505   const int kContentShadowHeight = 2;
506   gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP);
507   canvas->TileImageInt(*toolbar_top,
508                        0, 0,
509                        x, y - kContentShadowHeight,
510                        w, split_point + kContentShadowHeight + 1);
511 
512   // Draw the "lightening" shade line around the edges of the toolbar.
513   gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT);
514   canvas->TileImageInt(*toolbar_left,
515                        0, 0,
516                        x + kClientEdgeThickness,
517                        y + kClientEdgeThickness + kContentShadowHeight,
518                        toolbar_left->width(), theme_toolbar->height());
519   gfx::ImageSkia* toolbar_right =
520       tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT);
521   canvas->TileImageInt(*toolbar_right,
522                        0, 0,
523                        w - toolbar_right->width() - 2 * kClientEdgeThickness,
524                        y + kClientEdgeThickness + kContentShadowHeight,
525                        toolbar_right->width(), theme_toolbar->height());
526 
527   // Draw the content/toolbar separator.
528   canvas->FillRect(
529       gfx::Rect(x + kClientEdgeThickness,
530                 toolbar_bounds.bottom() - kClientEdgeThickness,
531                 w - (2 * kClientEdgeThickness),
532                 kClientEdgeThickness),
533       ThemeProperties::GetDefaultColor(
534           ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
535 }
536 
PaintContentEdge(gfx::Canvas * canvas)537 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
538   DCHECK(!UsePackagedAppHeaderStyle());
539   canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(),
540                              width(), kClientEdgeThickness),
541                    ThemeProperties::GetDefaultColor(
542                        ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
543 }
544