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/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
14 #include "content/public/browser/browser_accessibility_state.h"
15 #include "content/public/common/content_switches.h"
16 #include "ui/base/touch/touch_enabled.h"
17 #include "ui/base/view_prop.h"
18 #include "ui/base/win/internal_constants.h"
19 #include "ui/base/win/window_event_target.h"
20 #include "ui/gfx/geometry/rect.h"
21 #include "ui/gfx/win/direct_manipulation.h"
22 #include "ui/gfx/win/dpi.h"
26 // A custom MSAA object id used to determine if a screen reader or some
27 // other client is listening on MSAA events - if so, we enable full web
28 // accessibility support.
29 const int kIdScreenReaderHoneyPot
= 1;
32 LegacyRenderWidgetHostHWND
* LegacyRenderWidgetHostHWND::Create(
34 // content_unittests passes in the desktop window as the parent. We allow
35 // the LegacyRenderWidgetHostHWND instance to be created in this case for
36 // these tests to pass.
37 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
38 switches::kDisableLegacyIntermediateWindow
) ||
39 (!GetWindowEventTarget(parent
) && parent
!= ::GetDesktopWindow()))
42 LegacyRenderWidgetHostHWND
* legacy_window_instance
=
43 new LegacyRenderWidgetHostHWND(parent
);
44 // If we failed to create the child, or if the switch to disable the legacy
45 // window is passed in, then return NULL.
46 if (!::IsWindow(legacy_window_instance
->hwnd())) {
47 delete legacy_window_instance
;
50 legacy_window_instance
->Init();
51 return legacy_window_instance
;
54 void LegacyRenderWidgetHostHWND::Destroy() {
55 if (::IsWindow(hwnd()))
56 ::DestroyWindow(hwnd());
59 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent
) {
60 if (GetWindowEventTarget(GetParent()))
61 GetWindowEventTarget(GetParent())->HandleParentChanged();
62 ::SetParent(hwnd(), parent
);
63 // If the new parent is the desktop Window, then we disable the child window
64 // to ensure that it does not receive any input events. It should not because
65 // of WS_EX_TRANSPARENT. This is only for safety.
66 if (parent
== ::GetDesktopWindow()) {
67 ::EnableWindow(hwnd(), FALSE
);
69 ::EnableWindow(hwnd(), TRUE
);
73 HWND
LegacyRenderWidgetHostHWND::GetParent() {
74 return ::GetParent(hwnd());
77 void LegacyRenderWidgetHostHWND::Show() {
78 ::ShowWindow(hwnd(), SW_SHOW
);
81 void LegacyRenderWidgetHostHWND::Hide() {
82 ::ShowWindow(hwnd(), SW_HIDE
);
85 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect
& bounds
) {
86 gfx::Rect bounds_in_pixel
= gfx::win::DIPToScreenRect(bounds
);
87 ::SetWindowPos(hwnd(), NULL
, bounds_in_pixel
.x(), bounds_in_pixel
.y(),
88 bounds_in_pixel
.width(), bounds_in_pixel
.height(),
90 if (direct_manipulation_helper_
)
91 direct_manipulation_helper_
->SetBounds(bounds_in_pixel
);
94 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd
) {
96 host_
->OnLegacyWindowDestroyed();
102 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent
)
103 : mouse_tracking_enabled_(false),
106 Base::Create(parent
, rect
, L
"Chrome Legacy Window",
107 WS_CHILDWINDOW
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
111 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
112 DCHECK(!::IsWindow(hwnd()));
115 bool LegacyRenderWidgetHostHWND::Init() {
116 if (base::win::GetVersion() >= base::win::VERSION_WIN7
&&
117 ui::AreTouchEventsEnabled())
118 RegisterTouchWindow(hwnd(), TWF_WANTPALM
);
120 HRESULT hr
= ::CreateStdAccessibleObject(
121 hwnd(), OBJID_WINDOW
, IID_IAccessible
,
122 reinterpret_cast<void **>(window_accessible_
.Receive()));
123 DCHECK(SUCCEEDED(hr
));
125 if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
126 // Attempt to detect screen readers or other clients who want full
127 // accessibility support, by seeing if they respond to this event.
128 NotifyWinEvent(EVENT_SYSTEM_ALERT
, hwnd(), kIdScreenReaderHoneyPot
,
132 // Direct Manipulation is enabled on Windows 10+. The CreateInstance function
133 // returns NULL if Direct Manipulation is not available.
134 direct_manipulation_helper_
=
135 gfx::win::DirectManipulationHelper::CreateInstance();
136 if (direct_manipulation_helper_
)
137 direct_manipulation_helper_
->Initialize(hwnd());
139 return !!SUCCEEDED(hr
);
143 ui::WindowEventTarget
* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
145 return reinterpret_cast<ui::WindowEventTarget
*>(ui::ViewProp::GetValue(
146 parent
, ui::WindowEventTarget::kWin32InputEventTarget
));
149 LRESULT
LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message
,
155 LRESULT
LegacyRenderWidgetHostHWND::OnGetObject(UINT message
,
158 // Only the lower 32 bits of l_param are valid when checking the object id
159 // because it sometimes gets sign-extended incorrectly (but not always).
160 DWORD obj_id
= static_cast<DWORD
>(static_cast<DWORD_PTR
>(l_param
));
162 if (kIdScreenReaderHoneyPot
== obj_id
) {
163 // When an MSAA client has responded to our fake event on this id,
164 // enable screen reader support.
165 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
166 return static_cast<LRESULT
>(0L);
169 if (OBJID_CLIENT
!= obj_id
|| !host_
)
170 return static_cast<LRESULT
>(0L);
172 RenderWidgetHostImpl
* rwhi
= RenderWidgetHostImpl::From(
173 host_
->GetRenderWidgetHost());
175 return static_cast<LRESULT
>(0L);
177 BrowserAccessibilityManagerWin
* manager
=
178 static_cast<BrowserAccessibilityManagerWin
*>(
179 rwhi
->GetRootBrowserAccessibilityManager());
181 return static_cast<LRESULT
>(0L);
183 base::win::ScopedComPtr
<IAccessible
> root(
184 manager
->GetRoot()->ToBrowserAccessibilityWin());
185 return LresultFromObject(IID_IAccessible
, w_param
,
186 static_cast<IAccessible
*>(root
.Detach()));
189 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
190 // While this works, this has the side effect of converting input messages into
191 // sent messages which changes their priority and could technically result
192 // in these messages starving other messages in the queue. Additionally
193 // keyboard/mouse hooks would not see these messages. The alternative approach
194 // is to set and release capture as needed on the parent to ensure that it
195 // receives all mouse events. However that was shelved due to possible issues
196 // with capture changes.
197 LRESULT
LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message
,
202 if (GetWindowEventTarget(GetParent())) {
203 bool msg_handled
= false;
204 ret
= GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
205 message
, w_param
, l_param
, &msg_handled
);
206 handled
= msg_handled
;
211 LRESULT
LegacyRenderWidgetHostHWND::OnMouseRange(UINT message
,
215 if (message
== WM_MOUSEMOVE
) {
216 if (!mouse_tracking_enabled_
) {
217 mouse_tracking_enabled_
= true;
219 tme
.cbSize
= sizeof(tme
);
220 tme
.dwFlags
= TME_LEAVE
;
221 tme
.hwndTrack
= hwnd();
223 TrackMouseEvent(&tme
);
226 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
227 // in screen coordinates. We should not be converting them to parent
229 if ((message
>= WM_MOUSEFIRST
&& message
<= WM_MOUSELAST
) &&
230 (message
!= WM_MOUSEWHEEL
&& message
!= WM_MOUSEHWHEEL
)) {
232 mouse_coords
.x
= GET_X_LPARAM(l_param
);
233 mouse_coords
.y
= GET_Y_LPARAM(l_param
);
234 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords
, 1);
235 l_param
= MAKELPARAM(mouse_coords
.x
, mouse_coords
.y
);
240 if (GetWindowEventTarget(GetParent())) {
241 bool msg_handled
= false;
242 ret
= GetWindowEventTarget(GetParent())->HandleMouseMessage(
243 message
, w_param
, l_param
, &msg_handled
);
244 handled
= msg_handled
;
245 // If the parent did not handle non client mouse messages, we call
246 // DefWindowProc on the message with the parent window handle. This
247 // ensures that WM_SYSCOMMAND is generated for the parent and we are
248 // out of the picture.
250 (message
>= WM_NCMOUSEMOVE
&& message
<= WM_NCXBUTTONDBLCLK
)) {
251 ret
= ::DefWindowProc(GetParent(), message
, w_param
, l_param
);
256 if (direct_manipulation_helper_
&&
257 (message
== WM_MOUSEWHEEL
|| message
== WM_MOUSEHWHEEL
)) {
258 direct_manipulation_helper_
->HandleMouseWheel(hwnd(), message
, w_param
,
264 LRESULT
LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message
,
267 mouse_tracking_enabled_
= false;
269 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
270 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
271 // has moved outside the bounds of the parent.
273 ::GetCursorPos(&cursor_pos
);
274 if (::WindowFromPoint(cursor_pos
) != GetParent()) {
275 bool msg_handled
= false;
276 ret
= GetWindowEventTarget(GetParent())->HandleMouseMessage(
277 message
, w_param
, l_param
, &msg_handled
);
278 SetMsgHandled(msg_handled
);
284 LRESULT
LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message
,
287 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
288 // message going all the way to the parent which then messes up state
289 // related to focused views, etc. This is because it treats this as if
290 // it lost activation.
291 // Our dummy window should not interfere with focus and activation in
292 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
293 // is preserved. The only exception is if the parent was created with the
294 // WS_EX_NOACTIVATE style.
295 if (::GetWindowLong(GetParent(), GWL_EXSTYLE
) & WS_EX_NOACTIVATE
)
296 return MA_NOACTIVATE
;
297 // On Windows, if we select the menu item by touch and if the window at the
298 // location is another window on the same thread, that window gets a
299 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
300 // correct. We workaround this by setting a property on the window at the
301 // current cursor location. We check for this property in our
302 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
304 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow
)) {
305 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow
);
306 return MA_NOACTIVATE
;
311 LRESULT
LegacyRenderWidgetHostHWND::OnTouch(UINT message
,
315 if (GetWindowEventTarget(GetParent())) {
316 bool msg_handled
= false;
317 ret
= GetWindowEventTarget(GetParent())->HandleTouchMessage(
318 message
, w_param
, l_param
, &msg_handled
);
319 SetMsgHandled(msg_handled
);
324 LRESULT
LegacyRenderWidgetHostHWND::OnScroll(UINT message
,
328 if (GetWindowEventTarget(GetParent())) {
329 bool msg_handled
= false;
330 ret
= GetWindowEventTarget(GetParent())->HandleScrollMessage(
331 message
, w_param
, l_param
, &msg_handled
);
332 SetMsgHandled(msg_handled
);
337 LRESULT
LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message
,
340 if (GetWindowEventTarget(GetParent())) {
341 bool msg_handled
= false;
342 LRESULT hit_test
= GetWindowEventTarget(
343 GetParent())->HandleNcHitTestMessage(message
, w_param
, l_param
,
345 // If the parent returns HTNOWHERE which can happen for popup windows, etc
346 // we return HTCLIENT.
347 if (hit_test
== HTNOWHERE
)
354 LRESULT
LegacyRenderWidgetHostHWND::OnNCPaint(UINT message
,
360 LRESULT
LegacyRenderWidgetHostHWND::OnPaint(UINT message
,
363 PAINTSTRUCT ps
= {0};
364 ::BeginPaint(hwnd(), &ps
);
365 ::EndPaint(hwnd(), &ps
);
369 LRESULT
LegacyRenderWidgetHostHWND::OnSetCursor(UINT message
,
375 LRESULT
LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message
,
378 // Prevent scrollbars, etc from drawing.
382 LRESULT
LegacyRenderWidgetHostHWND::OnSize(UINT message
,
385 // Certain trackpad drivers on Windows have bugs where in they don't generate
386 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
387 // unless there is an entry for Chrome with the class name of the Window.
388 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
389 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
390 // We add these styles to ensure that trackpad/trackpoint scrolling
392 long current_style
= ::GetWindowLong(hwnd(), GWL_STYLE
);
393 ::SetWindowLong(hwnd(), GWL_STYLE
,
394 current_style
| WS_VSCROLL
| WS_HSCROLL
);
398 LRESULT
LegacyRenderWidgetHostHWND::OnWindowPosChanged(UINT message
,
401 WINDOWPOS
* window_pos
= reinterpret_cast<WINDOWPOS
*>(l_param
);
402 if (direct_manipulation_helper_
) {
403 if (window_pos
->flags
& SWP_SHOWWINDOW
) {
404 direct_manipulation_helper_
->Activate(hwnd());
405 } else if (window_pos
->flags
& SWP_HIDEWINDOW
) {
406 direct_manipulation_helper_
->Deactivate(hwnd());
409 SetMsgHandled(FALSE
);
413 } // namespace content