opaque_browser_frame_view.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/compiler_specific.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/profiles/profiles_state.h"
15#include "chrome/browser/signin/signin_header_helper.h"
16#include "chrome/browser/themes/theme_properties.h"
17#include "chrome/browser/ui/views/frame/browser_frame.h"
18#include "chrome/browser/ui/views/frame/browser_view.h"
19#include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
20#include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h"
21#include "chrome/browser/ui/views/profiles/avatar_label.h"
22#include "chrome/browser/ui/views/profiles/avatar_menu_button.h"
23#include "chrome/browser/ui/views/profiles/new_avatar_button.h"
24#include "chrome/browser/ui/views/tab_icon_view.h"
25#include "chrome/browser/ui/views/tabs/tab_strip.h"
26#include "chrome/browser/ui/views/theme_image_mapper.h"
27#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
28#include "chrome/common/pref_names.h"
29#include "components/signin/core/common/profile_management_switches.h"
30#include "content/public/browser/notification_service.h"
31#include "content/public/browser/web_contents.h"
32#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
34#include "grit/theme_resources.h"
35#include "grit/ui_resources.h"
36#include "ui/accessibility/ax_view_state.h"
37#include "ui/base/hit_test.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "ui/base/resource/resource_bundle.h"
40#include "ui/base/theme_provider.h"
41#include "ui/gfx/canvas.h"
42#include "ui/gfx/font_list.h"
43#include "ui/gfx/image/image.h"
44#include "ui/gfx/image/image_skia.h"
45#include "ui/gfx/path.h"
46#include "ui/gfx/rect_conversions.h"
47#include "ui/views/controls/button/image_button.h"
48#include "ui/views/controls/image_view.h"
49#include "ui/views/controls/label.h"
50#include "ui/views/layout/layout_constants.h"
51#include "ui/views/views_delegate.h"
52#include "ui/views/widget/root_view.h"
53#include "ui/views/window/frame_background.h"
54#include "ui/views/window/window_shape.h"
55
56#if defined(OS_LINUX)
57#include "ui/views/controls/menu/menu_runner.h"
58#endif
59
60using content::WebContents;
61
62namespace {
63
64// While resize areas on Windows are normally the same size as the window
65// borders, our top area is shrunk by 1 px to make it easier to move the window
66// around with our thinner top grabbable strip.  (Incidentally, our side and
67// bottom resize areas don't match the frame border thickness either -- they
68// span the whole nonclient area, so there's no "dead zone" for the mouse.)
69const int kTopResizeAdjust = 1;
70
71// In the window corners, the resize areas don't actually expand bigger, but the
72// 16 px at the end of each edge triggers diagonal resizing.
73const int kResizeAreaCornerSize = 16;
74
75// The content left/right images have a shadow built into them.
76const int kContentEdgeShadowThickness = 2;
77
78// The icon never shrinks below 16 px on a side.
79const int kIconMinimumSize = 16;
80
81#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
82// The number of pixels to move the frame background image upwards when using
83// the GTK+ theme and the titlebar is condensed.
84const int kGTKThemeCondensedFrameTopInset = 15;
85#endif
86
87}  // namespace
88
89///////////////////////////////////////////////////////////////////////////////
90// OpaqueBrowserFrameView, public:
91
92OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
93                                               BrowserView* browser_view)
94    : BrowserNonClientFrameView(frame, browser_view),
95      layout_(new OpaqueBrowserFrameViewLayout(this)),
96      minimize_button_(NULL),
97      maximize_button_(NULL),
98      restore_button_(NULL),
99      close_button_(NULL),
100      window_icon_(NULL),
101      window_title_(NULL),
102      frame_background_(new views::FrameBackground()) {
103  SetLayoutManager(layout_);
104
105  minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE,
106                                             IDR_MINIMIZE_H,
107                                             IDR_MINIMIZE_P,
108                                             IDR_MINIMIZE_BUTTON_MASK,
109                                             IDS_ACCNAME_MINIMIZE,
110                                             VIEW_ID_MINIMIZE_BUTTON);
111  maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE,
112                                             IDR_MAXIMIZE_H,
113                                             IDR_MAXIMIZE_P,
114                                             IDR_MAXIMIZE_BUTTON_MASK,
115                                             IDS_ACCNAME_MAXIMIZE,
116                                             VIEW_ID_MAXIMIZE_BUTTON);
117  restore_button_ = InitWindowCaptionButton(IDR_RESTORE,
118                                            IDR_RESTORE_H,
119                                            IDR_RESTORE_P,
120                                            IDR_RESTORE_BUTTON_MASK,
121                                            IDS_ACCNAME_RESTORE,
122                                            VIEW_ID_RESTORE_BUTTON);
123  close_button_ = InitWindowCaptionButton(IDR_CLOSE,
124                                          IDR_CLOSE_H,
125                                          IDR_CLOSE_P,
126                                          IDR_CLOSE_BUTTON_MASK,
127                                          IDS_ACCNAME_CLOSE,
128                                          VIEW_ID_CLOSE_BUTTON);
129
130  // Initializing the TabIconView is expensive, so only do it if we need to.
131  if (browser_view->ShouldShowWindowIcon()) {
132    window_icon_ = new TabIconView(this, this);
133    window_icon_->set_is_light(true);
134    window_icon_->set_id(VIEW_ID_WINDOW_ICON);
135    AddChildView(window_icon_);
136    window_icon_->Update();
137  }
138
139  window_title_ = new views::Label(
140      browser_view->GetWindowTitle(),
141      gfx::FontList(BrowserFrame::GetTitleFontList()));
142  window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
143  window_title_->SetEnabledColor(SK_ColorWHITE);
144  // TODO(msw): Use a transparent background color as a workaround to use the
145  // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts.
146  window_title_->SetBackgroundColor(0x00000000);
147  window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
148  window_title_->set_id(VIEW_ID_WINDOW_TITLE);
149  AddChildView(window_title_);
150
151  if (browser_view->IsRegularOrGuestSession() && switches::IsNewAvatarMenu())
152    UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON);
153  else
154    UpdateAvatarInfo();
155
156  if (!browser_view->IsOffTheRecord()) {
157    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
158                   content::NotificationService::AllSources());
159  }
160
161  platform_observer_.reset(OpaqueBrowserFrameViewPlatformSpecific::Create(
162      this, layout_, browser_view->browser()->profile()));
163}
164
165OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
166}
167
168///////////////////////////////////////////////////////////////////////////////
169// OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
170
171gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
172    views::View* tabstrip) const {
173  if (!tabstrip)
174    return gfx::Rect();
175
176  return layout_->GetBoundsForTabStrip(tabstrip->GetPreferredSize(), width());
177}
178
179int OpaqueBrowserFrameView::GetTopInset() const {
180  return browser_view()->IsTabStripVisible() ?
181      layout_->GetTabStripInsetsTop(false) :
182      layout_->NonClientTopBorderHeight(false);
183}
184
185int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
186  return 0;
187}
188
189void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
190  if (window_icon_)
191    window_icon_->Update();
192}
193
194gfx::Size OpaqueBrowserFrameView::GetMinimumSize() const {
195  return layout_->GetMinimumSize(width());
196}
197
198///////////////////////////////////////////////////////////////////////////////
199// OpaqueBrowserFrameView, views::NonClientFrameView implementation:
200
201gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
202  return layout_->client_view_bounds();
203}
204
205gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
206    const gfx::Rect& client_bounds) const {
207  return layout_->GetWindowBoundsForClientBounds(client_bounds);
208}
209
210int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
211  if (!bounds().Contains(point))
212    return HTNOWHERE;
213
214  // See if the point is within the avatar menu button or within the avatar
215  // label.
216  if ((avatar_button() &&
217       avatar_button()->GetMirroredBounds().Contains(point)) ||
218      (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point)) ||
219      (new_avatar_button() &&
220       new_avatar_button()->GetMirroredBounds().Contains(point)))
221    return HTCLIENT;
222
223  int frame_component = frame()->client_view()->NonClientHitTest(point);
224
225  // See if we're in the sysmenu region.  We still have to check the tabstrip
226  // first so that clicks in a tab don't get treated as sysmenu clicks.
227  gfx::Rect sysmenu_rect(IconBounds());
228  // In maximized mode we extend the rect to the screen corner to take advantage
229  // of Fitts' Law.
230  if (layout_->IsTitleBarCondensed())
231    sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
232  sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
233  if (sysmenu_rect.Contains(point))
234    return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
235
236  if (frame_component != HTNOWHERE)
237    return frame_component;
238
239  // Then see if the point is within any of the window controls.
240  if (close_button_ && close_button_->visible() &&
241      close_button_->GetMirroredBounds().Contains(point))
242    return HTCLOSE;
243  if (restore_button_ && restore_button_->visible() &&
244      restore_button_->GetMirroredBounds().Contains(point))
245    return HTMAXBUTTON;
246  if (maximize_button_ && maximize_button_->visible() &&
247      maximize_button_->GetMirroredBounds().Contains(point))
248    return HTMAXBUTTON;
249  if (minimize_button_ && minimize_button_->visible() &&
250      minimize_button_->GetMirroredBounds().Contains(point))
251    return HTMINBUTTON;
252
253  views::WidgetDelegate* delegate = frame()->widget_delegate();
254  if (!delegate) {
255    LOG(WARNING) << "delegate is NULL, returning safe default.";
256    return HTCAPTION;
257  }
258  int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
259      NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
260      delegate->CanResize());
261  // Fall back to the caption if no other component matches.
262  return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
263}
264
265void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
266                                           gfx::Path* window_mask) {
267  DCHECK(window_mask);
268
269  if (layout_->IsTitleBarCondensed() || frame()->IsFullscreen())
270    return;
271
272  views::GetDefaultWindowMask(size, window_mask);
273}
274
275void OpaqueBrowserFrameView::ResetWindowControls() {
276  restore_button_->SetState(views::CustomButton::STATE_NORMAL);
277  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
278  maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
279  // The close button isn't affected by this constraint.
280}
281
282void OpaqueBrowserFrameView::UpdateWindowIcon() {
283  window_icon_->SchedulePaint();
284}
285
286void OpaqueBrowserFrameView::UpdateWindowTitle() {
287  if (!frame()->IsFullscreen())
288    window_title_->SchedulePaint();
289}
290
291///////////////////////////////////////////////////////////////////////////////
292// OpaqueBrowserFrameView, views::View overrides:
293
294bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const {
295  if (!views::View::HitTestRect(rect)) {
296    // |rect| is outside OpaqueBrowserFrameView's bounds.
297    return false;
298  }
299
300  // If the rect is outside the bounds of the client area, claim it.
301  gfx::RectF rect_in_client_view_coords_f(rect);
302  View::ConvertRectToTarget(this, frame()->client_view(),
303      &rect_in_client_view_coords_f);
304  gfx::Rect rect_in_client_view_coords = gfx::ToEnclosingRect(
305      rect_in_client_view_coords_f);
306  if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords))
307    return true;
308
309  // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in
310  // a non-tab portion.
311  TabStrip* tabstrip = browser_view()->tabstrip();
312  if (!tabstrip || !browser_view()->IsTabStripVisible())
313    return false;
314
315  gfx::RectF rect_in_tabstrip_coords_f(rect);
316  View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f);
317  gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect(
318      rect_in_tabstrip_coords_f);
319  if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) {
320    // |rect| is below the tabstrip.
321    return false;
322  }
323
324  if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) {
325    // Claim |rect| if it is in a non-tab portion of the tabstrip.
326    return tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords);
327  }
328
329  // We claim |rect| because it is above the bottom of the tabstrip, but
330  // not in the tabstrip itself. In particular, the avatar label/button is left
331  // of the tabstrip and the window controls are right of the tabstrip.
332  return true;
333}
334
335void OpaqueBrowserFrameView::GetAccessibleState(
336    ui::AXViewState* state) {
337  state->role = ui::AX_ROLE_TITLE_BAR;
338}
339
340///////////////////////////////////////////////////////////////////////////////
341// OpaqueBrowserFrameView, views::ButtonListener implementation:
342
343void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
344                                           const ui::Event& event) {
345  if (sender == minimize_button_) {
346    frame()->Minimize();
347  } else if (sender == maximize_button_) {
348    frame()->Maximize();
349  } else if (sender == restore_button_) {
350    frame()->Restore();
351  } else if (sender == close_button_) {
352    frame()->Close();
353  } else if (sender == new_avatar_button()) {
354    browser_view()->ShowAvatarBubbleFromAvatarButton(
355        BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT,
356        signin::GAIA_SERVICE_TYPE_NONE);
357  }
358}
359
360void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View* source,
361                                                 const gfx::Point& point) {
362#if defined(OS_LINUX)
363  views::MenuRunner menu_runner(frame()->GetSystemMenuModel());
364  ignore_result(menu_runner.RunMenuAt(browser_view()->GetWidget(),
365                                      window_icon_,
366                                      window_icon_->GetBoundsInScreen(),
367                                      views::MENU_ANCHOR_TOPLEFT,
368                                      ui::MENU_SOURCE_MOUSE,
369                                      views::MenuRunner::HAS_MNEMONICS));
370#endif
371}
372
373///////////////////////////////////////////////////////////////////////////////
374// OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
375
376bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
377  // This function is queried during the creation of the window as the
378  // TabIconView we host is initialized, so we need to NULL check the selected
379  // WebContents because in this condition there is not yet a selected tab.
380  WebContents* current_tab = browser_view()->GetActiveWebContents();
381  return current_tab ? current_tab->IsLoading() : false;
382}
383
384gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() {
385  views::WidgetDelegate* delegate = frame()->widget_delegate();
386  if (!delegate) {
387    LOG(WARNING) << "delegate is NULL, returning safe default.";
388    return gfx::ImageSkia();
389  }
390  return delegate->GetWindowIcon();
391}
392
393///////////////////////////////////////////////////////////////////////////////
394// OpaqueBrowserFrameView, protected:
395
396void OpaqueBrowserFrameView::Observe(
397    int type,
398    const content::NotificationSource& source,
399    const content::NotificationDetails& details) {
400  switch (type) {
401    case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED:
402      if (browser_view() ->IsRegularOrGuestSession() &&
403          switches::IsNewAvatarMenu()) {
404        UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON);
405      } else {
406        UpdateAvatarInfo();
407      }
408      break;
409    default:
410      NOTREACHED() << "Got a notification we didn't register for!";
411      break;
412  }
413}
414
415///////////////////////////////////////////////////////////////////////////////
416// OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
417
418bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
419  views::WidgetDelegate* delegate = frame()->widget_delegate();
420  return ShouldShowWindowTitleBar() && delegate &&
421         delegate->ShouldShowWindowIcon();
422}
423
424bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const {
425  // |delegate| may be NULL if called from callback of InputMethodChanged while
426  // a window is being destroyed.
427  // See more discussion at http://crosbug.com/8958
428  views::WidgetDelegate* delegate = frame()->widget_delegate();
429  return ShouldShowWindowTitleBar() && delegate &&
430         delegate->ShouldShowWindowTitle();
431}
432
433base::string16 OpaqueBrowserFrameView::GetWindowTitle() const {
434  return frame()->widget_delegate()->GetWindowTitle();
435}
436
437int OpaqueBrowserFrameView::GetIconSize() const {
438#if defined(OS_WIN)
439  // This metric scales up if either the titlebar height or the titlebar font
440  // size are increased.
441  return GetSystemMetrics(SM_CYSMICON);
442#else
443  return std::max(BrowserFrame::GetTitleFontList().GetHeight(),
444                  kIconMinimumSize);
445#endif
446}
447
448bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const {
449  return frame()->ShouldLeaveOffsetNearTopBorder();
450}
451
452gfx::Size OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const {
453  return browser_view()->GetMinimumSize();
454}
455
456bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const {
457  return ShouldShowWindowTitleBar();
458}
459
460bool OpaqueBrowserFrameView::ShouldShowAvatar() const {
461  return browser_view()->ShouldShowAvatar();
462}
463
464bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const {
465  return browser_view()->IsRegularOrGuestSession();
466}
467
468gfx::ImageSkia OpaqueBrowserFrameView::GetOTRAvatarIcon() const {
469  return browser_view()->GetOTRAvatarIcon();
470}
471
472bool OpaqueBrowserFrameView::IsMaximized() const {
473  return frame()->IsMaximized();
474}
475
476bool OpaqueBrowserFrameView::IsMinimized() const {
477  return frame()->IsMinimized();
478}
479
480bool OpaqueBrowserFrameView::IsFullscreen() const {
481  return frame()->IsFullscreen();
482}
483
484bool OpaqueBrowserFrameView::IsTabStripVisible() const {
485  return browser_view()->IsTabStripVisible();
486}
487
488int OpaqueBrowserFrameView::GetTabStripHeight() const {
489  return browser_view()->GetTabStripHeight();
490}
491
492gfx::Size OpaqueBrowserFrameView::GetTabstripPreferredSize() const {
493  gfx::Size s = browser_view()->tabstrip()->GetPreferredSize();
494  return s;
495}
496
497///////////////////////////////////////////////////////////////////////////////
498// OpaqueBrowserFrameView, views::View overrides:
499
500void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
501  if (frame()->IsFullscreen())
502    return;  // Nothing is visible, so don't bother to paint.
503
504  if (layout_->IsTitleBarCondensed())
505    PaintMaximizedFrameBorder(canvas);
506  else
507    PaintRestoredFrameBorder(canvas);
508
509  // The window icon and title are painted by their respective views.
510  /* TODO(pkasting):  If this window is active, we should also draw a drop
511   * shadow on the title.  This is tricky, because we don't want to hardcode a
512   * shadow color (since we want to work with various themes), but we can't
513   * alpha-blend either (since the Windows text APIs don't really do this).
514   * So we'd need to sample the background color at the right location and
515   * synthesize a good shadow color. */
516
517  if (browser_view()->IsToolbarVisible())
518    PaintToolbarBackground(canvas);
519  if (!layout_->IsTitleBarCondensed())
520    PaintRestoredClientEdge(canvas);
521}
522
523///////////////////////////////////////////////////////////////////////////////
524// OpaqueBrowserFrameView, private:
525
526views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton(
527    int normal_image_id,
528    int hot_image_id,
529    int pushed_image_id,
530    int mask_image_id,
531    int accessibility_string_id,
532    ViewID view_id) {
533  views::ImageButton* button = new views::ImageButton(this);
534  ui::ThemeProvider* tp = frame()->GetThemeProvider();
535  button->SetImage(views::CustomButton::STATE_NORMAL,
536                   tp->GetImageSkiaNamed(normal_image_id));
537  button->SetImage(views::CustomButton::STATE_HOVERED,
538                   tp->GetImageSkiaNamed(hot_image_id));
539  button->SetImage(views::CustomButton::STATE_PRESSED,
540                   tp->GetImageSkiaNamed(pushed_image_id));
541  if (browser_view()->IsBrowserTypeNormal()) {
542    button->SetBackground(
543        tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND),
544        tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND),
545        tp->GetImageSkiaNamed(mask_image_id));
546  }
547  button->SetAccessibleName(
548      l10n_util::GetStringUTF16(accessibility_string_id));
549  button->set_id(view_id);
550  AddChildView(button);
551  return button;
552}
553
554int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
555  return layout_->FrameBorderThickness(restored);
556}
557
558int OpaqueBrowserFrameView::TopResizeHeight() const {
559  return FrameBorderThickness(false) - kTopResizeAdjust;
560}
561
562int OpaqueBrowserFrameView::NonClientBorderThickness() const {
563  return layout_->NonClientBorderThickness();
564}
565
566gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
567  return layout_->IconBounds();
568}
569
570bool OpaqueBrowserFrameView::ShouldShowWindowTitleBar() const {
571#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
572  // Do not show the custom title bar if the system title bar option is enabled.
573  if (!frame()->UseCustomFrame())
574    return false;
575#endif
576
577  // Do not show caption buttons if the window manager is forcefully providing a
578  // title bar (e.g., in Ubuntu Unity, if the window is maximized).
579  if (!views::ViewsDelegate::views_delegate)
580    return true;
581  return !views::ViewsDelegate::views_delegate->WindowManagerProvidesTitleBar(
582              IsMaximized());
583}
584
585void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
586  frame_background_->set_frame_color(GetFrameColor());
587  frame_background_->set_theme_image(GetFrameImage());
588  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
589  frame_background_->set_top_area_height(GetTopAreaHeight());
590
591  ui::ThemeProvider* tp = GetThemeProvider();
592  frame_background_->SetSideImages(
593      tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE),
594      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER),
595      tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE),
596      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER));
597  frame_background_->SetCornerImages(
598      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER),
599      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER),
600      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER),
601      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER));
602  frame_background_->PaintRestored(canvas, this);
603
604  // Note: When we don't have a toolbar, we need to draw some kind of bottom
605  // edge here.  Because the App Window graphics we use for this have an
606  // attached client edge and their sizing algorithm is a little involved, we do
607  // all this in PaintRestoredClientEdge().
608}
609
610void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
611  ui::ThemeProvider* tp = GetThemeProvider();
612  frame_background_->set_frame_color(GetFrameColor());
613  frame_background_->set_theme_image(GetFrameImage());
614  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
615  frame_background_->set_top_area_height(GetTopAreaHeight());
616#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
617  // The window manager typically shows a gradient in the native title bar (when
618  // the system title bar pref is set, or when maximized on Ubuntu). Hide the
619  // gradient in the tab strip (by shifting it up vertically) to avoid a
620  // double-gradient effect.
621  if (tp->UsingSystemTheme())
622    frame_background_->set_maximized_top_inset(kGTKThemeCondensedFrameTopInset);
623#endif
624
625  frame_background_->PaintMaximized(canvas, this);
626
627  // TODO(jamescook): Migrate this into FrameBackground.
628  if (!browser_view()->IsToolbarVisible()) {
629    // There's no toolbar to edge the frame border, so we need to draw a bottom
630    // edge.  The graphic we use for this has a built in client edge, so we clip
631    // it off the bottom.
632    gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
633    int edge_height = top_center->height() - kClientEdgeThickness;
634    canvas->TileImageInt(*top_center, 0,
635        frame()->client_view()->y() - edge_height, width(), edge_height);
636  }
637}
638
639void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
640  gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
641  if (toolbar_bounds.IsEmpty())
642    return;
643  gfx::Point toolbar_origin(toolbar_bounds.origin());
644  ConvertPointToTarget(browser_view(), this, &toolbar_origin);
645  toolbar_bounds.set_origin(toolbar_origin);
646
647  int x = toolbar_bounds.x();
648  int w = toolbar_bounds.width();
649  int y = toolbar_bounds.y();
650  int h = toolbar_bounds.height();
651
652  // Gross hack: We split the toolbar images into two pieces, since sometimes
653  // (popup mode) the toolbar isn't tall enough to show the whole image.  The
654  // split happens between the top shadow section and the bottom gradient
655  // section so that we never break the gradient.
656  int split_point = kFrameShadowThickness * 2;
657  int bottom_y = y + split_point;
658  ui::ThemeProvider* tp = GetThemeProvider();
659  gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(
660      IDR_CONTENT_TOP_LEFT_CORNER);
661  int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
662
663  // Split our canvas out so we can mask out the corners of the toolbar
664  // without masking out the frame.
665  canvas->SaveLayerAlpha(
666      255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
667                     h));
668
669  // Paint the bottom rect.
670  canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
671                   tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
672
673  // Tile the toolbar image starting at the frame edge on the left and where the
674  // horizontal tabstrip is (or would be) on the top.
675  gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
676  canvas->TileImageInt(*theme_toolbar,
677                       x + GetThemeBackgroundXInset(),
678                       bottom_y - GetTopInset(),
679                       x, bottom_y, w, theme_toolbar->height());
680
681  // Draw rounded corners for the tab.
682  gfx::ImageSkia* toolbar_left_mask =
683      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
684  gfx::ImageSkia* toolbar_right_mask =
685      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
686
687  // We mask out the corners by using the DestinationIn transfer mode,
688  // which keeps the RGB pixels from the destination and the alpha from
689  // the source.
690  SkPaint paint;
691  paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
692
693  // Mask the left edge.
694  int left_x = x - kContentEdgeShadowThickness;
695  canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
696                       split_point, left_x, y, toolbar_left_mask->width(),
697                       split_point, false, paint);
698  canvas->DrawImageInt(*toolbar_left_mask, 0,
699      toolbar_left_mask->height() - bottom_edge_height,
700      toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
701      toolbar_left_mask->width(), bottom_edge_height, false, paint);
702
703  // Mask the right edge.
704  int right_x =
705      x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
706  canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
707                       split_point, right_x, y, toolbar_right_mask->width(),
708                       split_point, false, paint);
709  canvas->DrawImageInt(*toolbar_right_mask, 0,
710      toolbar_right_mask->height() - bottom_edge_height,
711      toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
712      toolbar_right_mask->width(), bottom_edge_height, false, paint);
713  canvas->Restore();
714
715  canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
716                       left_x, y, toolbar_left->width(), split_point, false);
717  canvas->DrawImageInt(*toolbar_left, 0,
718      toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
719      bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
720      bottom_edge_height, false);
721
722  gfx::ImageSkia* toolbar_center =
723      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
724  canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
725      y, right_x - (left_x + toolbar_left->width()),
726      split_point);
727
728  gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed(
729      IDR_CONTENT_TOP_RIGHT_CORNER);
730  canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(),
731      split_point, right_x, y, toolbar_right->width(), split_point, false);
732  canvas->DrawImageInt(*toolbar_right, 0,
733      toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
734      bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
735      bottom_edge_height, false);
736
737  // Draw the content/toolbar separator.
738  canvas->FillRect(
739      gfx::Rect(x + kClientEdgeThickness,
740                toolbar_bounds.bottom() - kClientEdgeThickness,
741                w - (2 * kClientEdgeThickness),
742                kClientEdgeThickness),
743      ThemeProperties::GetDefaultColor(
744          ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
745}
746
747void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
748  ui::ThemeProvider* tp = GetThemeProvider();
749  int client_area_top = frame()->client_view()->y();
750  int image_top = client_area_top;
751
752  gfx::Rect client_area_bounds =
753      layout_->CalculateClientAreaBounds(width(), height());
754  SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
755
756  if (browser_view()->IsToolbarVisible()) {
757    // The client edge images always start below the toolbar corner images.  The
758    // client edge filled rects start there or at the bottom of the toolbar,
759    // whichever is shorter.
760    gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
761
762    gfx::ImageSkia* content_top_left_corner =
763        tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER);
764    // TODO(oshima): Sanity checks for crbug.com/374273. Remove when it's fixed.
765    CHECK(content_top_left_corner);
766    CHECK(!content_top_left_corner->isNull());
767
768    image_top += toolbar_bounds.y() + content_top_left_corner->height();
769    client_area_top = std::min(image_top,
770        client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
771  } else if (!browser_view()->IsTabStripVisible()) {
772    // The toolbar isn't going to draw a client edge for us, so draw one
773    // ourselves.
774    gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT);
775    gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
776    gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT);
777    int top_edge_y = client_area_top - top_center->height();
778    int height = client_area_top - top_edge_y;
779
780    canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height,
781        client_area_bounds.x() - top_left->width(), top_edge_y,
782        top_left->width(), height, false);
783    canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
784      client_area_bounds.width(), std::min(height, top_center->height()));
785    canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height,
786        client_area_bounds.right(), top_edge_y,
787        top_right->width(), height, false);
788
789    // Draw the toolbar color across the top edge.
790    canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
791        client_area_top - kClientEdgeThickness,
792        client_area_bounds.width() + (2 * kClientEdgeThickness),
793        kClientEdgeThickness), toolbar_color);
794  }
795
796  int client_area_bottom =
797      std::max(client_area_top, height() - NonClientBorderThickness());
798  int image_height = client_area_bottom - image_top;
799
800  // Draw the client edge images.
801  gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE);
802  canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
803                       right->width(), image_height);
804  canvas->DrawImageInt(
805      *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
806      client_area_bounds.right(), client_area_bottom);
807  gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER);
808  canvas->TileImageInt(*bottom, client_area_bounds.x(),
809      client_area_bottom, client_area_bounds.width(),
810      bottom->height());
811  gfx::ImageSkia* bottom_left =
812      tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
813  canvas->DrawImageInt(*bottom_left,
814      client_area_bounds.x() - bottom_left->width(), client_area_bottom);
815  gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE);
816  canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
817                       image_top, left->width(), image_height);
818
819  // Draw the toolbar color so that the client edges show the right color even
820  // where not covered by the toolbar image.  NOTE: We do this after drawing the
821  // images because the images are meant to alpha-blend atop the frame whereas
822  // these rects are meant to be fully opaque, without anything overlaid.
823  canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
824      client_area_top, kClientEdgeThickness,
825      client_area_bottom + kClientEdgeThickness - client_area_top),
826       toolbar_color);
827  canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom,
828                             client_area_bounds.width(), kClientEdgeThickness),
829                   toolbar_color);
830  canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top,
831      kClientEdgeThickness,
832      client_area_bottom + kClientEdgeThickness - client_area_top),
833      toolbar_color);
834}
835
836SkColor OpaqueBrowserFrameView::GetFrameColor() const {
837  bool is_incognito = browser_view()->IsOffTheRecord();
838  ThemeProperties::OverwritableByUserThemeProperty color_id;
839  if (ShouldPaintAsActive()) {
840    color_id = is_incognito ?
841               ThemeProperties::COLOR_FRAME_INCOGNITO :
842               ThemeProperties::COLOR_FRAME;
843  } else {
844    color_id = is_incognito ?
845               ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
846               ThemeProperties::COLOR_FRAME_INACTIVE;
847  }
848
849  if (browser_view()->IsBrowserTypeNormal() ||
850      platform_observer_->IsUsingSystemTheme()) {
851    return GetThemeProvider()->GetColor(color_id);
852  }
853
854  // Never theme app and popup windows unless the |platform_observer_|
855  // requested an override.
856  return ThemeProperties::GetDefaultColor(color_id);
857}
858
859gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const {
860  bool is_incognito = browser_view()->IsOffTheRecord();
861  int resource_id;
862  if (browser_view()->IsBrowserTypeNormal()) {
863    if (ShouldPaintAsActive()) {
864      resource_id = is_incognito ?
865          IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
866    } else {
867      resource_id = is_incognito ?
868          IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
869    }
870    return GetThemeProvider()->GetImageSkiaNamed(resource_id);
871  }
872  if (ShouldPaintAsActive()) {
873    resource_id = is_incognito ?
874        IDR_THEME_FRAME_INCOGNITO : IDR_FRAME;
875  } else {
876    resource_id = is_incognito ?
877        IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
878  }
879
880  if (platform_observer_->IsUsingSystemTheme()) {
881    // We want to use theme images provided by the system theme when enabled,
882    // even if we are an app or popup window.
883    return GetThemeProvider()->GetImageSkiaNamed(resource_id);
884  }
885
886  // Otherwise, never theme app and popup windows.
887  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
888  return rb.GetImageSkiaNamed(chrome::MapThemeImage(
889      chrome::GetHostDesktopTypeForNativeWindow(
890          browser_view()->GetNativeWindow()),
891      resource_id));
892}
893
894gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const {
895  ui::ThemeProvider* tp = GetThemeProvider();
896  if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
897      browser_view()->IsBrowserTypeNormal() &&
898      !browser_view()->IsOffTheRecord()) {
899    return tp->GetImageSkiaNamed(ShouldPaintAsActive() ?
900        IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
901  }
902  return NULL;
903}
904
905int OpaqueBrowserFrameView::GetTopAreaHeight() const {
906  gfx::ImageSkia* frame_image = GetFrameImage();
907  int top_area_height = frame_image->height();
908  if (browser_view()->IsTabStripVisible()) {
909    top_area_height = std::max(top_area_height,
910      GetBoundsForTabStrip(browser_view()->tabstrip()).bottom());
911  }
912  return top_area_height;
913}
914