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.
5 #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.h"
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"
24 #pragma comment(lib, "dwmapi.lib")
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;
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
{
38 explicit DesktopThemeProvider(ui::ThemeProvider
* delegate
)
39 : delegate_(delegate
) {
42 virtual bool UsingNativeTheme() const OVERRIDE
{
43 return delegate_
->UsingNativeTheme();
45 virtual gfx::ImageSkia
* GetImageSkiaNamed(int id
) const OVERRIDE
{
46 return delegate_
->GetImageSkiaNamed(
47 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE
, id
));
49 virtual SkColor
GetColor(int id
) const OVERRIDE
{
50 return delegate_
->GetColor(id
);
52 virtual int GetDisplayProperty(int id
) const OVERRIDE
{
53 return delegate_
->GetDisplayProperty(id
);
55 virtual bool ShouldUseNativeFrame() const OVERRIDE
{
56 return delegate_
->ShouldUseNativeFrame();
58 virtual bool HasCustomImage(int id
) const OVERRIDE
{
59 return delegate_
->HasCustomImage(
60 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE
, id
));
63 virtual base::RefCountedMemory
* GetRawData(
65 ui::ScaleFactor scale_factor
) const OVERRIDE
{
66 return delegate_
->GetRawData(id
, scale_factor
);
70 ui::ThemeProvider
* delegate_
;
72 DISALLOW_COPY_AND_ASSIGN(DesktopThemeProvider
);
77 ////////////////////////////////////////////////////////////////////////////////
78 // BrowserDesktopWindowTreeHostWin, public:
80 BrowserDesktopWindowTreeHostWin::BrowserDesktopWindowTreeHostWin(
81 views::internal::NativeWidgetDelegate
* native_widget_delegate
,
82 views::DesktopNativeWidgetAura
* desktop_native_widget_aura
,
83 BrowserView
* browser_view
,
84 BrowserFrame
* browser_frame
)
85 : DesktopWindowTreeHostWin(native_widget_delegate
,
86 desktop_native_widget_aura
),
87 browser_view_(browser_view
),
88 browser_frame_(browser_frame
),
89 did_gdi_clear_(false) {
90 scoped_ptr
<ui::ThemeProvider
> theme_provider(
91 new DesktopThemeProvider(ThemeServiceFactory::GetForProfile(
92 browser_view
->browser()->profile())));
93 browser_frame
->SetThemeProvider(theme_provider
.Pass());
96 BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {
99 views::NativeMenuWin
* BrowserDesktopWindowTreeHostWin::GetSystemMenu() {
100 if (!system_menu_
.get()) {
101 SystemMenuInsertionDelegateWin insertion_delegate
;
103 new views::NativeMenuWin(browser_frame_
->GetSystemMenuModel(),
105 system_menu_
->Rebuild(&insertion_delegate
);
107 return system_menu_
.get();
110 ////////////////////////////////////////////////////////////////////////////////
111 // BrowserDesktopWindowTreeHostWin, BrowserDesktopWindowTreeHost implementation:
113 views::DesktopWindowTreeHost
*
114 BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() {
118 int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const {
119 return minimize_button_metrics_
.GetMinimizeButtonOffsetX();
122 bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const {
126 ////////////////////////////////////////////////////////////////////////////////
127 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
129 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
130 STARTUPINFO si
= {0};
132 si
.dwFlags
= STARTF_USESHOWWINDOW
;
134 return si
.wShowWindow
;
137 bool BrowserDesktopWindowTreeHostWin::GetClientAreaInsets(
138 gfx::Insets
* insets
) const {
139 // Use the default client insets for an opaque frame or a glass popup/app
141 if (!GetWidget()->ShouldUseNativeFrame() ||
142 !browser_view_
->IsBrowserTypeNormal()) {
146 int border_thickness
=
147 GetSystemMetrics(SM_CXSIZEFRAME
) + GetSystemMetrics(SM_CXPADDEDBORDER
);
148 // In fullscreen mode, we have no frame. In restored mode, we draw our own
149 // client edge over part of the default frame.
150 if (GetWidget()->IsFullscreen())
151 border_thickness
= 0;
152 else if (!IsMaximized())
153 border_thickness
-= kClientEdgeThickness
;
154 insets
->Set(0, border_thickness
, border_thickness
, border_thickness
);
158 void BrowserDesktopWindowTreeHostWin::HandleCreate() {
159 DesktopWindowTreeHostWin::HandleCreate();
160 browser_window_property_manager_
=
161 BrowserWindowPropertyManager::CreateBrowserWindowPropertyManager(
163 if (browser_window_property_manager_
)
164 browser_window_property_manager_
->UpdateWindowProperties(GetHWND());
167 void BrowserDesktopWindowTreeHostWin::HandleFrameChanged() {
168 // Reinitialize the status bubble, since it needs to be initialized
169 // differently depending on whether or not DWM composition is enabled
170 browser_view_
->InitStatusBubble();
172 // We need to update the glass region on or off before the base class adjusts
173 // the window region.
175 DesktopWindowTreeHostWin::HandleFrameChanged();
178 bool BrowserDesktopWindowTreeHostWin::PreHandleMSG(UINT message
,
184 if (LOWORD(w_param
) != WA_INACTIVE
)
185 minimize_button_metrics_
.OnHWNDActivated();
188 chrome::SessionEnding();
190 case WM_INITMENUPOPUP
:
191 GetSystemMenu()->UpdateStates();
194 return DesktopWindowTreeHostWin::PreHandleMSG(
195 message
, w_param
, l_param
, result
);
198 void BrowserDesktopWindowTreeHostWin::PostHandleMSG(UINT message
,
203 minimize_button_metrics_
.Init(GetHWND());
205 case WM_WINDOWPOSCHANGED
: {
208 // Windows lies to us about the position of the minimize button before a
209 // window is visible. We use this position to place the OTR avatar in RTL
210 // mode, so when the window is shown, we need to re-layout and schedule a
211 // paint for the non-client frame view so that the icon top has the correct
212 // position when the window becomes visible. This fixes bugs where the icon
213 // appears to overlay the minimize button.
214 // Note that we will call Layout every time SetWindowPos is called with
215 // SWP_SHOWWINDOW, however callers typically are careful about not
216 // specifying this flag unless necessary to avoid flicker.
217 // This may be invoked during creation on XP and before the non_client_view
219 WINDOWPOS
* window_pos
= reinterpret_cast<WINDOWPOS
*>(l_param
);
220 if (window_pos
->flags
& SWP_SHOWWINDOW
&& GetWidget()->non_client_view()) {
221 GetWidget()->non_client_view()->Layout();
222 GetWidget()->non_client_view()->SchedulePaint();
227 if (!did_gdi_clear_
&& DesktopWindowTreeHostWin::ShouldUseNativeFrame()) {
228 // This is necessary to avoid white flashing in the titlebar area around
229 // the minimize/maximize/close buttons.
230 HDC dc
= GetDC(GetHWND());
231 MARGINS margins
= GetDWMFrameMargins();
233 GetClientRect(GetHWND(), &client_rect
);
234 HBRUSH brush
= CreateSolidBrush(0);
235 RECT rect
= { 0, 0, client_rect
.right
, margins
.cyTopHeight
};
236 FillRect(dc
, &rect
, brush
);
238 ReleaseDC(GetHWND(), dc
);
239 did_gdi_clear_
= true;
246 bool BrowserDesktopWindowTreeHostWin::IsUsingCustomFrame() const {
247 // We don't theme popup or app windows, so regardless of whether or not a
248 // theme is active for normal browser windows, we don't want to use the custom
249 // frame for popups/apps.
250 if (!browser_view_
->IsBrowserTypeNormal() &&
251 !DesktopWindowTreeHostWin::IsUsingCustomFrame()) {
255 // Otherwise, we use the native frame when we're told we should by the theme
256 // provider (e.g. no custom theme is active).
257 return !GetWidget()->GetThemeProvider()->ShouldUseNativeFrame();
260 bool BrowserDesktopWindowTreeHostWin::ShouldUseNativeFrame() const {
261 if (!views::DesktopWindowTreeHostWin::ShouldUseNativeFrame())
263 // This function can get called when the Browser window is closed i.e. in the
264 // context of the BrowserView destructor.
265 if (!browser_view_
->browser())
267 return chrome::ShouldUseNativeFrame(browser_view_
,
268 GetWidget()->GetThemeProvider());
271 void BrowserDesktopWindowTreeHostWin::FrameTypeChanged() {
272 views::DesktopWindowTreeHostWin::FrameTypeChanged();
273 did_gdi_clear_
= false;
276 ////////////////////////////////////////////////////////////////////////////////
277 // BrowserDesktopWindowTreeHostWin, private:
279 void BrowserDesktopWindowTreeHostWin::UpdateDWMFrame() {
280 // For "normal" windows on Aero, we always need to reset the glass area
281 // correctly, even if we're not currently showing the native frame (e.g.
282 // because a theme is showing), so we explicitly check for that case rather
283 // than checking browser_frame_->ShouldUseNativeFrame() here. Using that here
284 // would mean we wouldn't reset the glass area to zero when moving from the
285 // native frame to an opaque frame, leading to graphical glitches behind the
286 // opaque frame. Instead, we use that function below to tell us whether the
287 // frame is currently native or opaque.
288 if (!GetWidget()->client_view() || !browser_view_
->IsBrowserTypeNormal() ||
289 !DesktopWindowTreeHostWin::ShouldUseNativeFrame())
292 MARGINS margins
= GetDWMFrameMargins();
294 DwmExtendFrameIntoClientArea(GetHWND(), &margins
);
297 MARGINS
BrowserDesktopWindowTreeHostWin::GetDWMFrameMargins() const {
298 MARGINS margins
= { 0 };
300 // If the opaque frame is visible, we use the default (zero) margins.
301 // Otherwise, we need to figure out how to extend the glass in.
302 if (GetWidget()->ShouldUseNativeFrame()) {
303 // In fullscreen mode, we don't extend glass into the client area at all,
304 // because the GDI-drawn text in the web content composited over it will
305 // become semi-transparent over any glass area.
306 if (!IsMaximized() && !GetWidget()->IsFullscreen()) {
307 margins
.cxLeftWidth
= kClientEdgeThickness
+ 1;
308 margins
.cxRightWidth
= kClientEdgeThickness
+ 1;
309 margins
.cyBottomHeight
= kClientEdgeThickness
+ 1;
310 margins
.cyTopHeight
= kClientEdgeThickness
+ 1;
312 // In maximized mode, we only have a titlebar strip of glass, no side/bottom
314 if (!browser_view_
->IsFullscreen()) {
315 gfx::Rect
tabstrip_bounds(
316 browser_frame_
->GetBoundsForTabStrip(browser_view_
->tabstrip()));
317 tabstrip_bounds
= gfx::win::DIPToScreenRect(tabstrip_bounds
);
318 margins
.cyTopHeight
= tabstrip_bounds
.bottom() + kDWMFrameTopOffset
;
324 ////////////////////////////////////////////////////////////////////////////////
325 // BrowserDesktopWindowTreeHost, public:
328 BrowserDesktopWindowTreeHost
*
329 BrowserDesktopWindowTreeHost::CreateBrowserDesktopWindowTreeHost(
330 views::internal::NativeWidgetDelegate
* native_widget_delegate
,
331 views::DesktopNativeWidgetAura
* desktop_native_widget_aura
,
332 BrowserView
* browser_view
,
333 BrowserFrame
* browser_frame
) {
334 return new BrowserDesktopWindowTreeHostWin(native_widget_delegate
,
335 desktop_native_widget_aura
,