browser_desktop_window_tree_host_win.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 25namespace { 26 27const 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. 30const 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. 35class 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 78BrowserDesktopWindowTreeHostWin::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 94BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() { 95} 96 97views::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 111views::DesktopWindowTreeHost* 112 BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() { 113 return this; 114} 115 116int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const { 117 return minimize_button_metrics_.GetMinimizeButtonOffsetX(); 118} 119 120bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const { 121 return true; 122} 123 124//////////////////////////////////////////////////////////////////////////////// 125// BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides: 126 127int 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 135bool 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 155void 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 164void 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 175bool 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 195void 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 243bool 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 257bool 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 268void BrowserDesktopWindowTreeHostWin::FrameTypeChanged() { 269 views::DesktopWindowTreeHostWin::FrameTypeChanged(); 270 did_gdi_clear_ = false; 271} 272 273//////////////////////////////////////////////////////////////////////////////// 274// BrowserDesktopWindowTreeHostWin, private: 275 276void 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 294MARGINS 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 325BrowserDesktopWindowTreeHost* 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