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