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