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