1 // Copyright (c) 2014 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 "content/browser/renderer_host/legacy_render_widget_host_win.h"
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/windows_version.h"
10 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
11 #include "content/browser/accessibility/browser_accessibility_win.h"
12 #include "content/public/common/content_switches.h"
13 #include "ui/base/touch/touch_enabled.h"
14 #include "ui/base/view_prop.h"
15 #include "ui/base/win/internal_constants.h"
16 #include "ui/base/win/window_event_target.h"
17 #include "ui/gfx/geometry/rect.h"
21 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
22 ::DestroyWindow(hwnd());
26 scoped_ptr
<LegacyRenderWidgetHostHWND
> LegacyRenderWidgetHostHWND::Create(
28 // content_unittests passes in the desktop window as the parent. We allow
29 // the LegacyRenderWidgetHostHWND instance to be created in this case for
30 // these tests to pass.
31 if (CommandLine::ForCurrentProcess()->HasSwitch(
32 switches::kDisableLegacyIntermediateWindow
) ||
33 (!GetWindowEventTarget(parent
) && parent
!= ::GetDesktopWindow()))
34 return scoped_ptr
<LegacyRenderWidgetHostHWND
>();
36 scoped_ptr
<LegacyRenderWidgetHostHWND
> legacy_window_instance
;
37 legacy_window_instance
.reset(new LegacyRenderWidgetHostHWND(parent
));
38 // If we failed to create the child, or if the switch to disable the legacy
39 // window is passed in, then return NULL.
40 if (!::IsWindow(legacy_window_instance
->hwnd()))
41 return scoped_ptr
<LegacyRenderWidgetHostHWND
>();
43 legacy_window_instance
->Init();
44 return legacy_window_instance
.Pass();
47 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent
) {
48 ::SetParent(hwnd(), parent
);
49 // If the new parent is the desktop Window, then we disable the child window
50 // to ensure that it does not receive any input events. It should not because
51 // of WS_EX_TRANSPARENT. This is only for safety.
52 if (parent
== ::GetDesktopWindow()) {
53 ::EnableWindow(hwnd(), FALSE
);
55 ::EnableWindow(hwnd(), TRUE
);
59 HWND
LegacyRenderWidgetHostHWND::GetParent() {
60 return ::GetParent(hwnd());
63 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
67 void LegacyRenderWidgetHostHWND::Show() {
68 ::ShowWindow(hwnd(), SW_SHOW
);
71 void LegacyRenderWidgetHostHWND::Hide() {
72 ::ShowWindow(hwnd(), SW_HIDE
);
75 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect
& bounds
) {
76 ::SetWindowPos(hwnd(), NULL
, bounds
.x(), bounds
.y(), bounds
.width(),
80 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd
) {
82 manager_
->OnAccessibleHwndDeleted();
85 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent
)
87 mouse_tracking_enabled_(false) {
89 Base::Create(parent
, rect
, L
"Chrome Legacy Window",
90 WS_CHILDWINDOW
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
94 bool LegacyRenderWidgetHostHWND::Init() {
95 if (base::win::GetVersion() >= base::win::VERSION_WIN7
&&
96 ui::AreTouchEventsEnabled())
97 RegisterTouchWindow(hwnd(), TWF_WANTPALM
);
99 HRESULT hr
= ::CreateStdAccessibleObject(
100 hwnd(), OBJID_WINDOW
, IID_IAccessible
,
101 reinterpret_cast<void **>(window_accessible_
.Receive()));
102 DCHECK(SUCCEEDED(hr
));
103 return !!SUCCEEDED(hr
);
107 ui::WindowEventTarget
* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
109 return reinterpret_cast<ui::WindowEventTarget
*>(ui::ViewProp::GetValue(
110 parent
, ui::WindowEventTarget::kWin32InputEventTarget
));
113 LRESULT
LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message
,
119 LRESULT
LegacyRenderWidgetHostHWND::OnGetObject(UINT message
,
122 if (OBJID_CLIENT
!= l_param
|| !manager_
)
123 return static_cast<LRESULT
>(0L);
125 base::win::ScopedComPtr
<IAccessible
> root(
126 manager_
->GetRoot()->ToBrowserAccessibilityWin());
127 return LresultFromObject(IID_IAccessible
, w_param
,
128 static_cast<IAccessible
*>(root
.Detach()));
131 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
132 // While this works, this has the side effect of converting input messages into
133 // sent messages which changes their priority and could technically result
134 // in these messages starving other messages in the queue. Additionally
135 // keyboard/mouse hooks would not see these messages. The alternative approach
136 // is to set and release capture as needed on the parent to ensure that it
137 // receives all mouse events. However that was shelved due to possible issues
138 // with capture changes.
139 LRESULT
LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message
,
143 if (GetWindowEventTarget(GetParent())) {
144 return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
145 message
, w_param
, l_param
);
150 LRESULT
LegacyRenderWidgetHostHWND::OnMouseRange(UINT message
,
154 if (message
== WM_MOUSEMOVE
) {
155 if (!mouse_tracking_enabled_
) {
156 mouse_tracking_enabled_
= true;
158 tme
.cbSize
= sizeof(tme
);
159 tme
.dwFlags
= TME_LEAVE
;
160 tme
.hwndTrack
= hwnd();
162 TrackMouseEvent(&tme
);
165 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
166 // in screen coordinates. We should not be converting them to parent
168 if ((message
>= WM_MOUSEFIRST
&& message
<= WM_MOUSELAST
) &&
169 (message
!= WM_MOUSEWHEEL
&& message
!= WM_MOUSEHWHEEL
)) {
171 mouse_coords
.x
= GET_X_LPARAM(l_param
);
172 mouse_coords
.y
= GET_Y_LPARAM(l_param
);
173 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords
, 1);
174 l_param
= MAKELPARAM(mouse_coords
.x
, mouse_coords
.y
);
176 if (GetWindowEventTarget(GetParent())) {
177 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
178 message
, w_param
, l_param
);
183 LRESULT
LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message
,
186 mouse_tracking_enabled_
= false;
187 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
188 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
189 // has moved outside the bounds of the parent.
191 ::GetCursorPos(&cursor_pos
);
192 if (::WindowFromPoint(cursor_pos
) != GetParent()) {
193 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
194 message
, w_param
, l_param
);
200 LRESULT
LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message
,
203 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
204 // message going all the way to the parent which then messes up state
205 // related to focused views, etc. This is because it treats this as if
206 // it lost activation.
207 // Our dummy window should not interfere with focus and activation in
208 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
209 // is preserved. The only exception is if the parent was created with the
210 // WS_EX_NOACTIVATE style.
211 if (::GetWindowLong(GetParent(), GWL_EXSTYLE
) & WS_EX_NOACTIVATE
)
212 return MA_NOACTIVATE
;
213 // On Windows, if we select the menu item by touch and if the window at the
214 // location is another window on the same thread, that window gets a
215 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
216 // correct. We workaround this by setting a property on the window at the
217 // current cursor location. We check for this property in our
218 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
220 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow
)) {
221 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow
);
222 return MA_NOACTIVATE
;
227 LRESULT
LegacyRenderWidgetHostHWND::OnTouch(UINT message
,
230 if (GetWindowEventTarget(GetParent())) {
231 return GetWindowEventTarget(GetParent())->HandleTouchMessage(
232 message
, w_param
, l_param
);
237 LRESULT
LegacyRenderWidgetHostHWND::OnScroll(UINT message
,
240 if (GetWindowEventTarget(GetParent())) {
241 return GetWindowEventTarget(GetParent())->HandleScrollMessage(
242 message
, w_param
, l_param
);
247 LRESULT
LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message
,
250 if (GetWindowEventTarget(GetParent())) {
251 LRESULT hit_test
= GetWindowEventTarget(
252 GetParent())->HandleNcHitTestMessage(message
, w_param
, l_param
);
253 // If the parent returns HTNOWHERE which can happen for popup windows, etc
254 // we return HTCLIENT.
255 if (hit_test
== HTNOWHERE
)
262 LRESULT
LegacyRenderWidgetHostHWND::OnNCPaint(UINT message
,
268 LRESULT
LegacyRenderWidgetHostHWND::OnPaint(UINT message
,
271 PAINTSTRUCT ps
= {0};
272 ::BeginPaint(hwnd(), &ps
);
273 ::EndPaint(hwnd(), &ps
);
277 LRESULT
LegacyRenderWidgetHostHWND::OnSetCursor(UINT message
,
283 LRESULT
LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message
,
286 // Prevent scrollbars, etc from drawing.
290 LRESULT
LegacyRenderWidgetHostHWND::OnSize(UINT message
,
293 // Certain trackpad drivers on Windows have bugs where in they don't generate
294 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
295 // unless there is an entry for Chrome with the class name of the Window.
296 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
297 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
298 // We add these styles to ensure that trackpad/trackpoint scrolling
300 long current_style
= ::GetWindowLong(hwnd(), GWL_STYLE
);
301 ::SetWindowLong(hwnd(), GWL_STYLE
,
302 current_style
| WS_VSCROLL
| WS_HSCROLL
);
306 } // namespace content