• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
6 
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"
20 
21 namespace content {
22 
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;
27 
~LegacyRenderWidgetHostHWND()28 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
29   ::DestroyWindow(hwnd());
30 }
31 
32 // static
Create(HWND parent)33 scoped_ptr<LegacyRenderWidgetHostHWND> LegacyRenderWidgetHostHWND::Create(
34     HWND parent) {
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>();
42 
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>();
49 
50   legacy_window_instance->Init();
51   return legacy_window_instance.Pass();
52 }
53 
UpdateParent(HWND parent)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);
61   } else {
62     ::EnableWindow(hwnd(), TRUE);
63   }
64 }
65 
GetParent()66 HWND LegacyRenderWidgetHostHWND::GetParent() {
67   return ::GetParent(hwnd());
68 }
69 
OnManagerDeleted()70 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
71   manager_ = NULL;
72 }
73 
Show()74 void LegacyRenderWidgetHostHWND::Show() {
75   ::ShowWindow(hwnd(), SW_SHOW);
76 }
77 
Hide()78 void LegacyRenderWidgetHostHWND::Hide() {
79   ::ShowWindow(hwnd(), SW_HIDE);
80 }
81 
SetBounds(const gfx::Rect & bounds)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);
86 }
87 
OnFinalMessage(HWND hwnd)88 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
89   if (manager_)
90     manager_->OnAccessibleHwndDeleted();
91 }
92 
LegacyRenderWidgetHostHWND(HWND parent)93 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
94     : manager_(NULL),
95       mouse_tracking_enabled_(false) {
96   RECT rect = {0};
97   Base::Create(parent, rect, L"Chrome Legacy Window",
98                WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
99                WS_EX_TRANSPARENT);
100 }
101 
Init()102 bool LegacyRenderWidgetHostHWND::Init() {
103   if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
104       ui::AreTouchEventsEnabled())
105     RegisterTouchWindow(hwnd(), TWF_WANTPALM);
106 
107   HRESULT hr = ::CreateStdAccessibleObject(
108       hwnd(), OBJID_WINDOW, IID_IAccessible,
109       reinterpret_cast<void **>(window_accessible_.Receive()));
110   DCHECK(SUCCEEDED(hr));
111 
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,
116                    CHILDID_SELF);
117   }
118 
119   return !!SUCCEEDED(hr);
120 }
121 
122 // static
GetWindowEventTarget(HWND parent)123 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
124     HWND parent) {
125   return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
126       parent, ui::WindowEventTarget::kWin32InputEventTarget));
127 }
128 
OnEraseBkGnd(UINT message,WPARAM w_param,LPARAM l_param)129 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
130                                                  WPARAM w_param,
131                                                  LPARAM l_param) {
132   return 1;
133 }
134 
OnGetObject(UINT message,WPARAM w_param,LPARAM l_param)135 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
136                                                 WPARAM w_param,
137                                                 LPARAM l_param) {
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));
141 
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);
147   }
148 
149   if (OBJID_CLIENT != obj_id || !manager_)
150     return static_cast<LRESULT>(0L);
151 
152   base::win::ScopedComPtr<IAccessible> root(
153       manager_->GetRoot()->ToBrowserAccessibilityWin());
154   return LresultFromObject(IID_IAccessible, w_param,
155       static_cast<IAccessible*>(root.Detach()));
156 }
157 
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.
OnKeyboardRange(UINT message,WPARAM w_param,LPARAM l_param,BOOL & handled)166 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
167                                                     WPARAM w_param,
168                                                     LPARAM l_param,
169                                                     BOOL& handled) {
170   if (GetWindowEventTarget(GetParent())) {
171     return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
172         message, w_param, l_param);
173   }
174   return 0;
175 }
176 
OnMouseRange(UINT message,WPARAM w_param,LPARAM l_param,BOOL & handled)177 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
178                                                  WPARAM w_param,
179                                                  LPARAM l_param,
180                                                  BOOL& handled) {
181   if (message == WM_MOUSEMOVE) {
182     if (!mouse_tracking_enabled_) {
183       mouse_tracking_enabled_ = true;
184       TRACKMOUSEEVENT tme;
185       tme.cbSize = sizeof(tme);
186       tme.dwFlags = TME_LEAVE;
187       tme.hwndTrack = hwnd();
188       tme.dwHoverTime = 0;
189       TrackMouseEvent(&tme);
190     }
191   }
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
194   // coordinates.
195   if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
196       (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
197     POINT mouse_coords;
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);
202   }
203   if (GetWindowEventTarget(GetParent())) {
204     return GetWindowEventTarget(GetParent())->HandleMouseMessage(
205         message, w_param, l_param);
206   }
207   return 0;
208 }
209 
OnMouseLeave(UINT message,WPARAM w_param,LPARAM l_param)210 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
211                                                  WPARAM w_param,
212                                                  LPARAM l_param) {
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.
217     POINT cursor_pos;
218     ::GetCursorPos(&cursor_pos);
219     if (::WindowFromPoint(cursor_pos) != GetParent()) {
220       return GetWindowEventTarget(GetParent())->HandleMouseMessage(
221           message, w_param, l_param);
222     }
223   }
224   return 0;
225 }
226 
OnMouseActivate(UINT message,WPARAM w_param,LPARAM l_param)227 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
228                                                     WPARAM w_param,
229                                                     LPARAM l_param) {
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
246   // set.
247   if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
248     ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
249     return MA_NOACTIVATE;
250   }
251   return MA_ACTIVATE;
252 }
253 
OnTouch(UINT message,WPARAM w_param,LPARAM l_param)254 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
255                                             WPARAM w_param,
256                                             LPARAM l_param) {
257   if (GetWindowEventTarget(GetParent())) {
258     return GetWindowEventTarget(GetParent())->HandleTouchMessage(
259         message, w_param, l_param);
260   }
261   return 0;
262 }
263 
OnScroll(UINT message,WPARAM w_param,LPARAM l_param)264 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
265                                              WPARAM w_param,
266                                              LPARAM l_param) {
267   if (GetWindowEventTarget(GetParent())) {
268     return GetWindowEventTarget(GetParent())->HandleScrollMessage(
269         message, w_param, l_param);
270   }
271   return 0;
272 }
273 
OnNCHitTest(UINT message,WPARAM w_param,LPARAM l_param)274 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
275                                                 WPARAM w_param,
276                                                 LPARAM l_param) {
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)
283       hit_test = HTCLIENT;
284     return hit_test;
285   }
286   return HTNOWHERE;
287 }
288 
OnNCPaint(UINT message,WPARAM w_param,LPARAM l_param)289 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
290                                               WPARAM w_param,
291                                               LPARAM l_param) {
292   return 0;
293 }
294 
OnPaint(UINT message,WPARAM w_param,LPARAM l_param)295 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
296                                             WPARAM w_param,
297                                             LPARAM l_param) {
298   PAINTSTRUCT ps = {0};
299   ::BeginPaint(hwnd(), &ps);
300   ::EndPaint(hwnd(), &ps);
301   return 0;
302 }
303 
OnSetCursor(UINT message,WPARAM w_param,LPARAM l_param)304 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
305                                                 WPARAM w_param,
306                                                 LPARAM l_param) {
307   return 0;
308 }
309 
OnNCCalcSize(UINT message,WPARAM w_param,LPARAM l_param)310 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
311                                                  WPARAM w_param,
312                                                  LPARAM l_param) {
313   // Prevent scrollbars, etc from drawing.
314   return 0;
315 }
316 
OnSize(UINT message,WPARAM w_param,LPARAM l_param)317 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
318                                            WPARAM w_param,
319                                            LPARAM l_param) {
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
326   // work.
327   long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
328   ::SetWindowLong(hwnd(), GWL_STYLE,
329                   current_style | WS_VSCROLL | WS_HSCROLL);
330   return 0;
331 }
332 
333 }  // namespace content
334