Home | History | Annotate | Download | only in frame
      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 }
    917