Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / browser_desktop_window_tree_host_win.cc
blob16dc6e592200835c4a253181a66a670c5ca46964
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 "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"
23 #pragma comment(lib, "dwmapi.lib")
25 namespace {
27 const 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.
30 const int kDWMFrameTopOffset = 3;
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.
35 class DesktopThemeProvider : public ui::ThemeProvider {
36 public:
37 explicit DesktopThemeProvider(ui::ThemeProvider* delegate)
38 : delegate_(delegate) {
41 virtual bool UsingSystemTheme() const override {
42 return delegate_->UsingSystemTheme();
44 virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const override {
45 return delegate_->GetImageSkiaNamed(
46 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE, id));
48 virtual SkColor GetColor(int id) const override {
49 return delegate_->GetColor(id);
51 virtual int GetDisplayProperty(int id) const override {
52 return delegate_->GetDisplayProperty(id);
54 virtual bool ShouldUseNativeFrame() const override {
55 return delegate_->ShouldUseNativeFrame();
57 virtual bool HasCustomImage(int id) const override {
58 return delegate_->HasCustomImage(
59 chrome::MapThemeImage(chrome::HOST_DESKTOP_TYPE_NATIVE, id));
61 virtual base::RefCountedMemory* GetRawData(
62 int id,
63 ui::ScaleFactor scale_factor) const override {
64 return delegate_->GetRawData(id, scale_factor);
67 private:
68 ui::ThemeProvider* delegate_;
70 DISALLOW_COPY_AND_ASSIGN(DesktopThemeProvider);
73 } // namespace
75 ////////////////////////////////////////////////////////////////////////////////
76 // BrowserDesktopWindowTreeHostWin, public:
78 BrowserDesktopWindowTreeHostWin::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());
94 BrowserDesktopWindowTreeHostWin::~BrowserDesktopWindowTreeHostWin() {
97 views::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);
105 return system_menu_.get();
108 ////////////////////////////////////////////////////////////////////////////////
109 // BrowserDesktopWindowTreeHostWin, BrowserDesktopWindowTreeHost implementation:
111 views::DesktopWindowTreeHost*
112 BrowserDesktopWindowTreeHostWin::AsDesktopWindowTreeHost() {
113 return this;
116 int BrowserDesktopWindowTreeHostWin::GetMinimizeButtonOffset() const {
117 return minimize_button_metrics_.GetMinimizeButtonOffsetX();
120 bool BrowserDesktopWindowTreeHostWin::UsesNativeSystemMenu() const {
121 return true;
124 ////////////////////////////////////////////////////////////////////////////////
125 // BrowserDesktopWindowTreeHostWin, views::DesktopWindowTreeHostWin overrides:
127 int BrowserDesktopWindowTreeHostWin::GetInitialShowState() const {
128 STARTUPINFO si = {0};
129 si.cb = sizeof(si);
130 si.dwFlags = STARTF_USESHOWWINDOW;
131 GetStartupInfo(&si);
132 return si.wShowWindow;
135 bool 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;
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;
155 void 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());
164 void 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();
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();
175 bool 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;
191 return DesktopWindowTreeHostWin::PreHandleMSG(
192 message, w_param, l_param, result);
195 void 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();
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();
221 break;
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;
238 break;
243 bool 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;
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();
257 bool 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());
268 void BrowserDesktopWindowTreeHostWin::FrameTypeChanged() {
269 views::DesktopWindowTreeHostWin::FrameTypeChanged();
270 did_gdi_clear_ = false;
273 ////////////////////////////////////////////////////////////////////////////////
274 // BrowserDesktopWindowTreeHostWin, private:
276 void 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;
289 MARGINS margins = GetDWMFrameMargins();
291 DwmExtendFrameIntoClientArea(GetHWND(), &margins);
294 MARGINS BrowserDesktopWindowTreeHostWin::GetDWMFrameMargins() const {
295 MARGINS margins = { 0 };
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;
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;
318 return margins;
321 ////////////////////////////////////////////////////////////////////////////////
322 // BrowserDesktopWindowTreeHost, public:
324 // static
325 BrowserDesktopWindowTreeHost*
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);