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 "base/process/process_handle.h"
10 #include "base/win/windows_version.h"
11 #include "chrome/browser/lifetime/application_lifetime.h"
12 #include "chrome/browser/themes/theme_service.h"
13 #include "chrome/browser/themes/theme_service_factory.h"
14 #include "chrome/browser/ui/views/frame/browser_frame.h"
15 #include "chrome/browser/ui/views/frame/browser_frame_common_win.h"
16 #include "chrome/browser/ui/views/frame/browser_view.h"
17 #include "chrome/browser/ui/views/frame/browser_window_property_manager_win.h"
18 #include "chrome/browser/ui/views/frame/system_menu_insertion_delegate_win.h"
19 #include "chrome/browser/ui/views/tabs/tab_strip.h"
20 #include "chrome/browser/ui/views/theme_image_mapper.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "components/browser_watcher/exit_funnel_win.h"
23 #include "ui/base/theme_provider.h"
24 #include "ui/gfx/win/dpi.h"
25 #include "ui/views/controls/menu/native_menu_win.h"
27 #pragma comment(lib, "dwmapi.lib")
31 const int kClientEdgeThickness
= 3;
32 // We need to offset the DWMFrame into the toolbar so that the blackness
33 // doesn't show up on our rounded corners.
34 const int kDWMFrameTopOffset
= 3;
36 // DesktopThemeProvider maps resource ids using MapThemeImage(). This is
37 // necessary for BrowserDesktopWindowTreeHostWin so that it uses the windows
38 // theme images rather than the ash theme images.
39 class DesktopThemeProvider
: public ui::ThemeProvider
{
41 explicit DesktopThemeProvider(ui::ThemeProvider
* delegate
)
42 : delegate_(delegate
) {
45 bool UsingSystemTheme() const override
{
46 return delegate_
->UsingSystemTheme();
48 gfx::ImageSkia
* GetImageSkiaNamed(int id
) const override
{
49 return delegate_
->GetImageSkiaNamed(
50 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE
, id
));
52 SkColor
GetColor(int id
) const override
{
53 return delegate_
->GetColor(id
);
55 int GetDisplayProperty(int id
) const override
{
56 return delegate_
->GetDisplayProperty(id
);
58 bool ShouldUseNativeFrame() const override
{
59 return delegate_
->ShouldUseNativeFrame();
61 bool HasCustomImage(int id
) const override
{
62 return delegate_
->HasCustomImage(
63 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE
, id
));
65 base::RefCountedMemory
* GetRawData(
67 ui::ScaleFactor scale_factor
) const override
{
68 return delegate_
->GetRawData(id
, scale_factor
);
72 ui::ThemeProvider
* delegate_
;
74 DISALLOW_COPY_AND_ASSIGN(DesktopThemeProvider
);
77 // See http://crbug.com/412384.
78 void TraceSessionEnding(LPARAM lparam
) {
79 browser_watcher::ExitFunnel funnel
;
80 if (!funnel
.Init(chrome::kBrowserExitCodesRegistryPath
,
81 base::GetCurrentProcessHandle())) {
85 // This exit path is the prime suspect for most our unclean shutdowns.
86 // Trace all the possible options to WM_ENDSESSION. This may result in
87 // multiple events for a single shutdown, but that's fine.
88 funnel
.RecordEvent(L
"WM_ENDSESSION");
90 if (lparam
& ENDSESSION_CLOSEAPP
)
91 funnel
.RecordEvent(L
"ES_CloseApp");
92 if (lparam
& ENDSESSION_CRITICAL
)
93 funnel
.RecordEvent(L
"ES_Critical");
94 if (lparam
& ENDSESSION_LOGOFF
)
95 funnel
.RecordEvent(L
"ES_Logoff");
96 const LPARAM kKnownBits
=
97 ENDSESSION_CLOSEAPP
| ENDSESSION_CRITICAL
| ENDSESSION_LOGOFF
;
98 if (lparam
& ~kKnownBits
)
99 funnel
.RecordEvent(L
"ES_Other");
104 ////////////////////////////////////////////////////////////////////////////////
105 // BrowserDesktopWindowTreeHostWin, public:
107 BrowserDesktopWindowTreeHostWin::BrowserDesktopWindowTreeHostWin(
108 views::internal::NativeWidgetDelegate
* native_widget_delegate
,
109 views::DesktopNativeWidgetAura
* desktop_native_widget_aura
,
110 BrowserView
* browser_view
,
111 BrowserFrame
* browser_frame
)
112 : DesktopWindowTreeHostWin(native_widget_delegate
,
113 desktop_native_widget_aura
),
114 browser_view_(browser_view
),
115 browser_frame_(browser_frame
),
116 did_gdi_clear_(false) {
117 scoped_ptr
<ui::ThemeProvider
> theme_provider(
118 new DesktopThemeProvider(ThemeServiceFactory::GetForProfile(
119 browser_view
->browser()->profile())));
120 browser_frame
->SetThemeProvider(theme_provider
.Pass());
123 BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {
126 views::NativeMenuWin
* BrowserDesktopWindowTreeHostWin::GetSystemMenu() {
127 if (!system_menu_
.get()) {
128 SystemMenuInsertionDelegateWin insertion_delegate
;
130 new views::NativeMenuWin(browser_frame_
->GetSystemMenuModel(),
132 system_menu_
->Rebuild(&insertion_delegate
);
134 return system_menu_
.get();
137 ////////////////////////////////////////////////////////////////////////////////
138 // BrowserDesktopWindowTreeHostWin, BrowserDesktopWindowTreeHost implementation:
140 views::DesktopWindowTreeHost
*
141 BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() {
145 int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const {
146 return minimize_button_metrics_
.GetMinimizeButtonOffsetX();
149 bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const {
153 ////////////////////////////////////////////////////////////////////////////////
154 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
156 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
157 STARTUPINFO si
= {0};
159 si
.dwFlags
= STARTF_USESHOWWINDOW
;
161 return si
.wShowWindow
;
164 bool BrowserDesktopWindowTreeHostWin::GetClientAreaInsets(
165 gfx::Insets
* insets
) const {
166 // Use the default client insets for an opaque frame or a glass popup/app
168 if (!GetWidget()->ShouldUseNativeFrame() ||
169 !browser_view_
->IsBrowserTypeNormal()) {
173 int border_thickness
= GetSystemMetrics(SM_CXSIZEFRAME
);
174 // In fullscreen mode, we have no frame. In restored mode, we draw our own
175 // client edge over part of the default frame.
176 if (GetWidget()->IsFullscreen())
177 border_thickness
= 0;
178 else if (!IsMaximized())
179 border_thickness
-= kClientEdgeThickness
;
180 insets
->Set(0, border_thickness
, border_thickness
, border_thickness
);
184 void BrowserDesktopWindowTreeHostWin::HandleCreate() {
185 DesktopWindowTreeHostWin::HandleCreate();
186 browser_window_property_manager_
=
187 BrowserWindowPropertyManager::CreateBrowserWindowPropertyManager(
189 if (browser_window_property_manager_
)
190 browser_window_property_manager_
->UpdateWindowProperties(GetHWND());
193 void BrowserDesktopWindowTreeHostWin::HandleFrameChanged() {
194 // Reinitialize the status bubble, since it needs to be initialized
195 // differently depending on whether or not DWM composition is enabled
196 browser_view_
->InitStatusBubble();
198 // We need to update the glass region on or off before the base class adjusts
199 // the window region.
201 DesktopWindowTreeHostWin::HandleFrameChanged();
204 bool BrowserDesktopWindowTreeHostWin::PreHandleMSG(UINT message
,
210 if (LOWORD(w_param
) != WA_INACTIVE
)
211 minimize_button_metrics_
.OnHWNDActivated();
214 TraceSessionEnding(l_param
);
215 chrome::SessionEnding();
217 case WM_INITMENUPOPUP
:
218 GetSystemMenu()->UpdateStates();
221 return DesktopWindowTreeHostWin::PreHandleMSG(
222 message
, w_param
, l_param
, result
);
225 void BrowserDesktopWindowTreeHostWin::PostHandleMSG(UINT message
,
230 minimize_button_metrics_
.Init(GetHWND());
232 case WM_WINDOWPOSCHANGED
: {
235 // Windows lies to us about the position of the minimize button before a
236 // window is visible. We use this position to place the OTR avatar in RTL
237 // mode, so when the window is shown, we need to re-layout and schedule a
238 // paint for the non-client frame view so that the icon top has the correct
239 // position when the window becomes visible. This fixes bugs where the icon
240 // appears to overlay the minimize button.
241 // Note that we will call Layout every time SetWindowPos is called with
242 // SWP_SHOWWINDOW, however callers typically are careful about not
243 // specifying this flag unless necessary to avoid flicker.
244 // This may be invoked during creation on XP and before the non_client_view
246 WINDOWPOS
* window_pos
= reinterpret_cast<WINDOWPOS
*>(l_param
);
247 if (window_pos
->flags
& SWP_SHOWWINDOW
&& GetWidget()->non_client_view()) {
248 GetWidget()->non_client_view()->Layout();
249 GetWidget()->non_client_view()->SchedulePaint();
254 if (!did_gdi_clear_
&& DesktopWindowTreeHostWin::ShouldUseNativeFrame()) {
255 // This is necessary to avoid white flashing in the titlebar area around
256 // the minimize/maximize/close buttons.
257 HDC dc
= GetDC(GetHWND());
258 MARGINS margins
= GetDWMFrameMargins();
260 GetClientRect(GetHWND(), &client_rect
);
261 HBRUSH brush
= CreateSolidBrush(0);
262 RECT rect
= { 0, 0, client_rect
.right
, margins
.cyTopHeight
};
263 FillRect(dc
, &rect
, brush
);
265 ReleaseDC(GetHWND(), dc
);
266 did_gdi_clear_
= true;
273 bool BrowserDesktopWindowTreeHostWin::IsUsingCustomFrame() const {
274 // We don't theme popup or app windows, so regardless of whether or not a
275 // theme is active for normal browser windows, we don't want to use the custom
276 // frame for popups/apps.
277 if (!browser_view_
->IsBrowserTypeNormal() &&
278 !DesktopWindowTreeHostWin::IsUsingCustomFrame()) {
282 // Otherwise, we use the native frame when we're told we should by the theme
283 // provider (e.g. no custom theme is active).
284 return !GetWidget()->GetThemeProvider()->ShouldUseNativeFrame();
287 bool BrowserDesktopWindowTreeHostWin::ShouldUseNativeFrame() const {
288 if (!views::DesktopWindowTreeHostWin::ShouldUseNativeFrame())
290 // This function can get called when the Browser window is closed i.e. in the
291 // context of the BrowserView destructor.
292 if (!browser_view_
->browser())
294 return chrome::ShouldUseNativeFrame(browser_view_
,
295 GetWidget()->GetThemeProvider());
298 void BrowserDesktopWindowTreeHostWin::FrameTypeChanged() {
299 views::DesktopWindowTreeHostWin::FrameTypeChanged();
300 did_gdi_clear_
= false;
303 ////////////////////////////////////////////////////////////////////////////////
304 // BrowserDesktopWindowTreeHostWin, private:
306 void BrowserDesktopWindowTreeHostWin::UpdateDWMFrame() {
307 // For "normal" windows on Aero, we always need to reset the glass area
308 // correctly, even if we're not currently showing the native frame (e.g.
309 // because a theme is showing), so we explicitly check for that case rather
310 // than checking browser_frame_->ShouldUseNativeFrame() here. Using that here
311 // would mean we wouldn't reset the glass area to zero when moving from the
312 // native frame to an opaque frame, leading to graphical glitches behind the
313 // opaque frame. Instead, we use that function below to tell us whether the
314 // frame is currently native or opaque.
315 if (!GetWidget()->client_view() || !browser_view_
->IsBrowserTypeNormal() ||
316 !DesktopWindowTreeHostWin::ShouldUseNativeFrame())
319 MARGINS margins
= GetDWMFrameMargins();
321 DwmExtendFrameIntoClientArea(GetHWND(), &margins
);
324 MARGINS
BrowserDesktopWindowTreeHostWin::GetDWMFrameMargins() const {
325 MARGINS margins
= { 0 };
327 // If the opaque frame is visible, we use the default (zero) margins.
328 // Otherwise, we need to figure out how to extend the glass in.
329 if (GetWidget()->ShouldUseNativeFrame()) {
330 // In fullscreen mode, we don't extend glass into the client area at all,
331 // because the GDI-drawn text in the web content composited over it will
332 // become semi-transparent over any glass area.
333 if (!IsMaximized() && !GetWidget()->IsFullscreen()) {
334 margins
.cyTopHeight
= kClientEdgeThickness
+ 1;
335 // On Windows 10, we don't draw our own window border, so don't extend the
336 // nonclient area in for it. The top is extended so that the MARGINS isn't
337 // treated as an empty (ignored) extension.
338 if (base::win::GetVersion() >= base::win::VERSION_WIN10
) {
339 margins
.cxLeftWidth
= 0;
340 margins
.cxRightWidth
= 0;
341 margins
.cyBottomHeight
= 0;
343 margins
.cxLeftWidth
= kClientEdgeThickness
+ 1;
344 margins
.cxRightWidth
= kClientEdgeThickness
+ 1;
345 margins
.cyBottomHeight
= kClientEdgeThickness
+ 1;
348 // In maximized mode, we only have a titlebar strip of glass, no side/bottom
350 if (!browser_view_
->IsFullscreen()) {
351 gfx::Rect
tabstrip_bounds(
352 browser_frame_
->GetBoundsForTabStrip(browser_view_
->tabstrip()));
353 tabstrip_bounds
= gfx::win::DIPToScreenRect(tabstrip_bounds
);
354 margins
.cyTopHeight
= tabstrip_bounds
.bottom() + kDWMFrameTopOffset
;
360 ////////////////////////////////////////////////////////////////////////////////
361 // BrowserDesktopWindowTreeHost, public:
364 BrowserDesktopWindowTreeHost
*
365 BrowserDesktopWindowTreeHost::CreateBrowserDesktopWindowTreeHost(
366 views::internal::NativeWidgetDelegate
* native_widget_delegate
,
367 views::DesktopNativeWidgetAura
* desktop_native_widget_aura
,
368 BrowserView
* browser_view
,
369 BrowserFrame
* browser_frame
) {
370 return new BrowserDesktopWindowTreeHostWin(native_widget_delegate
,
371 desktop_native_widget_aura
,