1// Copyright (c) 2011 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_frame_win.h"
6
7#include <dwmapi.h>
8#include <shellapi.h>
9
10#include <set>
11
12#include "chrome/browser/accessibility/browser_accessibility_state.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/themes/theme_service.h"
15#include "chrome/browser/themes/theme_service_factory.h"
16#include "chrome/browser/ui/browser_list.h"
17#include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
18#include "chrome/browser/ui/views/frame/browser_view.h"
19#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
20#include "grit/theme_resources.h"
21#include "ui/gfx/font.h"
22#include "views/screen.h"
23#include "views/widget/root_view.h"
24#include "views/widget/widget_win.h"
25#include "views/window/window_win.h"
26
27// static
28static const int kClientEdgeThickness = 3;
29static const int kTabDragWindowAlpha = 200;
30// We need to offset the DWMFrame into the toolbar so that the blackness
31// doesn't show up on our rounded corners.
32static const int kDWMFrameTopOffset = 3;
33// If not -1, windows are shown with this state.
34static int explicit_show_state = -1;
35
36// static (Factory method.)
37BrowserFrame* BrowserFrame::Create(BrowserView* browser_view,
38                                   Profile* profile) {
39  BrowserFrameWin* frame = new BrowserFrameWin(browser_view, profile);
40  frame->InitBrowserFrame();
41  return frame;
42}
43
44///////////////////////////////////////////////////////////////////////////////
45// BrowserFrameWin, public:
46
47BrowserFrameWin::BrowserFrameWin(BrowserView* browser_view, Profile* profile)
48    : WindowWin(browser_view),
49      BrowserFrame(browser_view),
50      browser_view_(browser_view),
51      ALLOW_THIS_IN_INITIALIZER_LIST(delegate_(this)) {
52  set_native_browser_frame(this);
53  browser_view_->set_frame(this);
54  non_client_view()->SetFrameView(CreateFrameViewForWindow());
55  // Don't focus anything on creation, selecting a tab will set the focus.
56  set_focus_on_creation(false);
57}
58
59BrowserFrameWin::~BrowserFrameWin() {
60}
61
62void BrowserFrameWin::InitBrowserFrame() {
63  WindowWin::Init(NULL, gfx::Rect());
64}
65
66// static
67void BrowserFrameWin::SetShowState(int state) {
68  explicit_show_state = state;
69}
70
71///////////////////////////////////////////////////////////////////////////////
72// BrowserFrameWin, views::WindowWin overrides:
73
74int BrowserFrameWin::GetShowState() const {
75  if (explicit_show_state != -1)
76    return explicit_show_state;
77
78  STARTUPINFO si = {0};
79  si.cb = sizeof(si);
80  si.dwFlags = STARTF_USESHOWWINDOW;
81  GetStartupInfo(&si);
82  return si.wShowWindow;
83}
84
85gfx::Insets BrowserFrameWin::GetClientAreaInsets() const {
86  // Use the default client insets for an opaque frame or a glass popup/app
87  // frame.
88  if (!non_client_view()->UseNativeFrame() ||
89      !browser_view_->IsBrowserTypeNormal()) {
90    return WindowWin::GetClientAreaInsets();
91  }
92
93  int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
94  // In fullscreen mode, we have no frame. In restored mode, we draw our own
95  // client edge over part of the default frame.
96  if (IsFullscreen())
97    border_thickness = 0;
98  else if (!IsMaximized())
99    border_thickness -= kClientEdgeThickness;
100  return gfx::Insets(0, border_thickness, border_thickness, border_thickness);
101}
102
103bool BrowserFrameWin::GetAccelerator(int cmd_id,
104                                     ui::Accelerator* accelerator) {
105  return browser_view_->GetAccelerator(cmd_id, accelerator);
106}
107
108void BrowserFrameWin::OnEndSession(BOOL ending, UINT logoff) {
109  BrowserList::SessionEnding();
110}
111
112void BrowserFrameWin::OnInitMenuPopup(HMENU menu, UINT position,
113                                      BOOL is_system_menu) {
114  browser_view_->PrepareToRunSystemMenu(menu);
115}
116
117void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
118  WindowWin::OnWindowPosChanged(window_pos);
119  UpdateDWMFrame();
120
121  // Windows lies to us about the position of the minimize button before a
122  // window is visible.  We use this position to place the OTR avatar in RTL
123  // mode, so when the window is shown, we need to re-layout and schedule a
124  // paint for the non-client frame view so that the icon top has the correct
125  // position when the window becomes visible.  This fixes bugs where the icon
126  // appears to overlay the minimize button.
127  // Note that we will call Layout every time SetWindowPos is called with
128  // SWP_SHOWWINDOW, however callers typically are careful about not specifying
129  // this flag unless necessary to avoid flicker.
130  if (window_pos->flags & SWP_SHOWWINDOW) {
131    non_client_view()->Layout();
132    non_client_view()->SchedulePaint();
133  }
134}
135
136ThemeProvider* BrowserFrameWin::GetThemeProvider() const {
137  return ThemeServiceFactory::GetForProfile(
138      browser_view_->browser()->profile());
139}
140
141void BrowserFrameWin::OnScreenReaderDetected() {
142  BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
143  WindowWin::OnScreenReaderDetected();
144}
145
146///////////////////////////////////////////////////////////////////////////////
147// BrowserFrameWin, views::Window overrides:
148
149void BrowserFrameWin::Activate() {
150  // When running under remote desktop, if the remote desktop client is not
151  // active on the users desktop, then none of the windows contained in the
152  // remote desktop will be activated.  However, WindowWin::Activate will still
153  // bring this browser window to the foreground.  We explicitly set ourselves
154  // as the last active browser window to ensure that we get treated as such by
155  // the rest of Chrome.
156  BrowserList::SetLastActive(browser_view_->browser());
157
158  WindowWin::Activate();
159}
160
161void BrowserFrameWin::UpdateFrameAfterFrameChange() {
162  // We need to update the glass region on or off before the base class adjusts
163  // the window region.
164  UpdateDWMFrame();
165  WindowWin::UpdateFrameAfterFrameChange();
166}
167
168views::RootView* BrowserFrameWin::CreateRootView() {
169  return delegate_->DelegateCreateRootView();
170}
171
172views::NonClientFrameView* BrowserFrameWin::CreateFrameViewForWindow() {
173  return delegate_->DelegateCreateFrameViewForWindow();
174}
175
176bool BrowserFrameWin::ShouldUseNativeFrame() const {
177  return AlwaysUseNativeFrame();
178}
179
180////////////////////////////////////////////////////////////////////////////////
181// BrowserFrameWin, NativeBrowserFrame implementation:
182
183views::NativeWindow* BrowserFrameWin::AsNativeWindow() {
184  return this;
185}
186
187const views::NativeWindow* BrowserFrameWin::AsNativeWindow() const {
188  return this;
189}
190
191BrowserNonClientFrameView* BrowserFrameWin::CreateBrowserNonClientFrameView() {
192  if (AlwaysUseNativeFrame())
193    return new GlassBrowserFrameView(this, browser_view_);
194  return browser::CreateBrowserNonClientFrameView(this, browser_view_);
195}
196
197int BrowserFrameWin::GetMinimizeButtonOffset() const {
198  TITLEBARINFOEX titlebar_info;
199  titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
200  SendMessage(GetNativeView(), WM_GETTITLEBARINFOEX, 0, (WPARAM)&titlebar_info);
201
202  CPoint minimize_button_corner(titlebar_info.rgrect[2].left,
203                                titlebar_info.rgrect[2].top);
204  MapWindowPoints(HWND_DESKTOP, GetNativeView(), &minimize_button_corner, 1);
205
206  return minimize_button_corner.x;
207}
208
209ui::ThemeProvider* BrowserFrameWin::GetThemeProviderForFrame() const {
210  // This is implemented for a different interface than GetThemeProvider is,
211  // but they mean the same things.
212  return GetThemeProvider();
213}
214
215bool BrowserFrameWin::AlwaysUseNativeFrame() const {
216  // App panel windows draw their own frame.
217  if (browser_view_->IsBrowserTypePanel())
218    return false;
219
220  // We don't theme popup or app windows, so regardless of whether or not a
221  // theme is active for normal browser windows, we don't want to use the custom
222  // frame for popups/apps.
223  if (!browser_view_->IsBrowserTypeNormal() &&
224      views::WidgetWin::IsAeroGlassEnabled())
225    return true;
226
227  // Otherwise, we use the native frame when we're told we should by the theme
228  // provider (e.g. no custom theme is active).
229  return GetThemeProvider()->ShouldUseNativeFrame();
230}
231
232void BrowserFrameWin::TabStripDisplayModeChanged() {
233  if (GetRootView()->has_children()) {
234    // Make sure the child of the root view gets Layout again.
235    GetRootView()->GetChildViewAt(0)->InvalidateLayout();
236  }
237  GetRootView()->Layout();
238
239  UpdateDWMFrame();
240}
241
242///////////////////////////////////////////////////////////////////////////////
243// BrowserFrameWin, private:
244
245void BrowserFrameWin::UpdateDWMFrame() {
246  // Nothing to do yet, or we're not showing a DWM frame.
247  if (!client_view() || !AlwaysUseNativeFrame())
248    return;
249
250  MARGINS margins = { 0 };
251  if (browser_view_->IsBrowserTypeNormal()) {
252    // In fullscreen mode, we don't extend glass into the client area at all,
253    // because the GDI-drawn text in the web content composited over it will
254    // become semi-transparent over any glass area.
255    if (!IsMaximized() && !IsFullscreen()) {
256      margins.cxLeftWidth = kClientEdgeThickness + 1;
257      margins.cxRightWidth = kClientEdgeThickness + 1;
258      margins.cyBottomHeight = kClientEdgeThickness + 1;
259      margins.cyTopHeight = kClientEdgeThickness + 1;
260    }
261    // In maximized mode, we only have a titlebar strip of glass, no side/bottom
262    // borders.
263    if (!browser_view_->IsFullscreen()) {
264      gfx::Rect tabstrip_bounds(
265          GetBoundsForTabStrip(browser_view_->tabstrip()));
266      margins.cyTopHeight = (browser_view_->UseVerticalTabs() ?
267          tabstrip_bounds.y() : tabstrip_bounds.bottom()) + kDWMFrameTopOffset;
268    }
269  } else {
270    // For popup and app windows we want to use the default margins.
271  }
272  DwmExtendFrameIntoClientArea(GetNativeView(), &margins);
273}
274
275////////////////////////////////////////////////////////////////////////////////
276// BrowserFrame, public:
277
278// static
279const gfx::Font& BrowserFrame::GetTitleFont() {
280  static gfx::Font* title_font =
281      new gfx::Font(views::WindowWin::GetWindowTitleFont());
282  return *title_font;
283}
284
285