• 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/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|.
ConvertedHitTest(views::View * src,views::View * dst,const gfx::Point & point)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 
BrowserNonClientFrameViewAsh(BrowserFrame * frame,BrowserView * browser_view)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 
~BrowserNonClientFrameViewAsh()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 
Init()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 
GetBoundsForTabStrip(views::View * tabstrip) const167 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 
GetTopInset() const185 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 
GetThemeBackgroundXInset() const208 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const {
209   return ash::HeaderPainterUtil::GetThemeBackgroundXInset();
210 }
211 
UpdateThrobber(bool running)212 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) {
213   if (window_icon_)
214     window_icon_->Update();
215 }
216 
217 ///////////////////////////////////////////////////////////////////////////////
218 // views::NonClientFrameView:
219 
GetBoundsForClientView() const220 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 
GetWindowBoundsForClientBounds(const gfx::Rect & client_bounds) const230 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds(
231     const gfx::Rect& client_bounds) const {
232   return client_bounds;
233 }
234 
NonClientHitTest(const gfx::Point & point)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 
GetWindowMask(const gfx::Size & size,gfx::Path * window_mask)266 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size,
267                                                  gfx::Path* window_mask) {
268   // Aura does not use window masks.
269 }
270 
ResetWindowControls()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 
UpdateWindowIcon()281 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
282   if (window_icon_)
283     window_icon_->SchedulePaint();
284 }
285 
UpdateWindowTitle()286 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() {
287   if (!frame()->IsFullscreen())
288     header_painter_->SchedulePaintForTitle();
289 }
290 
SizeConstraintsChanged()291 void BrowserNonClientFrameViewAsh::SizeConstraintsChanged() {
292 }
293 
294 ///////////////////////////////////////////////////////////////////////////////
295 // views::View:
296 
OnPaint(gfx::Canvas * canvas)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 
Layout()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 
GetClassName() const352 const char* BrowserNonClientFrameViewAsh::GetClassName() const {
353   return kViewClassName;
354 }
355 
GetAccessibleState(ui::AXViewState * state)356 void BrowserNonClientFrameViewAsh::GetAccessibleState(
357     ui::AXViewState* state) {
358   state->role = ui::AX_ROLE_TITLE_BAR;
359 }
360 
GetMinimumSize() const361 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::
ChildPreferredSizeChanged(views::View * child)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 
OnMaximizeModeStarted()390 void BrowserNonClientFrameViewAsh::OnMaximizeModeStarted() {
391   caption_button_container_->UpdateSizeButtonVisibility();
392   InvalidateLayout();
393   frame()->client_view()->InvalidateLayout();
394   frame()->GetRootView()->Layout();
395 }
396 
OnMaximizeModeEnded()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 
ShouldTabIconViewAnimate() const407 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 
GetFaviconForTabIconView()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 
EnabledStateChangedForCommand(int id,bool enabled)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 
ButtonPressed(views::Button * sender,const ui::Event & event)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:
DoesIntersectRect(const views::View * target,const gfx::Rect & rect) const444 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 
GetTabStripLeftInset() const473 int BrowserNonClientFrameViewAsh::GetTabStripLeftInset() const {
474   return avatar_button() ? kAvatarSideSpacing +
475       browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing :
476       kTabstripLeftSpacing;
477 }
478 
GetTabStripRightInset() const479 int BrowserNonClientFrameViewAsh::GetTabStripRightInset() const {
480   return caption_button_container_->GetPreferredSize().width() +
481       kTabstripRightSpacing;
482 }
483 
UseImmersiveLightbarHeaderStyle() const484 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 
UsePackagedAppHeaderStyle() const492 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 
UseWebAppHeaderStyle() const498 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 
LayoutAvatar()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 
ShouldPaint() const535 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 
PaintImmersiveLightbarStyleHeader(gfx::Canvas * canvas)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 
PaintToolbarBackground(gfx::Canvas * canvas)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 
PaintContentEdge(gfx::Canvas * canvas)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 
UpdateBackButtonState(bool enabled)639 void BrowserNonClientFrameViewAsh::UpdateBackButtonState(bool enabled) {
640   web_app_back_button_->SetState(enabled ? views::Button::STATE_NORMAL
641                                          : views::Button::STATE_DISABLED);
642 }
643