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/browser/browser_accessibility_state.h"
13 #include "content/public/common/content_switches.h"
14 #include "ui/base/touch/touch_enabled.h"
15 #include "ui/base/view_prop.h"
16 #include "ui/base/win/internal_constants.h"
17 #include "ui/base/win/window_event_target.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/gfx/win/dpi.h"
23 // A custom MSAA object id used to determine if a screen reader or some
24 // other client is listening on MSAA events - if so, we enable full web
25 // accessibility support.
26 const int kIdScreenReaderHoneyPot = 1;
28 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
29 ::DestroyWindow(hwnd());
33 scoped_ptr<LegacyRenderWidgetHostHWND> LegacyRenderWidgetHostHWND::Create(
35 // content_unittests passes in the desktop window as the parent. We allow
36 // the LegacyRenderWidgetHostHWND instance to be created in this case for
37 // these tests to pass.
38 if (CommandLine::ForCurrentProcess()->HasSwitch(
39 switches::kDisableLegacyIntermediateWindow) ||
40 (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
41 return scoped_ptr<LegacyRenderWidgetHostHWND>();
43 scoped_ptr<LegacyRenderWidgetHostHWND> legacy_window_instance;
44 legacy_window_instance.reset(new LegacyRenderWidgetHostHWND(parent));
45 // If we failed to create the child, or if the switch to disable the legacy
46 // window is passed in, then return NULL.
47 if (!::IsWindow(legacy_window_instance->hwnd()))
48 return scoped_ptr<LegacyRenderWidgetHostHWND>();
50 legacy_window_instance->Init();
51 return legacy_window_instance.Pass();
54 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
55 ::SetParent(hwnd(), parent);
56 // If the new parent is the desktop Window, then we disable the child window
57 // to ensure that it does not receive any input events. It should not because
58 // of WS_EX_TRANSPARENT. This is only for safety.
59 if (parent == ::GetDesktopWindow()) {
60 ::EnableWindow(hwnd(), FALSE);
62 ::EnableWindow(hwnd(), TRUE);
66 HWND LegacyRenderWidgetHostHWND::GetParent() {
67 return ::GetParent(hwnd());
70 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
74 void LegacyRenderWidgetHostHWND::Show() {
75 ::ShowWindow(hwnd(), SW_SHOW);
78 void LegacyRenderWidgetHostHWND::Hide() {
79 ::ShowWindow(hwnd(), SW_HIDE);
82 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
83 gfx::Rect bounds_in_pixel = gfx::win::DIPToScreenRect(bounds);
84 ::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(),
85 bounds_in_pixel.width(), bounds_in_pixel.height(), 0);
88 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
90 manager_->OnAccessibleHwndDeleted();
93 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
95 mouse_tracking_enabled_(false) {
97 Base::Create(parent, rect, L"Chrome Legacy Window",
98 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
102 bool LegacyRenderWidgetHostHWND::Init() {
103 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
104 ui::AreTouchEventsEnabled())
105 RegisterTouchWindow(hwnd(), TWF_WANTPALM);
107 HRESULT hr = ::CreateStdAccessibleObject(
108 hwnd(), OBJID_WINDOW, IID_IAccessible,
109 reinterpret_cast<void **>(window_accessible_.Receive()));
110 DCHECK(SUCCEEDED(hr));
112 if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
113 // Attempt to detect screen readers or other clients who want full
114 // accessibility support, by seeing if they respond to this event.
115 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
119 return !!SUCCEEDED(hr);
123 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
125 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
126 parent, ui::WindowEventTarget::kWin32InputEventTarget));
129 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
135 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
138 // Only the lower 32 bits of l_param are valid when checking the object id
139 // because it sometimes gets sign-extended incorrectly (but not always).
140 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
142 if (kIdScreenReaderHoneyPot == obj_id) {
143 // When an MSAA client has responded to our fake event on this id,
144 // enable screen reader support.
145 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
146 return static_cast<LRESULT>(0L);
149 if (OBJID_CLIENT != obj_id || !manager_)
150 return static_cast<LRESULT>(0L);
152 base::win::ScopedComPtr<IAccessible> root(
153 manager_->GetRoot()->ToBrowserAccessibilityWin());
154 return LresultFromObject(IID_IAccessible, w_param,
155 static_cast<IAccessible*>(root.Detach()));
158 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
159 // While this works, this has the side effect of converting input messages into
160 // sent messages which changes their priority and could technically result
161 // in these messages starving other messages in the queue. Additionally
162 // keyboard/mouse hooks would not see these messages. The alternative approach
163 // is to set and release capture as needed on the parent to ensure that it
164 // receives all mouse events. However that was shelved due to possible issues
165 // with capture changes.
166 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
170 if (GetWindowEventTarget(GetParent())) {
171 return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
172 message, w_param, l_param);
177 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
181 if (message == WM_MOUSEMOVE) {
182 if (!mouse_tracking_enabled_) {
183 mouse_tracking_enabled_ = true;
185 tme.cbSize = sizeof(tme);
186 tme.dwFlags = TME_LEAVE;
187 tme.hwndTrack = hwnd();
189 TrackMouseEvent(&tme);
192 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
193 // in screen coordinates. We should not be converting them to parent
195 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
196 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
198 mouse_coords.x = GET_X_LPARAM(l_param);
199 mouse_coords.y = GET_Y_LPARAM(l_param);
200 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
201 l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
203 if (GetWindowEventTarget(GetParent())) {
204 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
205 message, w_param, l_param);
210 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
213 mouse_tracking_enabled_ = false;
214 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
215 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
216 // has moved outside the bounds of the parent.
218 ::GetCursorPos(&cursor_pos);
219 if (::WindowFromPoint(cursor_pos) != GetParent()) {
220 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
221 message, w_param, l_param);
227 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
230 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
231 // message going all the way to the parent which then messes up state
232 // related to focused views, etc. This is because it treats this as if
233 // it lost activation.
234 // Our dummy window should not interfere with focus and activation in
235 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
236 // is preserved. The only exception is if the parent was created with the
237 // WS_EX_NOACTIVATE style.
238 if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
239 return MA_NOACTIVATE;
240 // On Windows, if we select the menu item by touch and if the window at the
241 // location is another window on the same thread, that window gets a
242 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
243 // correct. We workaround this by setting a property on the window at the
244 // current cursor location. We check for this property in our
245 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
247 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
248 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
249 return MA_NOACTIVATE;
254 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
257 if (GetWindowEventTarget(GetParent())) {
258 return GetWindowEventTarget(GetParent())->HandleTouchMessage(
259 message, w_param, l_param);
264 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
267 if (GetWindowEventTarget(GetParent())) {
268 return GetWindowEventTarget(GetParent())->HandleScrollMessage(
269 message, w_param, l_param);
274 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
277 if (GetWindowEventTarget(GetParent())) {
278 LRESULT hit_test = GetWindowEventTarget(
279 GetParent())->HandleNcHitTestMessage(message, w_param, l_param);
280 // If the parent returns HTNOWHERE which can happen for popup windows, etc
281 // we return HTCLIENT.
282 if (hit_test == HTNOWHERE)
289 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
295 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
298 PAINTSTRUCT ps = {0};
299 ::BeginPaint(hwnd(), &ps);
300 ::EndPaint(hwnd(), &ps);
304 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
310 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
313 // Prevent scrollbars, etc from drawing.
317 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
320 // Certain trackpad drivers on Windows have bugs where in they don't generate
321 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
322 // unless there is an entry for Chrome with the class name of the Window.
323 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
324 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
325 // We add these styles to ensure that trackpad/trackpoint scrolling
327 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
328 ::SetWindowLong(hwnd(), GWL_STYLE,
329 current_style | WS_VSCROLL | WS_HSCROLL);
333 } // namespace content