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