opaque_browser_frame_view.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/opaque_browser_frame_view.h"
6
7#include <algorithm>
8#include <string>
9
10#include "base/command_line.h"
11#include "base/compiler_specific.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/themes/theme_properties.h"
15#include "chrome/browser/ui/views/avatar_menu_button.h"
16#include "chrome/browser/ui/views/frame/browser_frame.h"
17#include "chrome/browser/ui/views/frame/browser_view.h"
18#include "chrome/browser/ui/views/tab_icon_view.h"
19#include "chrome/browser/ui/views/tabs/tab_strip.h"
20#include "chrome/browser/ui/views/toolbar_view.h"
21#include "chrome/common/chrome_notification_types.h"
22#include "chrome/common/chrome_switches.h"
23#include "chrome/common/pref_names.h"
24#include "content/public/browser/notification_service.h"
25#include "content/public/browser/web_contents.h"
26#include "grit/chromium_strings.h"
27#include "grit/generated_resources.h"
28#include "grit/theme_resources.h"
29#include "grit/ui_resources.h"
30#include "ui/base/accessibility/accessible_view_state.h"
31#include "ui/base/hit_test.h"
32#include "ui/base/l10n/l10n_util.h"
33#include "ui/base/resource/resource_bundle.h"
34#include "ui/base/theme_provider.h"
35#include "ui/gfx/canvas.h"
36#include "ui/gfx/font.h"
37#include "ui/gfx/image/image.h"
38#include "ui/gfx/image/image_skia.h"
39#include "ui/gfx/path.h"
40#include "ui/views/controls/button/image_button.h"
41#include "ui/views/controls/image_view.h"
42#include "ui/views/controls/label.h"
43#include "ui/views/layout/layout_constants.h"
44#include "ui/views/widget/root_view.h"
45#include "ui/views/window/frame_background.h"
46#include "ui/views/window/window_shape.h"
47
48#if defined(OS_WIN)
49#include "win8/util/win8_util.h"
50#endif  // OS_WIN
51
52using content::WebContents;
53
54namespace {
55
56// The frame border is only visible in restored mode and is hardcoded to 4 px on
57// each side regardless of the system window border size.
58const int kFrameBorderThickness = 4;
59// Besides the frame border, there's another 9 px of empty space atop the
60// window in restored mode, to use to drag the window around.
61const int kNonClientRestoredExtraThickness = 9;
62// While resize areas on Windows are normally the same size as the window
63// borders, our top area is shrunk by 1 px to make it easier to move the window
64// around with our thinner top grabbable strip.  (Incidentally, our side and
65// bottom resize areas don't match the frame border thickness either -- they
66// span the whole nonclient area, so there's no "dead zone" for the mouse.)
67const int kTopResizeAdjust = 1;
68// In the window corners, the resize areas don't actually expand bigger, but the
69// 16 px at the end of each edge triggers diagonal resizing.
70const int kResizeAreaCornerSize = 16;
71// The titlebar never shrinks too short to show the caption button plus some
72// padding below it.
73const int kCaptionButtonHeightWithPadding = 19;
74// The content left/right images have a shadow built into them.
75const int kContentEdgeShadowThickness = 2;
76// The titlebar has a 2 px 3D edge along the top and bottom.
77const int kTitlebarTopAndBottomEdgeThickness = 2;
78// The icon is inset 2 px from the left frame border.
79const int kIconLeftSpacing = 2;
80// The icon never shrinks below 16 px on a side.
81const int kIconMinimumSize = 16;
82// There is a 4 px gap between the icon and the title text.
83const int kIconTitleSpacing = 4;
84// There is a 5 px gap between the title text and the caption buttons.
85const int kTitleLogoSpacing = 5;
86// The avatar ends 2 px above the bottom of the tabstrip (which, given the
87// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
88// user).
89const int kAvatarBottomSpacing = 2;
90// Space between the frame border and the left edge of the avatar.
91const int kAvatarLeftSpacing = 2;
92// Space between the right edge of the avatar and the tabstrip.
93const int kAvatarRightSpacing = -2;
94// The top 3 px of the tabstrip is shadow; in maximized mode we push this off
95// the top of the screen so the tabs appear flush against the screen edge.
96const int kTabstripTopShadowThickness = 3;
97// In restored mode, the New Tab button isn't at the same height as the caption
98// buttons, but the space will look cluttered if it actually slides under them,
99// so we stop it when the gap between the two is down to 5 px.
100const int kNewTabCaptionRestoredSpacing = 5;
101// In maximized mode, where the New Tab button and the caption buttons are at
102// similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid
103// looking too cluttered.
104const int kNewTabCaptionMaximizedSpacing = 16;
105// How far to indent the tabstrip from the left side of the screen when there
106// is no avatar icon.
107const int kTabStripIndent = -6;
108
109// Converts |bounds| from |src|'s coordinate system to |dst|, and checks if
110// |pt| is contained within.
111bool ConvertedContainsCheck(gfx::Rect bounds, const views::View* src,
112                            const views::View* dst, const gfx::Point& pt) {
113  DCHECK(src);
114  DCHECK(dst);
115  gfx::Point origin(bounds.origin());
116  views::View::ConvertPointToTarget(src, dst, &origin);
117  bounds.set_origin(origin);
118  return bounds.Contains(pt);
119}
120
121bool ShouldAddDefaultCaptionButtons() {
122#if defined(OS_WIN)
123  return !win8::IsSingleWindowMetroMode();
124#endif  // OS_WIN
125  return true;
126}
127
128}  // namespace
129
130///////////////////////////////////////////////////////////////////////////////
131// OpaqueBrowserFrameView, public:
132
133OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
134                                               BrowserView* browser_view)
135    : BrowserNonClientFrameView(frame, browser_view),
136      minimize_button_(NULL),
137      maximize_button_(NULL),
138      restore_button_(NULL),
139      close_button_(NULL),
140      window_icon_(NULL),
141      window_title_(NULL),
142      frame_background_(new views::FrameBackground()) {
143  if (ShouldAddDefaultCaptionButtons()) {
144    minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE,
145                                               IDR_MINIMIZE_H,
146                                               IDR_MINIMIZE_P,
147                                               IDR_MINIMIZE_BUTTON_MASK,
148                                               IDS_ACCNAME_MINIMIZE);
149    maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE,
150                                               IDR_MAXIMIZE_H,
151                                               IDR_MAXIMIZE_P,
152                                               IDR_MAXIMIZE_BUTTON_MASK,
153                                               IDS_ACCNAME_MAXIMIZE);
154    restore_button_ = InitWindowCaptionButton(IDR_RESTORE,
155                                              IDR_RESTORE_H,
156                                              IDR_RESTORE_P,
157                                              IDR_RESTORE_BUTTON_MASK,
158                                              IDS_ACCNAME_RESTORE);
159    close_button_ = InitWindowCaptionButton(IDR_CLOSE,
160                                            IDR_CLOSE_H,
161                                            IDR_CLOSE_P,
162                                            IDR_CLOSE_BUTTON_MASK,
163                                            IDS_ACCNAME_CLOSE);
164  }
165
166  // Initializing the TabIconView is expensive, so only do it if we need to.
167  if (browser_view->ShouldShowWindowIcon()) {
168    window_icon_ = new TabIconView(this);
169    window_icon_->set_is_light(true);
170    AddChildView(window_icon_);
171    window_icon_->Update();
172  }
173
174  window_title_ = new views::Label(browser_view->GetWindowTitle(),
175                                   BrowserFrame::GetTitleFont());
176  window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
177  window_title_->SetEnabledColor(SK_ColorWHITE);
178  // TODO(msw): Use a transparent background color as a workaround to use the
179  // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts.
180  window_title_->SetBackgroundColor(0x00000000);
181  window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
182  AddChildView(window_title_);
183
184  UpdateAvatarInfo();
185  if (!browser_view->IsOffTheRecord()) {
186    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
187                   content::NotificationService::AllSources());
188  }
189}
190
191OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
192}
193
194///////////////////////////////////////////////////////////////////////////////
195// OpaqueBrowserFrameView, protected:
196
197int OpaqueBrowserFrameView::GetReservedHeight() const {
198  return 0;
199}
200
201gfx::Rect OpaqueBrowserFrameView::GetBoundsForReservedArea() const {
202  gfx::Rect client_view_bounds = CalculateClientAreaBounds(width(), height());
203  return gfx::Rect(
204      client_view_bounds.x(),
205      client_view_bounds.y() + client_view_bounds.height(),
206      client_view_bounds.width(),
207      GetReservedHeight());
208}
209
210int OpaqueBrowserFrameView::NonClientTopBorderHeight(
211    bool restored) const {
212  views::WidgetDelegate* delegate = frame()->widget_delegate();
213  // |delegate| may be NULL if called from callback of InputMethodChanged while
214  // a window is being destroyed.
215  // See more discussion at http://crosbug.com/8958
216  if (delegate && delegate->ShouldShowWindowTitle()) {
217    return std::max(FrameBorderThickness(restored) + IconSize(),
218        CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) +
219        TitlebarBottomThickness(restored);
220  }
221
222  return FrameBorderThickness(restored) -
223      ((browser_view()->IsTabStripVisible() &&
224          !restored && !frame()->ShouldLeaveOffsetNearTopBorder())
225              ? kTabstripTopShadowThickness : 0);
226}
227
228///////////////////////////////////////////////////////////////////////////////
229// OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
230
231gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
232    views::View* tabstrip) const {
233  if (!tabstrip)
234    return gfx::Rect();
235
236  gfx::Rect bounds = GetBoundsForTabStripAndAvatarArea(tabstrip);
237  int space_left_of_tabstrip = browser_view()->ShouldShowAvatar() ?
238      (kAvatarLeftSpacing + avatar_bounds_.width() + kAvatarRightSpacing) :
239      kTabStripIndent;
240  if (avatar_label() && avatar_label()->bounds().width()) {
241    space_left_of_tabstrip += views::kRelatedControlHorizontalSpacing +
242                              avatar_label()->bounds().width();
243  }
244  bounds.Inset(space_left_of_tabstrip, 0, 0, 0);
245  return bounds;
246}
247
248BrowserNonClientFrameView::TabStripInsets
249OpaqueBrowserFrameView::GetTabStripInsets(bool restored) const {
250  int top = NonClientTopBorderHeight(restored) + ((!restored &&
251      (!frame()->ShouldLeaveOffsetNearTopBorder() ||
252      frame()->IsFullscreen())) ?
253      0 : kNonClientRestoredExtraThickness);
254  // TODO: include OTR and caption.
255  return TabStripInsets(top, 0, 0);
256}
257
258int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
259  return 0;
260}
261
262void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
263  if (window_icon_)
264    window_icon_->Update();
265}
266
267gfx::Size OpaqueBrowserFrameView::GetMinimumSize() {
268  gfx::Size min_size(browser_view()->GetMinimumSize());
269  int border_thickness = NonClientBorderThickness();
270  min_size.Enlarge(2 * border_thickness,
271                   NonClientTopBorderHeight(false) + border_thickness);
272
273  views::WidgetDelegate* delegate = frame()->widget_delegate();
274  int min_titlebar_width = (2 * FrameBorderThickness(false)) +
275      kIconLeftSpacing +
276      (delegate && delegate->ShouldShowWindowIcon() ?
277       (IconSize() + kTitleLogoSpacing) : 0);
278#if !defined(OS_CHROMEOS)
279  if (ShouldAddDefaultCaptionButtons()) {
280    min_titlebar_width +=
281        minimize_button_->GetMinimumSize().width() +
282        restore_button_->GetMinimumSize().width() +
283        close_button_->GetMinimumSize().width();
284  }
285#endif
286  min_size.set_width(std::max(min_size.width(), min_titlebar_width));
287
288  // Ensure that the minimum width is enough to hold a minimum width tab strip
289  // and avatar icon at their usual insets.
290  if (browser_view()->IsTabStripVisible()) {
291    TabStrip* tabstrip = browser_view()->tabstrip();
292    const int min_tabstrip_width = tabstrip->GetMinimumSize().width();
293    const int min_tabstrip_area_width =
294        width() - GetBoundsForTabStripAndAvatarArea(tabstrip).width() +
295        min_tabstrip_width + browser_view()->GetOTRAvatarIcon().width() +
296        kAvatarLeftSpacing + kAvatarRightSpacing;
297    min_size.set_width(std::max(min_size.width(), min_tabstrip_area_width));
298  }
299
300  return min_size;
301}
302
303///////////////////////////////////////////////////////////////////////////////
304// OpaqueBrowserFrameView, views::NonClientFrameView implementation:
305
306gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
307  return client_view_bounds_;
308}
309
310gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
311    const gfx::Rect& client_bounds) const {
312  int top_height = NonClientTopBorderHeight(false);
313  int border_thickness = NonClientBorderThickness();
314  return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
315                   std::max(0, client_bounds.y() - top_height),
316                   client_bounds.width() + (2 * border_thickness),
317                   client_bounds.height() + top_height + border_thickness);
318}
319
320int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
321  if (!bounds().Contains(point))
322    return HTNOWHERE;
323
324  // See if the point is within the avatar menu button.
325  if (avatar_button() &&
326      avatar_button()->GetMirroredBounds().Contains(point))
327    return HTCLIENT;
328
329  int frame_component = frame()->client_view()->NonClientHitTest(point);
330
331  // See if we're in the sysmenu region.  We still have to check the tabstrip
332  // first so that clicks in a tab don't get treated as sysmenu clicks.
333  gfx::Rect sysmenu_rect(IconBounds());
334  // In maximized mode we extend the rect to the screen corner to take advantage
335  // of Fitts' Law.
336  if (frame()->IsMaximized())
337    sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
338  sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
339  if (sysmenu_rect.Contains(point))
340    return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
341
342  if (frame_component != HTNOWHERE)
343    return frame_component;
344
345  // Then see if the point is within any of the window controls.
346  if (close_button_ && close_button_->visible() &&
347      close_button_->GetMirroredBounds().Contains(point))
348    return HTCLOSE;
349  if (restore_button_ && restore_button_->visible() &&
350      restore_button_->GetMirroredBounds().Contains(point))
351    return HTMAXBUTTON;
352  if (maximize_button_ && maximize_button_->visible() &&
353      maximize_button_->GetMirroredBounds().Contains(point))
354    return HTMAXBUTTON;
355  if (minimize_button_ && minimize_button_->visible() &&
356      minimize_button_->GetMirroredBounds().Contains(point))
357    return HTMINBUTTON;
358
359  views::WidgetDelegate* delegate = frame()->widget_delegate();
360  if (!delegate) {
361    LOG(WARNING) << "delegate is NULL, returning safe default.";
362    return HTCAPTION;
363  }
364  int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
365      NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
366      delegate->CanResize());
367  // Fall back to the caption if no other component matches.
368  return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
369}
370
371void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
372                                           gfx::Path* window_mask) {
373  DCHECK(window_mask);
374
375  if (frame()->IsMaximized() || frame()->IsFullscreen())
376    return;
377
378  views::GetDefaultWindowMask(size, window_mask);
379}
380
381void OpaqueBrowserFrameView::ResetWindowControls() {
382  if (!ShouldAddDefaultCaptionButtons())
383    return;
384  restore_button_->SetState(views::CustomButton::STATE_NORMAL);
385  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
386  maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
387  // The close button isn't affected by this constraint.
388}
389
390void OpaqueBrowserFrameView::UpdateWindowIcon() {
391  window_icon_->SchedulePaint();
392}
393
394void OpaqueBrowserFrameView::UpdateWindowTitle() {
395  if (!frame()->IsFullscreen())
396    window_title_->SchedulePaint();
397}
398
399///////////////////////////////////////////////////////////////////////////////
400// OpaqueBrowserFrameView, views::View overrides:
401
402void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
403  if (frame()->IsFullscreen())
404    return;  // Nothing is visible, so don't bother to paint.
405
406  if (frame()->IsMaximized())
407    PaintMaximizedFrameBorder(canvas);
408  else
409    PaintRestoredFrameBorder(canvas);
410
411  // The window icon and title are painted by their respective views.
412  /* TODO(pkasting):  If this window is active, we should also draw a drop
413   * shadow on the title.  This is tricky, because we don't want to hardcode a
414   * shadow color (since we want to work with various themes), but we can't
415   * alpha-blend either (since the Windows text APIs don't really do this).
416   * So we'd need to sample the background color at the right location and
417   * synthesize a good shadow color. */
418
419  if (browser_view()->IsToolbarVisible())
420    PaintToolbarBackground(canvas);
421  if (!frame()->IsMaximized())
422    PaintRestoredClientEdge(canvas);
423}
424
425void OpaqueBrowserFrameView::Layout() {
426  LayoutWindowControls();
427  LayoutTitleBar();
428  LayoutAvatar();
429  client_view_bounds_ = CalculateClientAreaBounds(width(), height());
430}
431
432bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const {
433  // If |rect| does not intersect the bounds of the client area, claim it.
434  bool in_nonclient = NonClientFrameView::HitTestRect(rect);
435  if (in_nonclient)
436    return in_nonclient;
437
438  // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
439  if (!browser_view()->tabstrip())
440    return false;
441  gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
442  gfx::Point tabstrip_origin(tabstrip_bounds.origin());
443  View::ConvertPointToTarget(frame()->client_view(), this, &tabstrip_origin);
444  tabstrip_bounds.set_origin(tabstrip_origin);
445  if (rect.bottom() > tabstrip_bounds.bottom())
446    return false;
447
448  // We convert from our parent's coordinates since we assume we fill its bounds
449  // completely. We need to do this since we're not a parent of the tabstrip,
450  // meaning ConvertPointToTarget would otherwise return something bogus.
451  // TODO(tdanderson): Initialize |browser_view_point| using |rect| instead of
452  // its center point once GetEventHandlerForRect() is implemented.
453  gfx::Point browser_view_point(rect.CenterPoint());
454  View::ConvertPointToTarget(parent(), browser_view(), &browser_view_point);
455  return browser_view()->IsPositionInWindowCaption(browser_view_point);
456}
457
458void OpaqueBrowserFrameView::GetAccessibleState(
459    ui::AccessibleViewState* state) {
460  state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
461}
462
463///////////////////////////////////////////////////////////////////////////////
464// OpaqueBrowserFrameView, views::ButtonListener implementation:
465
466void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
467                                           const ui::Event& event) {
468  if (sender == minimize_button_)
469    frame()->Minimize();
470  else if (sender == maximize_button_)
471    frame()->Maximize();
472  else if (sender == restore_button_)
473    frame()->Restore();
474  else if (sender == close_button_)
475    frame()->Close();
476}
477
478///////////////////////////////////////////////////////////////////////////////
479// OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
480
481bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
482  // This function is queried during the creation of the window as the
483  // TabIconView we host is initialized, so we need to NULL check the selected
484  // WebContents because in this condition there is not yet a selected tab.
485  WebContents* current_tab = browser_view()->GetActiveWebContents();
486  return current_tab ? current_tab->IsLoading() : false;
487}
488
489gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() {
490  views::WidgetDelegate* delegate = frame()->widget_delegate();
491  if (!delegate) {
492    LOG(WARNING) << "delegate is NULL, returning safe default.";
493    return gfx::ImageSkia();
494  }
495  return delegate->GetWindowIcon();
496}
497
498///////////////////////////////////////////////////////////////////////////////
499// OpaqueBrowserFrameView, protected:
500
501void OpaqueBrowserFrameView::Observe(
502    int type,
503    const content::NotificationSource& source,
504    const content::NotificationDetails& details) {
505  switch (type) {
506    case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED:
507      UpdateAvatarInfo();
508      break;
509    default:
510      NOTREACHED() << "Got a notification we didn't register for!";
511      break;
512  }
513}
514
515///////////////////////////////////////////////////////////////////////////////
516// OpaqueBrowserFrameView, private:
517
518views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton(
519    int normal_image_id,
520    int hot_image_id,
521    int pushed_image_id,
522    int mask_image_id,
523    int accessibility_string_id) {
524  views::ImageButton* button = new views::ImageButton(this);
525  ui::ThemeProvider* tp = frame()->GetThemeProvider();
526  button->SetImage(views::CustomButton::STATE_NORMAL,
527                   tp->GetImageSkiaNamed(normal_image_id));
528  button->SetImage(views::CustomButton::STATE_HOVERED,
529                   tp->GetImageSkiaNamed(hot_image_id));
530  button->SetImage(views::CustomButton::STATE_PRESSED,
531                   tp->GetImageSkiaNamed(pushed_image_id));
532  if (browser_view()->IsBrowserTypeNormal()) {
533    button->SetBackground(
534        tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND),
535        tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND),
536        tp->GetImageSkiaNamed(mask_image_id));
537  }
538  button->SetAccessibleName(
539      l10n_util::GetStringUTF16(accessibility_string_id));
540  AddChildView(button);
541  return button;
542}
543
544int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
545  return (!restored && (frame()->IsMaximized() || frame()->IsFullscreen())) ?
546      0 : kFrameBorderThickness;
547}
548
549int OpaqueBrowserFrameView::TopResizeHeight() const {
550  return FrameBorderThickness(false) - kTopResizeAdjust;
551}
552
553int OpaqueBrowserFrameView::NonClientBorderThickness() const {
554  // When we fill the screen, we don't show a client edge.
555  return FrameBorderThickness(false) +
556      ((frame()->IsMaximized() || frame()->IsFullscreen()) ?
557       0 : kClientEdgeThickness);
558}
559
560int OpaqueBrowserFrameView::CaptionButtonY(bool restored) const {
561  // Maximized buttons start at window top so that even if their images aren't
562  // drawn flush with the screen edge, they still obey Fitts' Law.
563  return (!restored && frame()->IsMaximized()) ?
564      FrameBorderThickness(false) : kFrameShadowThickness;
565}
566
567int OpaqueBrowserFrameView::TitlebarBottomThickness(bool restored) const {
568  return kTitlebarTopAndBottomEdgeThickness +
569      ((!restored && frame()->IsMaximized()) ? 0 : kClientEdgeThickness);
570}
571
572int OpaqueBrowserFrameView::IconSize() const {
573#if defined(OS_WIN)
574  // This metric scales up if either the titlebar height or the titlebar font
575  // size are increased.
576  return GetSystemMetrics(SM_CYSMICON);
577#else
578  return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize);
579#endif
580}
581
582gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
583  int size = IconSize();
584  int frame_thickness = FrameBorderThickness(false);
585  int y;
586  views::WidgetDelegate* delegate = frame()->widget_delegate();
587  if (delegate && (delegate->ShouldShowWindowIcon() ||
588                   delegate->ShouldShowWindowTitle())) {
589    // Our frame border has a different "3D look" than Windows'.  Theirs has a
590    // more complex gradient on the top that they push their icon/title below;
591    // then the maximized window cuts this off and the icon/title are centered
592    // in the remaining space.  Because the apparent shape of our border is
593    // simpler, using the same positioning makes things look slightly uncentered
594    // with restored windows, so when the window is restored, instead of
595    // calculating the remaining space from below the frame border, we calculate
596    // from below the 3D edge.
597    int unavailable_px_at_top = frame()->IsMaximized() ?
598        frame_thickness : kTitlebarTopAndBottomEdgeThickness;
599    // When the icon is shorter than the minimum space we reserve for the
600    // caption button, we vertically center it.  We want to bias rounding to put
601    // extra space above the icon, since the 3D edge (+ client edge, for
602    // restored windows) below looks (to the eye) more like additional space
603    // than does the 3D edge (or nothing at all, for maximized windows) above;
604    // hence the +1.
605    y = unavailable_px_at_top + (NonClientTopBorderHeight(false) -
606        unavailable_px_at_top - size - TitlebarBottomThickness(false) + 1) / 2;
607  } else {
608    // For "browser mode" windows, we use the native positioning, which is just
609    // below the top frame border.
610    y = frame_thickness;
611  }
612  return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
613}
614
615gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStripAndAvatarArea(
616    views::View* tabstrip) const {
617  int available_width = width();
618  if (minimize_button_) {
619    available_width = minimize_button_->x();
620  } else if (browser_view()->window_switcher_button()) {
621    // We don't have the sysmenu buttons in Windows 8 metro mode. However there
622    // are buttons like the window switcher which are drawn in the non client
623    // are in the BrowserView. We need to ensure that the tab strip does not
624    // draw on the window switcher button.
625    available_width -= browser_view()->window_switcher_button()->width();
626  }
627  const int caption_spacing = frame()->IsMaximized() ?
628      kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing;
629  const int tabstrip_x = NonClientBorderThickness();
630  const int tabstrip_width = available_width - tabstrip_x - caption_spacing;
631  return gfx::Rect(tabstrip_x, GetTabStripInsets(false).top,
632                   std::max(0, tabstrip_width),
633                   tabstrip->GetPreferredSize().height());
634}
635
636void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
637  frame_background_->set_frame_color(GetFrameColor());
638  frame_background_->set_theme_image(GetFrameImage());
639  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
640  frame_background_->set_top_area_height(GetTopAreaHeight());
641
642  ui::ThemeProvider* tp = GetThemeProvider();
643  frame_background_->SetSideImages(
644      tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE),
645      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER),
646      tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE),
647      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER));
648  frame_background_->SetCornerImages(
649      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER),
650      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER),
651      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER),
652      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER));
653  frame_background_->PaintRestored(canvas, this);
654
655  // Note: When we don't have a toolbar, we need to draw some kind of bottom
656  // edge here.  Because the App Window graphics we use for this have an
657  // attached client edge and their sizing algorithm is a little involved, we do
658  // all this in PaintRestoredClientEdge().
659}
660
661void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
662  frame_background_->set_frame_color(GetFrameColor());
663  frame_background_->set_theme_image(GetFrameImage());
664  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
665  frame_background_->set_top_area_height(GetTopAreaHeight());
666
667  // Theme frame must be aligned with the tabstrip as if we were
668  // in restored mode.  Note that the top of the tabstrip is
669  // kTabstripTopShadowThickness px off the top of the screen.
670  int theme_background_y = -(GetTabStripInsets(true).top +
671      kTabstripTopShadowThickness);
672  frame_background_->set_theme_background_y(theme_background_y);
673
674  frame_background_->PaintMaximized(canvas, this);
675
676  // TODO(jamescook): Migrate this into FrameBackground.
677  if (!browser_view()->IsToolbarVisible()) {
678    // There's no toolbar to edge the frame border, so we need to draw a bottom
679    // edge.  The graphic we use for this has a built in client edge, so we clip
680    // it off the bottom.
681    gfx::ImageSkia* top_center =
682        GetThemeProvider()->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
683    int edge_height = top_center->height() - kClientEdgeThickness;
684    canvas->TileImageInt(*top_center, 0,
685        frame()->client_view()->y() - edge_height, width(), edge_height);
686  }
687}
688
689void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
690  gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
691  if (toolbar_bounds.IsEmpty())
692    return;
693  gfx::Point toolbar_origin(toolbar_bounds.origin());
694  ConvertPointToTarget(browser_view(), this, &toolbar_origin);
695  toolbar_bounds.set_origin(toolbar_origin);
696
697  int x = toolbar_bounds.x();
698  int w = toolbar_bounds.width();
699  int y = toolbar_bounds.y();
700  int h = toolbar_bounds.height();
701
702  // Gross hack: We split the toolbar images into two pieces, since sometimes
703  // (popup mode) the toolbar isn't tall enough to show the whole image.  The
704  // split happens between the top shadow section and the bottom gradient
705  // section so that we never break the gradient.
706  int split_point = kFrameShadowThickness * 2;
707  int bottom_y = y + split_point;
708  ui::ThemeProvider* tp = GetThemeProvider();
709  gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(
710      IDR_CONTENT_TOP_LEFT_CORNER);
711  int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
712
713  // Split our canvas out so we can mask out the corners of the toolbar
714  // without masking out the frame.
715  canvas->SaveLayerAlpha(
716      255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
717                     h));
718
719  // Paint the bottom rect.
720  canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
721                   tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
722
723  // Tile the toolbar image starting at the frame edge on the left and where the
724  // horizontal tabstrip is (or would be) on the top.
725  gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
726  canvas->TileImageInt(*theme_toolbar,
727                       x + GetThemeBackgroundXInset(),
728                       bottom_y - GetTabStripInsets(false).top,
729                       x, bottom_y, w, theme_toolbar->height());
730
731  // Draw rounded corners for the tab.
732  gfx::ImageSkia* toolbar_left_mask =
733      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
734  gfx::ImageSkia* toolbar_right_mask =
735      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
736
737  // We mask out the corners by using the DestinationIn transfer mode,
738  // which keeps the RGB pixels from the destination and the alpha from
739  // the source.
740  SkPaint paint;
741  paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
742
743  // Mask the left edge.
744  int left_x = x - kContentEdgeShadowThickness;
745  canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
746                       split_point, left_x, y, toolbar_left_mask->width(),
747                       split_point, false, paint);
748  canvas->DrawImageInt(*toolbar_left_mask, 0,
749      toolbar_left_mask->height() - bottom_edge_height,
750      toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
751      toolbar_left_mask->width(), bottom_edge_height, false, paint);
752
753  // Mask the right edge.
754  int right_x =
755      x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
756  canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
757                       split_point, right_x, y, toolbar_right_mask->width(),
758                       split_point, false, paint);
759  canvas->DrawImageInt(*toolbar_right_mask, 0,
760      toolbar_right_mask->height() - bottom_edge_height,
761      toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
762      toolbar_right_mask->width(), bottom_edge_height, false, paint);
763  canvas->Restore();
764
765  canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
766                       left_x, y, toolbar_left->width(), split_point, false);
767  canvas->DrawImageInt(*toolbar_left, 0,
768      toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
769      bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
770      bottom_edge_height, false);
771
772  gfx::ImageSkia* toolbar_center =
773      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
774  canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
775      y, right_x - (left_x + toolbar_left->width()),
776      split_point);
777
778  gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed(
779      IDR_CONTENT_TOP_RIGHT_CORNER);
780  canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(),
781      split_point, right_x, y, toolbar_right->width(), split_point, false);
782  canvas->DrawImageInt(*toolbar_right, 0,
783      toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
784      bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
785      bottom_edge_height, false);
786
787  // Draw the content/toolbar separator.
788  canvas->FillRect(
789      gfx::Rect(x + kClientEdgeThickness,
790                toolbar_bounds.bottom() - kClientEdgeThickness,
791                w - (2 * kClientEdgeThickness),
792                kClientEdgeThickness),
793      ThemeProperties::GetDefaultColor(
794          ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
795}
796
797void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
798  ui::ThemeProvider* tp = GetThemeProvider();
799  int client_area_top = frame()->client_view()->y();
800  int image_top = client_area_top;
801
802  gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height());
803  SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
804
805  if (browser_view()->IsToolbarVisible()) {
806    // The client edge images always start below the toolbar corner images.  The
807    // client edge filled rects start there or at the bottom of the toolbar,
808    // whichever is shorter.
809    gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
810    image_top += toolbar_bounds.y() +
811        tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height();
812    client_area_top = std::min(image_top,
813        client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
814  } else if (!browser_view()->IsTabStripVisible()) {
815    // The toolbar isn't going to draw a client edge for us, so draw one
816    // ourselves.
817    gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT);
818    gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
819    gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT);
820    int top_edge_y = client_area_top - top_center->height();
821    int height = client_area_top - top_edge_y;
822
823    canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height,
824        client_area_bounds.x() - top_left->width(), top_edge_y,
825        top_left->width(), height, false);
826    canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
827      client_area_bounds.width(), std::min(height, top_center->height()));
828    canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height,
829        client_area_bounds.right(), top_edge_y,
830        top_right->width(), height, false);
831
832    // Draw the toolbar color across the top edge.
833    canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
834        client_area_top - kClientEdgeThickness,
835        client_area_bounds.width() + (2 * kClientEdgeThickness),
836        kClientEdgeThickness), toolbar_color);
837  }
838
839  int client_area_bottom =
840      std::max(client_area_top, height() - NonClientBorderThickness());
841  int image_height = client_area_bottom - image_top;
842
843  // Draw the client edge images.
844  gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE);
845  canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
846                       right->width(), image_height);
847  canvas->DrawImageInt(
848      *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
849      client_area_bounds.right(), client_area_bottom);
850  gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER);
851  canvas->TileImageInt(*bottom, client_area_bounds.x(),
852      client_area_bottom, client_area_bounds.width(),
853      bottom->height());
854  gfx::ImageSkia* bottom_left =
855      tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
856  canvas->DrawImageInt(*bottom_left,
857      client_area_bounds.x() - bottom_left->width(), client_area_bottom);
858  gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE);
859  canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
860                       image_top, left->width(), image_height);
861
862  // Draw the toolbar color so that the client edges show the right color even
863  // where not covered by the toolbar image.  NOTE: We do this after drawing the
864  // images because the images are meant to alpha-blend atop the frame whereas
865  // these rects are meant to be fully opaque, without anything overlaid.
866  canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
867      client_area_top, kClientEdgeThickness,
868      client_area_bottom + kClientEdgeThickness - client_area_top),
869       toolbar_color);
870  canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom,
871                             client_area_bounds.width(), kClientEdgeThickness),
872                   toolbar_color);
873  canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top,
874      kClientEdgeThickness,
875      client_area_bottom + kClientEdgeThickness - client_area_top),
876      toolbar_color);
877}
878
879SkColor OpaqueBrowserFrameView::GetFrameColor() const {
880  bool is_incognito = browser_view()->IsOffTheRecord();
881  if (browser_view()->IsBrowserTypeNormal()) {
882    if (ShouldPaintAsActive()) {
883      return GetThemeProvider()->GetColor(is_incognito ?
884          ThemeProperties::COLOR_FRAME_INCOGNITO :
885          ThemeProperties::COLOR_FRAME);
886    }
887    return GetThemeProvider()->GetColor(is_incognito ?
888        ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
889        ThemeProperties::COLOR_FRAME_INACTIVE);
890  }
891  // Never theme app and popup windows.
892  if (ShouldPaintAsActive()) {
893    return ThemeProperties::GetDefaultColor(is_incognito ?
894        ThemeProperties::COLOR_FRAME_INCOGNITO : ThemeProperties::COLOR_FRAME);
895  }
896  return ThemeProperties::GetDefaultColor(is_incognito ?
897      ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
898      ThemeProperties::COLOR_FRAME_INACTIVE);
899}
900
901gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const {
902  bool is_incognito = browser_view()->IsOffTheRecord();
903  int resource_id;
904  if (browser_view()->IsBrowserTypeNormal()) {
905    if (ShouldPaintAsActive()) {
906      resource_id = is_incognito ?
907          IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
908    } else {
909      resource_id = is_incognito ?
910          IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
911    }
912    return GetThemeProvider()->GetImageSkiaNamed(resource_id);
913  }
914  // Never theme app and popup windows.
915  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
916  if (ShouldPaintAsActive()) {
917    resource_id = is_incognito ?
918        IDR_THEME_FRAME_INCOGNITO : IDR_FRAME;
919  } else {
920    resource_id = is_incognito ?
921        IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
922  }
923  return rb.GetImageSkiaNamed(resource_id);
924}
925
926gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const {
927  ui::ThemeProvider* tp = GetThemeProvider();
928  if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
929      browser_view()->IsBrowserTypeNormal() &&
930      !browser_view()->IsOffTheRecord()) {
931    return tp->GetImageSkiaNamed(ShouldPaintAsActive() ?
932        IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
933  }
934  return NULL;
935}
936
937int OpaqueBrowserFrameView::GetTopAreaHeight() const {
938  gfx::ImageSkia* frame_image = GetFrameImage();
939  int top_area_height = frame_image->height();
940  if (browser_view()->IsTabStripVisible()) {
941    top_area_height = std::max(top_area_height,
942      GetBoundsForTabStrip(browser_view()->tabstrip()).bottom());
943  }
944  return top_area_height;
945}
946
947void OpaqueBrowserFrameView::LayoutWindowControls() {
948  if (!ShouldAddDefaultCaptionButtons())
949    return;
950  bool is_maximized = frame()->IsMaximized();
951  close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
952                                   views::ImageButton::ALIGN_BOTTOM);
953  int caption_y = CaptionButtonY(false);
954  // There should always be the same number of non-shadow pixels visible to the
955  // side of the caption buttons.  In maximized mode we extend the rightmost
956  // button to the screen corner to obey Fitts' Law.
957  int right_extra_width = is_maximized ?
958      (kFrameBorderThickness - kFrameShadowThickness) : 0;
959  gfx::Size close_button_size = close_button_->GetPreferredSize();
960  close_button_->SetBounds(width() - FrameBorderThickness(false) -
961      right_extra_width - close_button_size.width(), caption_y,
962      close_button_size.width() + right_extra_width,
963      close_button_size.height());
964
965  // When the window is restored, we show a maximized button; otherwise, we show
966  // a restore button.
967  bool is_restored = !is_maximized && !frame()->IsMinimized();
968  views::ImageButton* invisible_button = is_restored ?
969      restore_button_ : maximize_button_;
970  invisible_button->SetVisible(false);
971
972  views::ImageButton* visible_button = is_restored ?
973      maximize_button_ : restore_button_;
974  visible_button->SetVisible(true);
975  visible_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
976                                    views::ImageButton::ALIGN_BOTTOM);
977  gfx::Size visible_button_size = visible_button->GetPreferredSize();
978  visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
979                            caption_y, visible_button_size.width(),
980                            visible_button_size.height());
981
982  minimize_button_->SetVisible(true);
983  minimize_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
984                                      views::ImageButton::ALIGN_BOTTOM);
985  gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
986  minimize_button_->SetBounds(
987      visible_button->x() - minimize_button_size.width(), caption_y,
988      minimize_button_size.width(),
989      minimize_button_size.height());
990}
991
992void OpaqueBrowserFrameView::LayoutTitleBar() {
993  const views::WidgetDelegate* delegate = frame()->widget_delegate();
994  if (delegate) {
995    gfx::Rect icon_bounds(IconBounds());
996    if (delegate->ShouldShowWindowIcon())
997      window_icon_->SetBoundsRect(icon_bounds);
998
999    window_title_->SetVisible(delegate->ShouldShowWindowTitle());
1000    if (delegate->ShouldShowWindowTitle()) {
1001      window_title_->SetText(delegate->GetWindowTitle());
1002      const int title_x = delegate->ShouldShowWindowIcon() ?
1003          icon_bounds.right() + kIconTitleSpacing : icon_bounds.x();
1004      window_title_->SetBounds(title_x, icon_bounds.y(),
1005          std::max(0, minimize_button_->x() - kTitleLogoSpacing - title_x),
1006          icon_bounds.height());
1007    }
1008  }
1009}
1010
1011void OpaqueBrowserFrameView::LayoutAvatar() {
1012  // Even though the avatar is used for both incognito and profiles we always
1013  // use the incognito icon to layout the avatar button. The profile icon
1014  // can be customized so we can't depend on its size to perform layout.
1015  gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
1016
1017  int avatar_bottom = GetTabStripInsets(false).top +
1018      browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
1019  int avatar_restored_y = avatar_bottom - incognito_icon.height();
1020  int avatar_y = frame()->IsMaximized() ?
1021      (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) :
1022      avatar_restored_y;
1023  avatar_bounds_.SetRect(NonClientBorderThickness() + kAvatarLeftSpacing,
1024      avatar_y, incognito_icon.width(),
1025      browser_view()->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0);
1026  if (avatar_button())
1027    avatar_button()->SetBoundsRect(avatar_bounds_);
1028
1029  if (avatar_label()) {
1030    gfx::Size size = avatar_label()->GetPreferredSize();
1031    int label_height = std::min(avatar_bounds_.height(), size.height());
1032    gfx::Rect label_bounds(
1033        avatar_bounds_.right() + views::kRelatedControlHorizontalSpacing,
1034        avatar_y + (avatar_bounds_.height() - label_height) / 2,
1035        size.width(),
1036        browser_view()->ShouldShowAvatar() ? size.height() : 0);
1037    avatar_label()->SetBoundsRect(label_bounds);
1038  }
1039}
1040
1041gfx::Rect OpaqueBrowserFrameView::CalculateClientAreaBounds(int width,
1042                                                            int height) const {
1043  int top_height = NonClientTopBorderHeight(false);
1044  int border_thickness = NonClientBorderThickness();
1045  return gfx::Rect(border_thickness, top_height,
1046                   std::max(0, width - (2 * border_thickness)),
1047                   std::max(0, height - GetReservedHeight() -
1048                       top_height - border_thickness));
1049}
1050