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