Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / legacy_render_widget_host_win.cc
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/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/dpi.h"
22
23 namespace content {
24
25 // A custom MSAA object id used to determine if a screen reader or some
26 // other client is listening on MSAA events - if so, we enable full web
27 // accessibility support.
28 const int kIdScreenReaderHoneyPot = 1;
29
30 // static
31 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
32     HWND parent) {
33   // content_unittests passes in the desktop window as the parent. We allow
34   // the LegacyRenderWidgetHostHWND instance to be created in this case for
35   // these tests to pass.
36   if (CommandLine::ForCurrentProcess()->HasSwitch(
37           switches::kDisableLegacyIntermediateWindow) ||
38       (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
39     return NULL;
40
41   LegacyRenderWidgetHostHWND* legacy_window_instance =
42       new LegacyRenderWidgetHostHWND(parent);
43   // If we failed to create the child, or if the switch to disable the legacy
44   // window is passed in, then return NULL.
45   if (!::IsWindow(legacy_window_instance->hwnd())) {
46     delete legacy_window_instance;
47     return NULL;
48   }
49   legacy_window_instance->Init();
50   return legacy_window_instance;
51 }
52
53 void LegacyRenderWidgetHostHWND::Destroy() {
54   if (::IsWindow(hwnd()))
55     ::DestroyWindow(hwnd());
56 }
57
58 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
59   ::SetParent(hwnd(), parent);
60   // If the new parent is the desktop Window, then we disable the child window
61   // to ensure that it does not receive any input events. It should not because
62   // of WS_EX_TRANSPARENT. This is only for safety.
63   if (parent == ::GetDesktopWindow()) {
64     ::EnableWindow(hwnd(), FALSE);
65   } else {
66     ::EnableWindow(hwnd(), TRUE);
67   }
68 }
69
70 HWND LegacyRenderWidgetHostHWND::GetParent() {
71   return ::GetParent(hwnd());
72 }
73
74 void LegacyRenderWidgetHostHWND::Show() {
75   ::ShowWindow(hwnd(), SW_SHOW);
76 }
77
78 void LegacyRenderWidgetHostHWND::Hide() {
79   ::ShowWindow(hwnd(), SW_HIDE);
80 }
81
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(),
86                  SWP_NOREDRAW);
87 }
88
89 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
90   if (host_) {
91     host_->OnLegacyWindowDestroyed();
92     host_ = NULL;
93   }
94   delete this;
95 }
96
97 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
98     : mouse_tracking_enabled_(false),
99       host_(NULL) {
100   RECT rect = {0};
101   Base::Create(parent, rect, L"Chrome Legacy Window",
102                WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
103                WS_EX_TRANSPARENT);
104 }
105
106 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
107   DCHECK(!::IsWindow(hwnd()));
108 }
109
110 bool LegacyRenderWidgetHostHWND::Init() {
111   if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
112       ui::AreTouchEventsEnabled())
113     RegisterTouchWindow(hwnd(), TWF_WANTPALM);
114
115   HRESULT hr = ::CreateStdAccessibleObject(
116       hwnd(), OBJID_WINDOW, IID_IAccessible,
117       reinterpret_cast<void **>(window_accessible_.Receive()));
118   DCHECK(SUCCEEDED(hr));
119
120   if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
121     // Attempt to detect screen readers or other clients who want full
122     // accessibility support, by seeing if they respond to this event.
123     NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
124                    CHILDID_SELF);
125   }
126
127   return !!SUCCEEDED(hr);
128 }
129
130 // static
131 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
132     HWND parent) {
133   return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
134       parent, ui::WindowEventTarget::kWin32InputEventTarget));
135 }
136
137 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
138                                                  WPARAM w_param,
139                                                  LPARAM l_param) {
140   return 1;
141 }
142
143 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
144                                                 WPARAM w_param,
145                                                 LPARAM l_param) {
146   // Only the lower 32 bits of l_param are valid when checking the object id
147   // because it sometimes gets sign-extended incorrectly (but not always).
148   DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
149
150   if (kIdScreenReaderHoneyPot == obj_id) {
151     // When an MSAA client has responded to our fake event on this id,
152     // enable screen reader support.
153     BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
154     return static_cast<LRESULT>(0L);
155   }
156
157   if (OBJID_CLIENT != obj_id || !host_)
158     return static_cast<LRESULT>(0L);
159
160   RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(
161       host_->GetRenderWidgetHost());
162   if (!rwhi)
163     return static_cast<LRESULT>(0L);
164
165   BrowserAccessibilityManagerWin* manager =
166       static_cast<BrowserAccessibilityManagerWin*>(
167           rwhi->GetRootBrowserAccessibilityManager());
168   if (!manager)
169     return static_cast<LRESULT>(0L);
170
171   base::win::ScopedComPtr<IAccessible> root(
172       manager->GetRoot()->ToBrowserAccessibilityWin());
173   return LresultFromObject(IID_IAccessible, w_param,
174       static_cast<IAccessible*>(root.Detach()));
175 }
176
177 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
178 // While this works, this has the side effect of converting input messages into
179 // sent messages which changes their priority and could technically result
180 // in these messages starving other messages in the queue. Additionally
181 // keyboard/mouse hooks would not see these messages. The alternative approach
182 // is to set and release capture as needed on the parent to ensure that it
183 // receives all mouse events. However that was shelved due to possible issues
184 // with capture changes.
185 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
186                                                     WPARAM w_param,
187                                                     LPARAM l_param,
188                                                     BOOL& handled) {
189   LRESULT ret = 0;
190   if (GetWindowEventTarget(GetParent())) {
191     bool msg_handled = false;
192     ret = GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
193         message, w_param, l_param, &msg_handled);
194     handled = msg_handled;
195   }
196   return ret;
197 }
198
199 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
200                                                  WPARAM w_param,
201                                                  LPARAM l_param,
202                                                  BOOL& handled) {
203   if (message == WM_MOUSEMOVE) {
204     if (!mouse_tracking_enabled_) {
205       mouse_tracking_enabled_ = true;
206       TRACKMOUSEEVENT tme;
207       tme.cbSize = sizeof(tme);
208       tme.dwFlags = TME_LEAVE;
209       tme.hwndTrack = hwnd();
210       tme.dwHoverTime = 0;
211       TrackMouseEvent(&tme);
212     }
213   }
214   // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
215   // in screen coordinates. We should not be converting them to parent
216   // coordinates.
217   if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
218       (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
219     POINT mouse_coords;
220     mouse_coords.x = GET_X_LPARAM(l_param);
221     mouse_coords.y = GET_Y_LPARAM(l_param);
222     ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
223     l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
224   }
225
226   LRESULT ret = 0;
227
228   if (GetWindowEventTarget(GetParent())) {
229     bool msg_handled = false;
230     ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
231         message, w_param, l_param, &msg_handled);
232     handled = msg_handled;
233     // If the parent did not handle non client mouse messages, we call
234     // DefWindowProc on the message with the parent window handle. This
235     // ensures that WM_SYSCOMMAND is generated for the parent and we are
236     // out of the picture.
237     if (!handled &&
238          (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
239       ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
240       handled = TRUE;
241     }
242   }
243   return ret;
244 }
245
246 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
247                                                  WPARAM w_param,
248                                                  LPARAM l_param) {
249   mouse_tracking_enabled_ = false;
250   LRESULT ret = 0;
251   if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
252     // We should send a WM_MOUSELEAVE to the parent window only if the mouse
253     // has moved outside the bounds of the parent.
254     POINT cursor_pos;
255     ::GetCursorPos(&cursor_pos);
256     if (::WindowFromPoint(cursor_pos) != GetParent()) {
257       bool msg_handled = false;
258       ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
259           message, w_param, l_param, &msg_handled);
260       SetMsgHandled(msg_handled);
261     }
262   }
263   return ret;
264 }
265
266 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
267                                                     WPARAM w_param,
268                                                     LPARAM l_param) {
269   // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
270   // message going all the way to the parent which then messes up state
271   // related to focused views, etc. This is because it treats this as if
272   // it lost activation.
273   // Our dummy window should not interfere with focus and activation in
274   // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
275   // is preserved. The only exception is if the parent was created with the
276   // WS_EX_NOACTIVATE style.
277   if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
278     return MA_NOACTIVATE;
279   // On Windows, if we select the menu item by touch and if the window at the
280   // location is another window on the same thread, that window gets a
281   // WM_MOUSEACTIVATE message and ends up activating itself, which is not
282   // correct. We workaround this by setting a property on the window at the
283   // current cursor location. We check for this property in our
284   // WM_MOUSEACTIVATE handler and don't activate the window if the property is
285   // set.
286   if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
287     ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
288     return MA_NOACTIVATE;
289   }
290   return MA_ACTIVATE;
291 }
292
293 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
294                                             WPARAM w_param,
295                                             LPARAM l_param) {
296   LRESULT ret = 0;
297   if (GetWindowEventTarget(GetParent())) {
298     bool msg_handled = false;
299     ret = GetWindowEventTarget(GetParent())->HandleTouchMessage(
300         message, w_param, l_param, &msg_handled);
301     SetMsgHandled(msg_handled);
302   }
303   return ret;
304 }
305
306 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
307                                              WPARAM w_param,
308                                              LPARAM l_param) {
309   LRESULT ret = 0;
310   if (GetWindowEventTarget(GetParent())) {
311     bool msg_handled = false;
312     ret = GetWindowEventTarget(GetParent())->HandleScrollMessage(
313         message, w_param, l_param, &msg_handled);
314     SetMsgHandled(msg_handled);
315   }
316   return ret;
317 }
318
319 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
320                                                 WPARAM w_param,
321                                                 LPARAM l_param) {
322   if (GetWindowEventTarget(GetParent())) {
323     bool msg_handled = false;
324     LRESULT hit_test = GetWindowEventTarget(
325         GetParent())->HandleNcHitTestMessage(message, w_param, l_param,
326                                              &msg_handled);
327     // If the parent returns HTNOWHERE which can happen for popup windows, etc
328     // we return HTCLIENT.
329     if (hit_test == HTNOWHERE)
330       hit_test = HTCLIENT;
331     return hit_test;
332   }
333   return HTNOWHERE;
334 }
335
336 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
337                                               WPARAM w_param,
338                                               LPARAM l_param) {
339   return 0;
340 }
341
342 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
343                                             WPARAM w_param,
344                                             LPARAM l_param) {
345   PAINTSTRUCT ps = {0};
346   ::BeginPaint(hwnd(), &ps);
347   ::EndPaint(hwnd(), &ps);
348   return 0;
349 }
350
351 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
352                                                 WPARAM w_param,
353                                                 LPARAM l_param) {
354   return 0;
355 }
356
357 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
358                                                  WPARAM w_param,
359                                                  LPARAM l_param) {
360   // Prevent scrollbars, etc from drawing.
361   return 0;
362 }
363
364 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
365                                            WPARAM w_param,
366                                            LPARAM l_param) {
367   // Certain trackpad drivers on Windows have bugs where in they don't generate
368   // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
369   // unless there is an entry for Chrome with the class name of the Window.
370   // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
371   // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
372   // We add these styles to ensure that trackpad/trackpoint scrolling
373   // work.
374   long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
375   ::SetWindowLong(hwnd(), GWL_STYLE,
376                   current_style | WS_VSCROLL | WS_HSCROLL);
377   return 0;
378 }
379
380 }  // namespace content