Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / browser_desktop_window_tree_host_win.cc
blobac7d9c2bad702f65debcafb896b7940447bf9457
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"
7 #include <dwmapi.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")
29 namespace {
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 {
40 public:
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(
66 int id,
67 ui::ScaleFactor scale_factor) const override {
68 return delegate_->GetRawData(id, scale_factor);
71 private:
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())) {
82 return;
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");
102 } // namespace
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;
129 system_menu_.reset(
130 new views::NativeMenuWin(browser_frame_->GetSystemMenuModel(),
131 GetHWND()));
132 system_menu_->Rebuild(&insertion_delegate);
134 return system_menu_.get();
137 ////////////////////////////////////////////////////////////////////////////////
138 // BrowserDesktopWindowTreeHostWin, BrowserDesktopWindowTreeHost implementation:
140 views::DesktopWindowTreeHost*
141 BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() {
142 return this;
145 int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const {
146 return minimize_button_metrics_.GetMinimizeButtonOffsetX();
149 bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const {
150 return true;
153 ////////////////////////////////////////////////////////////////////////////////
154 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
156 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
157 STARTUPINFO si = {0};
158 si.cb = sizeof(si);
159 si.dwFlags = STARTF_USESHOWWINDOW;
160 GetStartupInfo(&si);
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
167 // frame.
168 if (!GetWidget()->ShouldUseNativeFrame() ||
169 !browser_view_->IsBrowserTypeNormal()) {
170 return false;
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);
181 return true;
184 void BrowserDesktopWindowTreeHostWin::HandleCreate() {
185 DesktopWindowTreeHostWin::HandleCreate();
186 browser_window_property_manager_ =
187 BrowserWindowPropertyManager::CreateBrowserWindowPropertyManager(
188 browser_view_);
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.
200 UpdateDWMFrame();
201 DesktopWindowTreeHostWin::HandleFrameChanged();
204 bool BrowserDesktopWindowTreeHostWin::PreHandleMSG(UINT message,
205 WPARAM w_param,
206 LPARAM l_param,
207 LRESULT* result) {
208 switch (message) {
209 case WM_ACTIVATE:
210 if (LOWORD(w_param) != WA_INACTIVE)
211 minimize_button_metrics_.OnHWNDActivated();
212 return false;
213 case WM_ENDSESSION:
214 TraceSessionEnding(l_param);
215 chrome::SessionEnding();
216 return true;
217 case WM_INITMENUPOPUP:
218 GetSystemMenu()->UpdateStates();
219 return true;
221 return DesktopWindowTreeHostWin::PreHandleMSG(
222 message, w_param, l_param, result);
225 void BrowserDesktopWindowTreeHostWin::PostHandleMSG(UINT message,
226 WPARAM w_param,
227 LPARAM l_param) {
228 switch (message) {
229 case WM_CREATE:
230 minimize_button_metrics_.Init(GetHWND());
231 break;
232 case WM_WINDOWPOSCHANGED: {
233 UpdateDWMFrame();
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
245 // has been created.
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();
251 break;
253 case WM_ERASEBKGND:
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();
259 RECT client_rect;
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);
264 DeleteObject(brush);
265 ReleaseDC(GetHWND(), dc);
266 did_gdi_clear_ = true;
268 break;
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()) {
279 return false;
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())
289 return false;
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())
293 return false;
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())
317 return;
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;
342 } else {
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
349 // borders.
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;
357 return margins;
360 ////////////////////////////////////////////////////////////////////////////////
361 // BrowserDesktopWindowTreeHost, public:
363 // static
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,
372 browser_view,
373 browser_frame);