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/browser_desktop_window_tree_host_win.h"
      6 
      7 #include <dwmapi.h>
      8 
      9 #include "chrome/browser/lifetime/application_lifetime.h"
     10 #include "chrome/browser/themes/theme_service.h"
     11 #include "chrome/browser/themes/theme_service_factory.h"
     12 #include "chrome/browser/ui/views/frame/browser_frame.h"
     13 #include "chrome/browser/ui/views/frame/browser_frame_common_win.h"
     14 #include "chrome/browser/ui/views/frame/browser_view.h"
     15 #include "chrome/browser/ui/views/frame/browser_window_property_manager_win.h"
     16 #include "chrome/browser/ui/views/frame/system_menu_insertion_delegate_win.h"
     17 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     18 #include "chrome/browser/ui/views/theme_image_mapper.h"
     19 #include "ui/base/theme_provider.h"
     20 #include "ui/gfx/win/dpi.h"
     21 #include "ui/views/controls/menu/native_menu_win.h"
     22 
     23 #pragma comment(lib, "dwmapi.lib")
     24 
     25 namespace {
     26 
     27 const int kClientEdgeThickness = 3;
     28 // We need to offset the DWMFrame into the toolbar so that the blackness
     29 // doesn't show up on our rounded corners.
     30 const int kDWMFrameTopOffset = 3;
     31 
     32 // DesktopThemeProvider maps resource ids using MapThemeImage(). This is
     33 // necessary for BrowserDesktopWindowTreeHostWin so that it uses the windows
     34 // theme images rather than the ash theme images.
     35 class DesktopThemeProvider : public ui::ThemeProvider {
     36  public:
     37   explicit DesktopThemeProvider(ui::ThemeProvider* delegate)
     38       : delegate_(delegate) {
     39   }
     40 
     41   virtual bool UsingSystemTheme() const OVERRIDE {
     42     return delegate_->UsingSystemTheme();
     43   }
     44   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
     45     return delegate_->GetImageSkiaNamed(
     46         chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE, id));
     47   }
     48   virtual SkColor GetColor(int id) const OVERRIDE {
     49     return delegate_->GetColor(id);
     50   }
     51   virtual int GetDisplayProperty(int id) const OVERRIDE {
     52     return delegate_->GetDisplayProperty(id);
     53   }
     54   virtual bool ShouldUseNativeFrame() const OVERRIDE {
     55     return delegate_->ShouldUseNativeFrame();
     56   }
     57   virtual bool HasCustomImage(int id) const OVERRIDE {
     58     return delegate_->HasCustomImage(
     59         chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE, id));
     60   }
     61   virtual base::RefCountedMemory* GetRawData(
     62       int id,
     63       ui::ScaleFactor scale_factor) const OVERRIDE {
     64     return delegate_->GetRawData(id, scale_factor);
     65   }
     66 
     67  private:
     68   ui::ThemeProvider* delegate_;
     69 
     70   DISALLOW_COPY_AND_ASSIGN(DesktopThemeProvider);
     71 };
     72 
     73 }  // namespace
     74 
     75 ////////////////////////////////////////////////////////////////////////////////
     76 // BrowserDesktopWindowTreeHostWin, public:
     77 
     78 BrowserDesktopWindowTreeHostWin::BrowserDesktopWindowTreeHostWin(
     79     views::internal::NativeWidgetDelegate* native_widget_delegate,
     80     views::DesktopNativeWidgetAura* desktop_native_widget_aura,
     81     BrowserView* browser_view,
     82     BrowserFrame* browser_frame)
     83     : DesktopWindowTreeHostWin(native_widget_delegate,
     84                                desktop_native_widget_aura),
     85       browser_view_(browser_view),
     86       browser_frame_(browser_frame),
     87       did_gdi_clear_(false) {
     88   scoped_ptr<ui::ThemeProvider> theme_provider(
     89       new DesktopThemeProvider(ThemeServiceFactory::GetForProfile(
     90                                    browser_view->browser()->profile())));
     91   browser_frame->SetThemeProvider(theme_provider.Pass());
     92 }
     93 
     94 BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {
     95 }
     96 
     97 views::NativeMenuWin* BrowserDesktopWindowTreeHostWin::GetSystemMenu() {
     98   if (!system_menu_.get()) {
     99     SystemMenuInsertionDelegateWin insertion_delegate;
    100     system_menu_.reset(
    101         new views::NativeMenuWin(browser_frame_->GetSystemMenuModel(),
    102                                  GetHWND()));
    103     system_menu_->Rebuild(&insertion_delegate);
    104   }
    105   return system_menu_.get();
    106 }
    107 
    108 ////////////////////////////////////////////////////////////////////////////////
    109 // BrowserDesktopWindowTreeHostWin, BrowserDesktopWindowTreeHost implementation:
    110 
    111 views::DesktopWindowTreeHost*
    112     BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() {
    113   return this;
    114 }
    115 
    116 int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const {
    117   return minimize_button_metrics_.GetMinimizeButtonOffsetX();
    118 }
    119 
    120 bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const {
    121   return true;
    122 }
    123 
    124 ////////////////////////////////////////////////////////////////////////////////
    125 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
    126 
    127 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
    128   STARTUPINFO si = {0};
    129   si.cb = sizeof(si);
    130   si.dwFlags = STARTF_USESHOWWINDOW;
    131   GetStartupInfo(&si);
    132   return si.wShowWindow;
    133 }
    134 
    135 bool BrowserDesktopWindowTreeHostWin::GetClientAreaInsets(
    136     gfx::Insets* insets) const {
    137   // Use the default client insets for an opaque frame or a glass popup/app
    138   // frame.
    139   if (!GetWidget()->ShouldUseNativeFrame() ||
    140       !browser_view_->IsBrowserTypeNormal()) {
    141     return false;
    142   }
    143 
    144   int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
    145   // In fullscreen mode, we have no frame. In restored mode, we draw our own
    146   // client edge over part of the default frame.
    147   if (GetWidget()->IsFullscreen())
    148     border_thickness = 0;
    149   else if (!IsMaximized())
    150     border_thickness -= kClientEdgeThickness;
    151   insets->Set(0, border_thickness, border_thickness, border_thickness);
    152   return true;
    153 }
    154 
    155 void BrowserDesktopWindowTreeHostWin::HandleCreate() {
    156   DesktopWindowTreeHostWin::HandleCreate();
    157   browser_window_property_manager_ =
    158       BrowserWindowPropertyManager::CreateBrowserWindowPropertyManager(
    159           browser_view_);
    160   if (browser_window_property_manager_)
    161     browser_window_property_manager_->UpdateWindowProperties(GetHWND());
    162 }
    163 
    164 void BrowserDesktopWindowTreeHostWin::HandleFrameChanged() {
    165   // Reinitialize the status bubble, since it needs to be initialized
    166   // differently depending on whether or not DWM composition is enabled
    167   browser_view_->InitStatusBubble();
    168 
    169   // We need to update the glass region on or off before the base class adjusts
    170   // the window region.
    171   UpdateDWMFrame();
    172   DesktopWindowTreeHostWin::HandleFrameChanged();
    173 }
    174 
    175 bool BrowserDesktopWindowTreeHostWin::PreHandleMSG(UINT message,
    176                                                    WPARAM w_param,
    177                                                    LPARAM l_param,
    178                                                    LRESULT* result) {
    179   switch (message) {
    180     case WM_ACTIVATE:
    181       if (LOWORD(w_param) != WA_INACTIVE)
    182         minimize_button_metrics_.OnHWNDActivated();
    183       return false;
    184     case WM_ENDSESSION:
    185       chrome::SessionEnding();
    186       return true;
    187     case WM_INITMENUPOPUP:
    188       GetSystemMenu()->UpdateStates();
    189       return true;
    190   }
    191   return DesktopWindowTreeHostWin::PreHandleMSG(
    192       message, w_param, l_param, result);
    193 }
    194 
    195 void BrowserDesktopWindowTreeHostWin::PostHandleMSG(UINT message,
    196                                                     WPARAM w_param,
    197                                                     LPARAM l_param) {
    198   switch (message) {
    199   case WM_CREATE:
    200     minimize_button_metrics_.Init(GetHWND());
    201     break;
    202   case WM_WINDOWPOSCHANGED: {
    203     UpdateDWMFrame();
    204 
    205     // Windows lies to us about the position of the minimize button before a
    206     // window is visible.  We use this position to place the OTR avatar in RTL
    207     // mode, so when the window is shown, we need to re-layout and schedule a
    208     // paint for the non-client frame view so that the icon top has the correct
    209     // position when the window becomes visible.  This fixes bugs where the icon
    210     // appears to overlay the minimize button.
    211     // Note that we will call Layout every time SetWindowPos is called with
    212     // SWP_SHOWWINDOW, however callers typically are careful about not
    213     // specifying this flag unless necessary to avoid flicker.
    214     // This may be invoked during creation on XP and before the non_client_view
    215     // has been created.
    216     WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(l_param);
    217     if (window_pos->flags & SWP_SHOWWINDOW && GetWidget()->non_client_view()) {
    218       GetWidget()->non_client_view()->Layout();
    219       GetWidget()->non_client_view()->SchedulePaint();
    220     }
    221     break;
    222   }
    223   case WM_ERASEBKGND:
    224     if (!did_gdi_clear_ && DesktopWindowTreeHostWin::ShouldUseNativeFrame()) {
    225       // This is necessary to avoid white flashing in the titlebar area around
    226       // the minimize/maximize/close buttons.
    227       HDC dc = GetDC(GetHWND());
    228       MARGINS margins = GetDWMFrameMargins();
    229       RECT client_rect;
    230       GetClientRect(GetHWND(), &client_rect);
    231       HBRUSH brush = CreateSolidBrush(0);
    232       RECT rect = { 0, 0, client_rect.right, margins.cyTopHeight };
    233       FillRect(dc, &rect, brush);
    234       DeleteObject(brush);
    235       ReleaseDC(GetHWND(), dc);
    236       did_gdi_clear_ = true;
    237     }
    238     break;
    239   }
    240 }
    241 
    242 
    243 bool BrowserDesktopWindowTreeHostWin::IsUsingCustomFrame() const {
    244   // We don't theme popup or app windows, so regardless of whether or not a
    245   // theme is active for normal browser windows, we don't want to use the custom
    246   // frame for popups/apps.
    247   if (!browser_view_->IsBrowserTypeNormal() &&
    248       !DesktopWindowTreeHostWin::IsUsingCustomFrame()) {
    249     return false;
    250   }
    251 
    252   // Otherwise, we use the native frame when we're told we should by the theme
    253   // provider (e.g. no custom theme is active).
    254   return !GetWidget()->GetThemeProvider()->ShouldUseNativeFrame();
    255 }
    256 
    257 bool BrowserDesktopWindowTreeHostWin::ShouldUseNativeFrame() const {
    258   if (!views::DesktopWindowTreeHostWin::ShouldUseNativeFrame())
    259     return false;
    260   // This function can get called when the Browser window is closed i.e. in the
    261   // context of the BrowserView destructor.
    262   if (!browser_view_->browser())
    263     return false;
    264   return chrome::ShouldUseNativeFrame(browser_view_,
    265                                       GetWidget()->GetThemeProvider());
    266 }
    267 
    268 void BrowserDesktopWindowTreeHostWin::FrameTypeChanged() {
    269   views::DesktopWindowTreeHostWin::FrameTypeChanged();
    270   did_gdi_clear_ = false;
    271 }
    272 
    273 ////////////////////////////////////////////////////////////////////////////////
    274 // BrowserDesktopWindowTreeHostWin, private:
    275 
    276 void BrowserDesktopWindowTreeHostWin::UpdateDWMFrame() {
    277   // For "normal" windows on Aero, we always need to reset the glass area
    278   // correctly, even if we're not currently showing the native frame (e.g.
    279   // because a theme is showing), so we explicitly check for that case rather
    280   // than checking browser_frame_->ShouldUseNativeFrame() here.  Using that here
    281   // would mean we wouldn't reset the glass area to zero when moving from the
    282   // native frame to an opaque frame, leading to graphical glitches behind the
    283   // opaque frame.  Instead, we use that function below to tell us whether the
    284   // frame is currently native or opaque.
    285   if (!GetWidget()->client_view() || !browser_view_->IsBrowserTypeNormal() ||
    286       !DesktopWindowTreeHostWin::ShouldUseNativeFrame())
    287     return;
    288 
    289   MARGINS margins = GetDWMFrameMargins();
    290 
    291   DwmExtendFrameIntoClientArea(GetHWND(), &margins);
    292 }
    293 
    294 MARGINS BrowserDesktopWindowTreeHostWin::GetDWMFrameMargins() const {
    295   MARGINS margins = { 0 };
    296 
    297   // If the opaque frame is visible, we use the default (zero) margins.
    298   // Otherwise, we need to figure out how to extend the glass in.
    299   if (GetWidget()->ShouldUseNativeFrame()) {
    300     // In fullscreen mode, we don't extend glass into the client area at all,
    301     // because the GDI-drawn text in the web content composited over it will
    302     // become semi-transparent over any glass area.
    303     if (!IsMaximized() && !GetWidget()->IsFullscreen()) {
    304       margins.cxLeftWidth = kClientEdgeThickness + 1;
    305       margins.cxRightWidth = kClientEdgeThickness + 1;
    306       margins.cyBottomHeight = kClientEdgeThickness + 1;
    307       margins.cyTopHeight = kClientEdgeThickness + 1;
    308     }
    309     // In maximized mode, we only have a titlebar strip of glass, no side/bottom
    310     // borders.
    311     if (!browser_view_->IsFullscreen()) {
    312       gfx::Rect tabstrip_bounds(
    313           browser_frame_->GetBoundsForTabStrip(browser_view_->tabstrip()));
    314       tabstrip_bounds = gfx::win::DIPToScreenRect(tabstrip_bounds);
    315       margins.cyTopHeight = tabstrip_bounds.bottom() + kDWMFrameTopOffset;
    316     }
    317   }
    318   return margins;
    319 }
    320 
    321 ////////////////////////////////////////////////////////////////////////////////
    322 // BrowserDesktopWindowTreeHost, public:
    323 
    324 // static
    325 BrowserDesktopWindowTreeHost*
    326     BrowserDesktopWindowTreeHost::CreateBrowserDesktopWindowTreeHost(
    327         views::internal::NativeWidgetDelegate* native_widget_delegate,
    328         views::DesktopNativeWidgetAura* desktop_native_widget_aura,
    329         BrowserView* browser_view,
    330         BrowserFrame* browser_frame) {
    331   return new BrowserDesktopWindowTreeHostWin(native_widget_delegate,
    332                                              desktop_native_widget_aura,
    333                                              browser_view,
    334                                              browser_frame);
    335 }
    336