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