1 // Copyright 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.
5 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
9 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
10 #include "ash/frame/default_header_painter.h"
11 #include "ash/frame/frame_border_hit_test_controller.h"
12 #include "ash/frame/header_painter_util.h"
13 #include "ash/shell.h"
14 #include "base/profiler/scoped_tracker.h"
15 #include "chrome/app/chrome_command_ids.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/profiles/profiles_state.h"
18 #include "chrome/browser/themes/theme_properties.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_commands.h"
21 #include "chrome/browser/ui/views/frame/browser_frame.h"
22 #include "chrome/browser/ui/views/frame/browser_header_painter_ash.h"
23 #include "chrome/browser/ui/views/frame/browser_view.h"
24 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
25 #include "chrome/browser/ui/views/frame/web_app_left_header_view_ash.h"
26 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
27 #include "chrome/browser/ui/views/tab_icon_view.h"
28 #include "chrome/browser/ui/views/tabs/tab_strip.h"
29 #include "chrome/browser/web_applications/web_app.h"
30 #include "components/signin/core/common/profile_management_switches.h"
31 #include "content/public/browser/web_contents.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "grit/theme_resources.h"
34 #include "ui/accessibility/ax_view_state.h"
35 #include "ui/aura/client/aura_constants.h"
36 #include "ui/aura/window.h"
37 #include "ui/base/hit_test.h"
38 #include "ui/base/layout.h"
39 #include "ui/base/resource/material_design/material_design_controller.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/base/theme_provider.h"
42 #include "ui/compositor/layer_animator.h"
43 #include "ui/gfx/canvas.h"
44 #include "ui/gfx/geometry/rect_conversions.h"
45 #include "ui/gfx/image/image_skia.h"
46 #include "ui/views/controls/label.h"
47 #include "ui/views/layout/layout_constants.h"
48 #include "ui/views/widget/widget.h"
49 #include "ui/views/widget/widget_delegate.h"
51 #if defined(ENABLE_SUPERVISED_USERS)
52 #include "chrome/browser/ui/views/profiles/supervised_user_avatar_label.h"
57 // The avatar ends 2 px above the bottom of the tabstrip (which, given the
58 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
60 const int kAvatarBottomSpacing
= 2;
61 // There are 2 px on each side of the avatar (between the frame border and
62 // it on the left, and between it and the tabstrip on the right).
63 const int kAvatarSideSpacing
= 2;
64 #if defined(FRAME_AVATAR_BUTTON)
65 // Space between the new avatar button and the minimize button.
66 const int kNewAvatarButtonOffset
= 5;
68 // Space between left edge of window and tabstrip.
69 const int kTabstripLeftSpacing
= 0;
70 // Space between right edge of tabstrip and maximize button.
71 const int kTabstripRightSpacing
= 10;
72 // Height of the shadow of the content area, at the top of the toolbar.
73 const int kContentShadowHeight
= 1;
74 // Space between top of window and top of tabstrip for tall headers, such as
75 // for restored windows, apps, etc.
76 const int kTabstripTopSpacingTall
= 7;
77 // Space between top of window and top of tabstrip for short headers, such as
78 // for maximized windows, pop-ups, etc.
79 const int kTabstripTopSpacingShort
= 0;
80 // Height of the shadow in the tab image, used to ensure clicks in the shadow
81 // area still drag restored windows. This keeps the clickable area large enough
83 const int kTabShadowHeight
= 4;
85 // Combines View::ConvertPointToTarget() and View::HitTest() for a given
86 // |point|. Converts |point| from |src| to |dst| and hit tests it against |dst|.
87 bool ConvertedHitTest(views::View
* src
,
89 const gfx::Point
& point
) {
92 gfx::Point
converted_point(point
);
93 views::View::ConvertPointToTarget(src
, dst
, &converted_point
);
94 return dst
->HitTestPoint(converted_point
);
99 ///////////////////////////////////////////////////////////////////////////////
100 // BrowserNonClientFrameViewAsh, public:
103 const char BrowserNonClientFrameViewAsh::kViewClassName
[] =
104 "BrowserNonClientFrameViewAsh";
106 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
108 BrowserView
* browser_view
)
109 : BrowserNonClientFrameView(frame
, browser_view
),
110 caption_button_container_(nullptr),
111 web_app_left_header_view_(nullptr),
112 window_icon_(nullptr),
113 frame_border_hit_test_controller_(
114 new ash::FrameBorderHitTestController(frame
)) {
115 ash::Shell::GetInstance()->AddShellObserver(this);
118 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
119 ash::Shell::GetInstance()->RemoveShellObserver(this);
122 void BrowserNonClientFrameViewAsh::Init() {
123 caption_button_container_
= new ash::FrameCaptionButtonContainerView(frame());
124 caption_button_container_
->UpdateSizeButtonVisibility();
125 AddChildView(caption_button_container_
);
127 // Initializing the TabIconView is expensive, so only do it if we need to.
128 if (browser_view()->ShouldShowWindowIcon()) {
129 window_icon_
= new TabIconView(this, nullptr);
130 window_icon_
->set_is_light(true);
131 AddChildView(window_icon_
);
132 window_icon_
->Update();
137 // HeaderPainter handles layout.
138 if (UsePackagedAppHeaderStyle()) {
139 ash::DefaultHeaderPainter
* header_painter
= new ash::DefaultHeaderPainter
;
140 header_painter_
.reset(header_painter
);
141 header_painter
->Init(frame(), this, caption_button_container_
);
143 header_painter
->UpdateLeftHeaderView(window_icon_
);
145 } else if (UseWebAppHeaderStyle()) {
146 web_app_left_header_view_
= new WebAppLeftHeaderView(browser_view());
147 AddChildView(web_app_left_header_view_
);
149 ash::DefaultHeaderPainter
* header_painter
= new ash::DefaultHeaderPainter
;
150 header_painter_
.reset(header_painter
);
151 header_painter
->Init(frame(), this, caption_button_container_
);
152 header_painter
->UpdateLeftHeaderView(web_app_left_header_view_
);
154 BrowserHeaderPainterAsh
* header_painter
= new BrowserHeaderPainterAsh
;
155 header_painter_
.reset(header_painter
);
156 header_painter
->Init(frame(), browser_view(), this, window_icon_
,
157 caption_button_container_
);
161 ///////////////////////////////////////////////////////////////////////////////
162 // BrowserNonClientFrameView:
164 gfx::Rect
BrowserNonClientFrameViewAsh::GetBoundsForTabStrip(
165 views::View
* tabstrip
) const {
169 // When the tab strip is painted in the immersive fullscreen light bar style,
170 // the caption buttons and the avatar button are not visible. However, their
171 // bounds are still used to compute the tab strip bounds so that the tabs have
172 // the same horizontal position when the tab strip is painted in the immersive
173 // light bar style as when the top-of-window views are revealed.
174 int left_inset
= GetTabStripLeftInset();
175 int right_inset
= GetTabStripRightInset();
176 return gfx::Rect(left_inset
,
178 std::max(0, width() - left_inset
- right_inset
),
179 tabstrip
->GetPreferredSize().height());
182 int BrowserNonClientFrameViewAsh::GetTopInset() const {
183 if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle())
186 if (browser_view()->IsTabStripVisible()) {
187 if (frame()->IsMaximized() || frame()->IsFullscreen())
188 return kTabstripTopSpacingShort
;
190 return kTabstripTopSpacingTall
;
193 if (UsePackagedAppHeaderStyle() || UseWebAppHeaderStyle())
194 return header_painter_
->GetHeaderHeight();
196 int caption_buttons_bottom
= caption_button_container_
->bounds().bottom();
198 // The toolbar partially overlaps the caption buttons.
199 if (browser_view()->IsToolbarVisible())
200 return caption_buttons_bottom
- kContentShadowHeight
;
202 return caption_buttons_bottom
+ kClientEdgeThickness
;
205 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
206 return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
209 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running
) {
211 window_icon_
->Update();
214 void BrowserNonClientFrameViewAsh::UpdateToolbar() {
215 if (web_app_left_header_view_
)
216 web_app_left_header_view_
->Update();
219 views::View
* BrowserNonClientFrameViewAsh::GetLocationIconView() const {
220 if (web_app_left_header_view_
)
221 return web_app_left_header_view_
->GetLocationIconView();
226 ///////////////////////////////////////////////////////////////////////////////
227 // views::NonClientFrameView:
229 gfx::Rect
BrowserNonClientFrameViewAsh::GetBoundsForClientView() const {
230 // The ClientView must be flush with the top edge of the widget so that the
231 // web contents can take up the entire screen in immersive fullscreen (with
232 // or without the top-of-window views revealed). When in immersive fullscreen
233 // and the top-of-window views are revealed, the TopContainerView paints the
234 // window header by redirecting paints from its background to
235 // BrowserNonClientFrameViewAsh.
239 gfx::Rect
BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
240 const gfx::Rect
& client_bounds
) const {
241 return client_bounds
;
244 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point
& point
) {
245 int hit_test
= ash::FrameBorderHitTestController::NonClientHitTest(this,
246 caption_button_container_
, point
);
248 #if defined(FRAME_AVATAR_BUTTON)
249 if (hit_test
== HTCAPTION
&& new_avatar_button() &&
250 ConvertedHitTest(this, new_avatar_button(), point
)) {
255 // See if the point is actually within the web app back button.
256 if (hit_test
== HTCAPTION
&& web_app_left_header_view_
&&
257 ConvertedHitTest(this, web_app_left_header_view_
, point
)) {
261 #if defined(ENABLE_SUPERVISED_USERS)
262 // ...or within the avatar label, if it's a supervised user.
263 if (hit_test
== HTCAPTION
&& supervised_user_avatar_label() &&
264 ConvertedHitTest(this, supervised_user_avatar_label(), point
)) {
269 // When the window is restored we want a large click target above the tabs
270 // to drag the window, so redirect clicks in the tab's shadow to caption.
271 if (hit_test
== HTCLIENT
&&
272 !(frame()->IsMaximized() || frame()->IsFullscreen())) {
273 // Convert point to client coordinates.
274 gfx::Point
client_point(point
);
275 View::ConvertPointToTarget(this, frame()->client_view(), &client_point
);
276 // Report hits in shadow at top of tabstrip as caption.
277 gfx::Rect
tabstrip_bounds(browser_view()->tabstrip()->bounds());
278 if (client_point
.y() < tabstrip_bounds
.y() + kTabShadowHeight
)
279 hit_test
= HTCAPTION
;
284 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size
& size
,
285 gfx::Path
* window_mask
) {
286 // Aura does not use window masks.
289 void BrowserNonClientFrameViewAsh::ResetWindowControls() {
290 // Hide the caption buttons in immersive fullscreen when the tab light bar
291 // is visible because it's confusing when the user hovers or clicks in the
292 // top-right of the screen and hits one.
293 bool button_visibility
= !UseImmersiveLightbarHeaderStyle();
294 caption_button_container_
->SetVisible(button_visibility
);
296 caption_button_container_
->ResetWindowControls();
299 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
301 window_icon_
->SchedulePaint();
304 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
305 if (!frame()->IsFullscreen())
306 header_painter_
->SchedulePaintForTitle();
309 void BrowserNonClientFrameViewAsh::SizeConstraintsChanged() {
312 ///////////////////////////////////////////////////////////////////////////////
315 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas
* canvas
) {
319 if (UseImmersiveLightbarHeaderStyle()) {
320 PaintImmersiveLightbarStyleHeader(canvas
);
324 caption_button_container_
->SetPaintAsActive(ShouldPaintAsActive());
325 if (web_app_left_header_view_
)
326 web_app_left_header_view_
->SetPaintAsActive(ShouldPaintAsActive());
328 ash::HeaderPainter::Mode header_mode
= ShouldPaintAsActive() ?
329 ash::HeaderPainter::MODE_ACTIVE
: ash::HeaderPainter::MODE_INACTIVE
;
330 header_painter_
->PaintHeader(canvas
, header_mode
);
331 if (browser_view()->IsToolbarVisible())
332 PaintToolbarBackground(canvas
);
333 else if (!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle())
334 PaintContentEdge(canvas
);
337 void BrowserNonClientFrameViewAsh::Layout() {
338 // The header must be laid out before computing |painted_height| because the
339 // computation of |painted_height| for app and popup windows depends on the
340 // position of the window controls.
341 header_painter_
->LayoutHeader();
343 int painted_height
= 0;
344 if (browser_view()->IsTabStripVisible()) {
345 painted_height
= GetTopInset() +
346 browser_view()->tabstrip()->GetPreferredSize().height();
347 } else if (browser_view()->IsToolbarVisible()) {
348 // Paint the header so that it overlaps with the top few pixels of the
349 // toolbar because the top few pixels of the toolbar are not opaque.
350 painted_height
= GetTopInset() + kFrameShadowThickness
* 2;
352 painted_height
= GetTopInset();
354 header_painter_
->SetHeaderHeightForPainting(painted_height
);
356 if (avatar_button()) {
358 header_painter_
->UpdateLeftViewXInset(avatar_button()->bounds().right());
360 #if defined(FRAME_AVATAR_BUTTON)
361 if (new_avatar_button())
362 LayoutNewStyleAvatar();
364 header_painter_
->UpdateLeftViewXInset(
365 ash::HeaderPainterUtil::GetDefaultLeftViewXInset());
366 BrowserNonClientFrameView::Layout();
369 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
370 return kViewClassName
;
373 void BrowserNonClientFrameViewAsh::GetAccessibleState(
374 ui::AXViewState
* state
) {
375 state
->role
= ui::AX_ROLE_TITLE_BAR
;
378 gfx::Size
BrowserNonClientFrameViewAsh::GetMinimumSize() const {
379 gfx::Size
min_client_view_size(frame()->client_view()->GetMinimumSize());
380 int min_width
= std::max(header_painter_
->GetMinimumHeaderWidth(),
381 min_client_view_size
.width());
382 if (browser_view()->IsTabStripVisible()) {
383 // Ensure that the minimum width is enough to hold a minimum width tab strip
384 // at its usual insets.
385 int min_tabstrip_width
=
386 browser_view()->tabstrip()->GetMinimumSize().width();
387 min_width
= std::max(min_width
,
388 min_tabstrip_width
+ GetTabStripLeftInset() + GetTabStripRightInset());
390 return gfx::Size(min_width
, min_client_view_size
.height());
393 void BrowserNonClientFrameViewAsh::
394 ChildPreferredSizeChanged(views::View
* child
) {
395 // FrameCaptionButtonContainerView animates the visibility changes in
396 // UpdateSizeButtonVisibility(false). Due to this a new size is not available
397 // until the completion of the animation. Layout in response to the preferred
399 if (!browser_view()->initialized())
401 bool needs_layout
= child
== caption_button_container_
;
402 #if defined(FRAME_AVATAR_BUTTON)
403 needs_layout
= needs_layout
|| child
== new_avatar_button();
407 frame()->GetRootView()->Layout();
411 ///////////////////////////////////////////////////////////////////////////////
412 // ash::ShellObserver:
414 void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
415 caption_button_container_
->UpdateSizeButtonVisibility();
417 frame()->client_view()->InvalidateLayout();
418 frame()->GetRootView()->Layout();
421 void BrowserNonClientFrameViewAsh::OnMaximizeModeEnded() {
422 caption_button_container_
->UpdateSizeButtonVisibility();
424 frame()->client_view()->InvalidateLayout();
425 frame()->GetRootView()->Layout();
428 ///////////////////////////////////////////////////////////////////////////////
429 // chrome::TabIconViewModel:
431 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
432 // This function is queried during the creation of the window as the
433 // TabIconView we host is initialized, so we need to null check the selected
434 // WebContents because in this condition there is not yet a selected tab.
435 content::WebContents
* current_tab
= browser_view()->GetActiveWebContents();
436 return current_tab
? current_tab
->IsLoading() : false;
439 gfx::ImageSkia
BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() {
440 views::WidgetDelegate
* delegate
= frame()->widget_delegate();
442 return gfx::ImageSkia();
443 return delegate
->GetWindowIcon();
446 ///////////////////////////////////////////////////////////////////////////////
447 // views::ButtonListener:
449 void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button
* sender
,
450 const ui::Event
& event
) {
451 #if !defined(FRAME_AVATAR_BUTTON)
454 DCHECK(sender
== new_avatar_button());
455 int command
= IDC_SHOW_AVATAR_MENU
;
456 if (event
.IsMouseEvent() &&
457 static_cast<const ui::MouseEvent
&>(event
).IsRightMouseButton()) {
458 command
= IDC_SHOW_FAST_USER_SWITCHER
;
460 chrome::ExecuteCommand(browser_view()->browser(), command
);
464 ///////////////////////////////////////////////////////////////////////////////
465 // BrowserNonClientFrameViewAsh, protected:
467 // BrowserNonClientFrameView:
468 void BrowserNonClientFrameViewAsh::UpdateNewAvatarButtonImpl() {
469 #if defined(FRAME_AVATAR_BUTTON)
470 UpdateNewAvatarButton(this, NewAvatarButton::NATIVE_BUTTON
);
474 ///////////////////////////////////////////////////////////////////////////////
475 // BrowserNonClientFrameViewAsh, private:
477 // views::NonClientFrameView:
478 bool BrowserNonClientFrameViewAsh::DoesIntersectRect(
479 const views::View
* target
,
480 const gfx::Rect
& rect
) const {
481 CHECK_EQ(target
, this);
482 if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect
)) {
483 // |rect| is outside BrowserNonClientFrameViewAsh's bounds.
487 TabStrip
* tabstrip
= browser_view()->tabstrip();
488 if (tabstrip
&& browser_view()->IsTabStripVisible()) {
489 // Claim |rect| only if it is above the bottom of the tabstrip in a non-tab
491 gfx::RectF
rect_in_tabstrip_coords_f(rect
);
492 View::ConvertRectToTarget(this, tabstrip
, &rect_in_tabstrip_coords_f
);
493 gfx::Rect rect_in_tabstrip_coords
= gfx::ToEnclosingRect(
494 rect_in_tabstrip_coords_f
);
496 if (rect_in_tabstrip_coords
.y() > tabstrip
->height())
499 return !tabstrip
->HitTestRect(rect_in_tabstrip_coords
) ||
500 tabstrip
->IsRectInWindowCaption(rect_in_tabstrip_coords
);
503 // Claim |rect| if it is above the top of the topmost view in the client area.
504 return rect
.y() < GetTopInset();
507 int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
508 return avatar_button() ? kAvatarSideSpacing
+
509 browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing
:
510 kTabstripLeftSpacing
;
513 int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
514 int tabstrip_width
= kTabstripRightSpacing
+
515 caption_button_container_
->GetPreferredSize().width();
517 #if defined(FRAME_AVATAR_BUTTON)
518 if (new_avatar_button()) {
519 tabstrip_width
+= kNewAvatarButtonOffset
+
520 new_avatar_button()->GetPreferredSize().width();
524 return tabstrip_width
;
527 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const {
528 ImmersiveModeController
* immersive_controller
=
529 browser_view()->immersive_mode_controller();
530 return immersive_controller
->IsEnabled() &&
531 !immersive_controller
->IsRevealed() &&
532 browser_view()->IsTabStripVisible();
535 bool BrowserNonClientFrameViewAsh::UsePackagedAppHeaderStyle() const {
536 // Use the packaged app style for apps that aren't using the newer WebApp
538 return browser_view()->browser()->is_app() && !UseWebAppHeaderStyle();
541 bool BrowserNonClientFrameViewAsh::UseWebAppHeaderStyle() const {
542 return browser_view()->browser()->SupportsWindowFeature(
543 Browser::FEATURE_WEBAPPFRAME
);
546 void BrowserNonClientFrameViewAsh::LayoutAvatar() {
547 DCHECK(avatar_button());
548 #if !defined(OS_CHROMEOS)
549 // ChromeOS shows avatar on V1 app.
550 DCHECK(browser_view()->IsTabStripVisible());
552 gfx::ImageSkia incognito_icon
= browser_view()->GetOTRAvatarIcon();
554 int avatar_bottom
= GetTopInset() +
555 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing
;
556 int avatar_restored_y
= avatar_bottom
- incognito_icon
.height();
558 (browser_view()->IsTabStripVisible() &&
559 (frame()->IsMaximized() || frame()->IsFullscreen())) ?
560 GetTopInset() + kContentShadowHeight
: avatar_restored_y
;
562 // Hide the incognito icon in immersive fullscreen when the tab light bar is
563 // visible because the header is too short for the icognito icon to be
565 bool avatar_visible
= !UseImmersiveLightbarHeaderStyle();
566 int avatar_height
= avatar_visible
? avatar_bottom
- avatar_y
: 0;
568 gfx::Rect
avatar_bounds(kAvatarSideSpacing
,
570 incognito_icon
.width(),
572 avatar_button()->SetBoundsRect(avatar_bounds
);
573 avatar_button()->SetVisible(avatar_visible
);
576 #if defined(FRAME_AVATAR_BUTTON)
577 void BrowserNonClientFrameViewAsh::LayoutNewStyleAvatar() {
578 DCHECK(switches::IsNewAvatarMenu());
580 gfx::Size button_size
= new_avatar_button()->GetPreferredSize();
581 int button_x
= width() -
582 caption_button_container_
->GetPreferredSize().width() -
583 kNewAvatarButtonOffset
- button_size
.width();
585 new_avatar_button()->SetBounds(
589 caption_button_container_
->GetPreferredSize().height());
593 bool BrowserNonClientFrameViewAsh::ShouldPaint() const {
594 if (!frame()->IsFullscreen())
597 // We need to paint when in immersive fullscreen and either:
598 // - The top-of-window views are revealed.
599 // - The lightbar style tabstrip is visible.
600 ImmersiveModeController
* immersive_mode_controller
=
601 browser_view()->immersive_mode_controller();
602 return immersive_mode_controller
->IsEnabled() &&
603 (immersive_mode_controller
->IsRevealed() ||
604 UseImmersiveLightbarHeaderStyle());
607 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader(
608 gfx::Canvas
* canvas
) {
609 // The light bar header is not themed because theming it does not look good.
611 gfx::Rect(width(), header_painter_
->GetHeaderHeightForPainting()),
615 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas
* canvas
) {
616 gfx::Rect
toolbar_bounds(browser_view()->GetToolbarBounds());
617 if (toolbar_bounds
.IsEmpty())
619 gfx::Point
toolbar_origin(toolbar_bounds
.origin());
620 View::ConvertPointToTarget(browser_view(), this, &toolbar_origin
);
621 toolbar_bounds
.set_origin(toolbar_origin
);
623 int x
= toolbar_bounds
.x();
624 int w
= toolbar_bounds
.width();
625 int y
= toolbar_bounds
.y();
626 int h
= toolbar_bounds
.height();
627 ui::ThemeProvider
* tp
= GetThemeProvider();
629 if (ui::MaterialDesignController::IsModeMaterial()) {
630 // Paint the main toolbar image. Since this image is also used to draw the
631 // tab background, we must use the tab strip offset to compute the image
632 // source y position. If you have to debug this code use an image editor
633 // to paint a diagonal line through the toolbar image and ensure it lines up
634 // across the tab and toolbar.
635 gfx::ImageSkia
* theme_toolbar
= tp
->GetImageSkiaNamed(IDR_THEME_TOOLBAR
);
636 canvas
->TileImageInt(
638 x
+ GetThemeBackgroundXInset(),
641 w
, theme_toolbar
->height());
643 // Draw the content/toolbar separator.
644 toolbar_bounds
.Inset(kClientEdgeThickness
, 0);
645 BrowserView::Paint1pxHorizontalLine(
647 ThemeProperties::GetDefaultColor(
648 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
),
651 // Gross hack: We split the toolbar images into two pieces, since sometimes
652 // (popup mode) the toolbar isn't tall enough to show the whole image. The
653 // split happens between the top shadow section and the bottom gradient
654 // section so that we never break the gradient.
655 // NOTE(pkotwicz): If the computation for |bottom_y| is changed, Layout()
656 // must be changed as well.
657 int split_point
= kFrameShadowThickness
* 2;
658 int bottom_y
= y
+ split_point
;
659 int bottom_edge_height
= h
- split_point
;
661 canvas
->FillRect(gfx::Rect(x
, bottom_y
, w
, bottom_edge_height
),
662 tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
));
664 // Paint the main toolbar image. Since this image is also used to draw the
665 // tab background, we must use the tab strip offset to compute the image
666 // source y position. If you have to debug this code use an image editor
667 // to paint a diagonal line through the toolbar image and ensure it lines up
668 // across the tab and toolbar.
669 gfx::ImageSkia
* theme_toolbar
= tp
->GetImageSkiaNamed(IDR_THEME_TOOLBAR
);
670 canvas
->TileImageInt(
672 x
+ GetThemeBackgroundXInset(),
673 bottom_y
- GetTopInset(),
675 w
, theme_toolbar
->height());
677 // The pre-material design content area line has a shadow that extends a
678 // couple of pixels above the toolbar bounds.
679 const int kContentShadowHeight
= 2;
680 gfx::ImageSkia
* toolbar_top
= tp
->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP
);
681 canvas
->TileImageInt(*toolbar_top
,
683 x
, y
- kContentShadowHeight
,
684 w
, split_point
+ kContentShadowHeight
+ 1);
686 // Draw the "lightening" shade line around the edges of the toolbar.
687 gfx::ImageSkia
* toolbar_left
=
688 tp
->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT
);
689 canvas
->TileImageInt(*toolbar_left
,
691 x
+ kClientEdgeThickness
,
692 y
+ kClientEdgeThickness
+ kContentShadowHeight
,
693 toolbar_left
->width(), theme_toolbar
->height());
694 gfx::ImageSkia
* toolbar_right
=
695 tp
->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT
);
696 canvas
->TileImageInt(*toolbar_right
,
698 w
- toolbar_right
->width() - 2 * kClientEdgeThickness
,
699 y
+ kClientEdgeThickness
+ kContentShadowHeight
,
700 toolbar_right
->width(), theme_toolbar
->height());
702 // Draw the content/toolbar separator.
704 gfx::Rect(x
+ kClientEdgeThickness
,
705 toolbar_bounds
.bottom() - kClientEdgeThickness
,
706 w
- (2 * kClientEdgeThickness
),
707 kClientEdgeThickness
),
708 ThemeProperties::GetDefaultColor(
709 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
));
713 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas
* canvas
) {
714 DCHECK(!UsePackagedAppHeaderStyle() && !UseWebAppHeaderStyle());
715 canvas
->FillRect(gfx::Rect(0, caption_button_container_
->bounds().bottom(),
716 width(), kClientEdgeThickness
),
717 ThemeProperties::GetDefaultColor(
718 ThemeProperties::COLOR_TOOLBAR_SEPARATOR
));