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"
22 // A custom MSAA object id used to determine if a screen reader or some
23 // other client is listening on MSAA events - if so, we enable full web
24 // accessibility support.
25 const int kIdScreenReaderHoneyPot = 1;
27 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
28 ::DestroyWindow(hwnd());
32 scoped_ptr<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 (CommandLine::ForCurrentProcess()->HasSwitch(
38 switches::kDisableLegacyIntermediateWindow) ||
39 (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
40 return scoped_ptr<LegacyRenderWidgetHostHWND>();
42 scoped_ptr<LegacyRenderWidgetHostHWND> legacy_window_instance;
43 legacy_window_instance.reset(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 return scoped_ptr<LegacyRenderWidgetHostHWND>();
49 legacy_window_instance->Init();
50 return legacy_window_instance.Pass();
53 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
54 ::SetParent(hwnd(), parent);
55 // If the new parent is the desktop Window, then we disable the child window
56 // to ensure that it does not receive any input events. It should not because
57 // of WS_EX_TRANSPARENT. This is only for safety.
58 if (parent == ::GetDesktopWindow()) {
59 ::EnableWindow(hwnd(), FALSE);
61 ::EnableWindow(hwnd(), TRUE);
65 HWND LegacyRenderWidgetHostHWND::GetParent() {
66 return ::GetParent(hwnd());
69 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
73 void LegacyRenderWidgetHostHWND::Show() {
74 ::ShowWindow(hwnd(), SW_SHOW);
77 void LegacyRenderWidgetHostHWND::Hide() {
78 ::ShowWindow(hwnd(), SW_HIDE);
81 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
82 ::SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(),
86 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
88 manager_->OnAccessibleHwndDeleted();
91 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
93 mouse_tracking_enabled_(false) {
95 Base::Create(parent, rect, L"Chrome Legacy Window",
96 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
100 bool LegacyRenderWidgetHostHWND::Init() {
101 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
102 ui::AreTouchEventsEnabled())
103 RegisterTouchWindow(hwnd(), TWF_WANTPALM);
105 HRESULT hr = ::CreateStdAccessibleObject(
106 hwnd(), OBJID_WINDOW, IID_IAccessible,
107 reinterpret_cast<void **>(window_accessible_.Receive()));
108 DCHECK(SUCCEEDED(hr));
110 if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
111 // Attempt to detect screen readers or other clients who want full
112 // accessibility support, by seeing if they respond to this event.
113 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
117 return !!SUCCEEDED(hr);
121 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
123 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
124 parent, ui::WindowEventTarget::kWin32InputEventTarget));
127 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
133 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
136 if (kIdScreenReaderHoneyPot == l_param) {
137 // When an MSAA client has responded to our fake event on this id,
138 // enable screen reader support.
139 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
140 return static_cast<LRESULT>(0L);
143 if (OBJID_CLIENT != l_param || !manager_)
144 return static_cast<LRESULT>(0L);
146 base::win::ScopedComPtr<IAccessible> root(
147 manager_->GetRoot()->ToBrowserAccessibilityWin());
148 return LresultFromObject(IID_IAccessible, w_param,
149 static_cast<IAccessible*>(root.Detach()));
152 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
153 // While this works, this has the side effect of converting input messages into
154 // sent messages which changes their priority and could technically result
155 // in these messages starving other messages in the queue. Additionally
156 // keyboard/mouse hooks would not see these messages. The alternative approach
157 // is to set and release capture as needed on the parent to ensure that it
158 // receives all mouse events. However that was shelved due to possible issues
159 // with capture changes.
160 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
164 if (GetWindowEventTarget(GetParent())) {
165 return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
166 message, w_param, l_param);
171 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
175 if (message == WM_MOUSEMOVE) {
176 if (!mouse_tracking_enabled_) {
177 mouse_tracking_enabled_ = true;
179 tme.cbSize = sizeof(tme);
180 tme.dwFlags = TME_LEAVE;
181 tme.hwndTrack = hwnd();
183 TrackMouseEvent(&tme);
186 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
187 // in screen coordinates. We should not be converting them to parent
189 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
190 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
192 mouse_coords.x = GET_X_LPARAM(l_param);
193 mouse_coords.y = GET_Y_LPARAM(l_param);
194 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
195 l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
197 if (GetWindowEventTarget(GetParent())) {
198 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
199 message, w_param, l_param);
204 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
207 mouse_tracking_enabled_ = false;
208 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
209 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
210 // has moved outside the bounds of the parent.
212 ::GetCursorPos(&cursor_pos);
213 if (::WindowFromPoint(cursor_pos) != GetParent()) {
214 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
215 message, w_param, l_param);
221 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
224 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
225 // message going all the way to the parent which then messes up state
226 // related to focused views, etc. This is because it treats this as if
227 // it lost activation.
228 // Our dummy window should not interfere with focus and activation in
229 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
230 // is preserved. The only exception is if the parent was created with the
231 // WS_EX_NOACTIVATE style.
232 if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
233 return MA_NOACTIVATE;
234 // On Windows, if we select the menu item by touch and if the window at the
235 // location is another window on the same thread, that window gets a
236 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
237 // correct. We workaround this by setting a property on the window at the
238 // current cursor location. We check for this property in our
239 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
241 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
242 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
243 return MA_NOACTIVATE;
248 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
251 if (GetWindowEventTarget(GetParent())) {
252 return GetWindowEventTarget(GetParent())->HandleTouchMessage(
253 message, w_param, l_param);
258 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
261 if (GetWindowEventTarget(GetParent())) {
262 return GetWindowEventTarget(GetParent())->HandleScrollMessage(
263 message, w_param, l_param);
268 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
271 if (GetWindowEventTarget(GetParent())) {
272 LRESULT hit_test = GetWindowEventTarget(
273 GetParent())->HandleNcHitTestMessage(message, w_param, l_param);
274 // If the parent returns HTNOWHERE which can happen for popup windows, etc
275 // we return HTCLIENT.
276 if (hit_test == HTNOWHERE)
283 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
289 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
292 PAINTSTRUCT ps = {0};
293 ::BeginPaint(hwnd(), &ps);
294 ::EndPaint(hwnd(), &ps);
298 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
304 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
307 // Prevent scrollbars, etc from drawing.
311 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
314 // Certain trackpad drivers on Windows have bugs where in they don't generate
315 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
316 // unless there is an entry for Chrome with the class name of the Window.
317 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
318 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
319 // We add these styles to ensure that trackpad/trackpoint scrolling
321 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
322 ::SetWindowLong(hwnd(), GWL_STYLE,
323 current_style | WS_VSCROLL | WS_HSCROLL);
327 } // namespace content