Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / legacy_render_widget_host_win.cc
blobbd0101d1007c5157d15e0cdc671b4cb422bc5f40
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"
24 namespace content {
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;
31 // static
32 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
33 HWND parent) {
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()))
40 return nullptr;
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;
48 return NULL;
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);
68 } else {
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(),
89 SWP_NOREDRAW);
90 if (direct_manipulation_helper_)
91 direct_manipulation_helper_->SetBounds(bounds_in_pixel);
94 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
95 if (host_) {
96 host_->OnLegacyWindowDestroyed();
97 host_ = NULL;
99 delete this;
102 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
103 : mouse_tracking_enabled_(false),
104 host_(NULL) {
105 RECT rect = {0};
106 Base::Create(parent, rect, L"Chrome Legacy Window",
107 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
108 WS_EX_TRANSPARENT);
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,
129 CHILDID_SELF);
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);
142 // static
143 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
144 HWND parent) {
145 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
146 parent, ui::WindowEventTarget::kWin32InputEventTarget));
149 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
150 WPARAM w_param,
151 LPARAM l_param) {
152 return 1;
155 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
156 WPARAM w_param,
157 LPARAM l_param) {
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());
174 if (!rwhi)
175 return static_cast<LRESULT>(0L);
177 BrowserAccessibilityManagerWin* manager =
178 static_cast<BrowserAccessibilityManagerWin*>(
179 rwhi->GetRootBrowserAccessibilityManager());
180 if (!manager)
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,
198 WPARAM w_param,
199 LPARAM l_param,
200 BOOL& handled) {
201 LRESULT ret = 0;
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;
208 return ret;
211 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
212 WPARAM w_param,
213 LPARAM l_param,
214 BOOL& handled) {
215 if (message == WM_MOUSEMOVE) {
216 if (!mouse_tracking_enabled_) {
217 mouse_tracking_enabled_ = true;
218 TRACKMOUSEEVENT tme;
219 tme.cbSize = sizeof(tme);
220 tme.dwFlags = TME_LEAVE;
221 tme.hwndTrack = hwnd();
222 tme.dwHoverTime = 0;
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
228 // coordinates.
229 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
230 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
231 POINT mouse_coords;
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);
238 LRESULT ret = 0;
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.
249 if (!handled &&
250 (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
251 ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
252 handled = TRUE;
256 if (direct_manipulation_helper_ &&
257 (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL)) {
258 direct_manipulation_helper_->HandleMouseWheel(hwnd(), message, w_param,
259 l_param);
261 return ret;
264 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
265 WPARAM w_param,
266 LPARAM l_param) {
267 mouse_tracking_enabled_ = false;
268 LRESULT ret = 0;
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.
272 POINT cursor_pos;
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);
281 return ret;
284 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
285 WPARAM w_param,
286 LPARAM l_param) {
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
303 // set.
304 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
305 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
306 return MA_NOACTIVATE;
308 return MA_ACTIVATE;
311 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
312 WPARAM w_param,
313 LPARAM l_param) {
314 LRESULT ret = 0;
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);
321 return ret;
324 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
325 WPARAM w_param,
326 LPARAM l_param) {
327 LRESULT ret = 0;
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);
334 return ret;
337 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
338 WPARAM w_param,
339 LPARAM l_param) {
340 if (GetWindowEventTarget(GetParent())) {
341 bool msg_handled = false;
342 LRESULT hit_test = GetWindowEventTarget(
343 GetParent())->HandleNcHitTestMessage(message, w_param, l_param,
344 &msg_handled);
345 // If the parent returns HTNOWHERE which can happen for popup windows, etc
346 // we return HTCLIENT.
347 if (hit_test == HTNOWHERE)
348 hit_test = HTCLIENT;
349 return hit_test;
351 return HTNOWHERE;
354 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
355 WPARAM w_param,
356 LPARAM l_param) {
357 return 0;
360 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
361 WPARAM w_param,
362 LPARAM l_param) {
363 PAINTSTRUCT ps = {0};
364 ::BeginPaint(hwnd(), &ps);
365 ::EndPaint(hwnd(), &ps);
366 return 0;
369 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
370 WPARAM w_param,
371 LPARAM l_param) {
372 return 0;
375 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
376 WPARAM w_param,
377 LPARAM l_param) {
378 // Prevent scrollbars, etc from drawing.
379 return 0;
382 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
383 WPARAM w_param,
384 LPARAM l_param) {
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
391 // work.
392 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
393 ::SetWindowLong(hwnd(), GWL_STYLE,
394 current_style | WS_VSCROLL | WS_HSCROLL);
395 return 0;
398 LRESULT LegacyRenderWidgetHostHWND::OnWindowPosChanged(UINT message,
399 WPARAM w_param,
400 LPARAM l_param) {
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);
410 return 0;
413 } // namespace content