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