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