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