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