Upstream version 7.36.149.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/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
20 namespace content {
21
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;
26
27 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
28   ::DestroyWindow(hwnd());
29 }
30
31 // static
32 scoped_ptr<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 (CommandLine::ForCurrentProcess()->HasSwitch(
38           switches::kDisableLegacyIntermediateWindow) ||
39       (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
40     return scoped_ptr<LegacyRenderWidgetHostHWND>();
41
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>();
48
49   legacy_window_instance->Init();
50   return legacy_window_instance.Pass();
51 }
52
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);
60   } else {
61     ::EnableWindow(hwnd(), TRUE);
62   }
63 }
64
65 HWND LegacyRenderWidgetHostHWND::GetParent() {
66   return ::GetParent(hwnd());
67 }
68
69 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
70   manager_ = NULL;
71 }
72
73 void LegacyRenderWidgetHostHWND::Show() {
74   ::ShowWindow(hwnd(), SW_SHOW);
75 }
76
77 void LegacyRenderWidgetHostHWND::Hide() {
78   ::ShowWindow(hwnd(), SW_HIDE);
79 }
80
81 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
82   ::SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(),
83                   bounds.height(), 0);
84 }
85
86 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
87   if (manager_)
88     manager_->OnAccessibleHwndDeleted();
89 }
90
91 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
92     : manager_(NULL),
93       mouse_tracking_enabled_(false) {
94   RECT rect = {0};
95   Base::Create(parent, rect, L"Chrome Legacy Window",
96                WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
97                WS_EX_TRANSPARENT);
98 }
99
100 bool LegacyRenderWidgetHostHWND::Init() {
101   if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
102       ui::AreTouchEventsEnabled())
103     RegisterTouchWindow(hwnd(), TWF_WANTPALM);
104
105   HRESULT hr = ::CreateStdAccessibleObject(
106       hwnd(), OBJID_WINDOW, IID_IAccessible,
107       reinterpret_cast<void **>(window_accessible_.Receive()));
108   DCHECK(SUCCEEDED(hr));
109
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,
114                    CHILDID_SELF);
115   }
116
117   return !!SUCCEEDED(hr);
118 }
119
120 // static
121 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
122     HWND parent) {
123   return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
124       parent, ui::WindowEventTarget::kWin32InputEventTarget));
125 }
126
127 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
128                                                  WPARAM w_param,
129                                                  LPARAM l_param) {
130   return 1;
131 }
132
133 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
134                                                 WPARAM w_param,
135                                                 LPARAM l_param) {
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);
141   }
142
143   if (OBJID_CLIENT != l_param || !manager_)
144     return static_cast<LRESULT>(0L);
145
146   base::win::ScopedComPtr<IAccessible> root(
147       manager_->GetRoot()->ToBrowserAccessibilityWin());
148   return LresultFromObject(IID_IAccessible, w_param,
149       static_cast<IAccessible*>(root.Detach()));
150 }
151
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,
161                                                     WPARAM w_param,
162                                                     LPARAM l_param,
163                                                     BOOL& handled) {
164   if (GetWindowEventTarget(GetParent())) {
165     return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
166         message, w_param, l_param);
167   }
168   return 0;
169 }
170
171 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
172                                                  WPARAM w_param,
173                                                  LPARAM l_param,
174                                                  BOOL& handled) {
175   if (message == WM_MOUSEMOVE) {
176     if (!mouse_tracking_enabled_) {
177       mouse_tracking_enabled_ = true;
178       TRACKMOUSEEVENT tme;
179       tme.cbSize = sizeof(tme);
180       tme.dwFlags = TME_LEAVE;
181       tme.hwndTrack = hwnd();
182       tme.dwHoverTime = 0;
183       TrackMouseEvent(&tme);
184     }
185   }
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
188   // coordinates.
189   if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
190       (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
191     POINT mouse_coords;
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);
196   }
197   if (GetWindowEventTarget(GetParent())) {
198     return GetWindowEventTarget(GetParent())->HandleMouseMessage(
199         message, w_param, l_param);
200   }
201   return 0;
202 }
203
204 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
205                                                  WPARAM w_param,
206                                                  LPARAM l_param) {
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.
211     POINT cursor_pos;
212     ::GetCursorPos(&cursor_pos);
213     if (::WindowFromPoint(cursor_pos) != GetParent()) {
214       return GetWindowEventTarget(GetParent())->HandleMouseMessage(
215           message, w_param, l_param);
216     }
217   }
218   return 0;
219 }
220
221 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
222                                                     WPARAM w_param,
223                                                     LPARAM l_param) {
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
240   // set.
241   if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
242     ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
243     return MA_NOACTIVATE;
244   }
245   return MA_ACTIVATE;
246 }
247
248 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
249                                             WPARAM w_param,
250                                             LPARAM l_param) {
251   if (GetWindowEventTarget(GetParent())) {
252     return GetWindowEventTarget(GetParent())->HandleTouchMessage(
253         message, w_param, l_param);
254   }
255   return 0;
256 }
257
258 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
259                                              WPARAM w_param,
260                                              LPARAM l_param) {
261   if (GetWindowEventTarget(GetParent())) {
262     return GetWindowEventTarget(GetParent())->HandleScrollMessage(
263         message, w_param, l_param);
264   }
265   return 0;
266 }
267
268 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
269                                                 WPARAM w_param,
270                                                 LPARAM l_param) {
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)
277       hit_test = HTCLIENT;
278     return hit_test;
279   }
280   return HTNOWHERE;
281 }
282
283 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
284                                               WPARAM w_param,
285                                               LPARAM l_param) {
286   return 0;
287 }
288
289 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
290                                             WPARAM w_param,
291                                             LPARAM l_param) {
292   PAINTSTRUCT ps = {0};
293   ::BeginPaint(hwnd(), &ps);
294   ::EndPaint(hwnd(), &ps);
295   return 0;
296 }
297
298 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
299                                                 WPARAM w_param,
300                                                 LPARAM l_param) {
301   return 0;
302 }
303
304 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
305                                                  WPARAM w_param,
306                                                  LPARAM l_param) {
307   // Prevent scrollbars, etc from drawing.
308   return 0;
309 }
310
311 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
312                                            WPARAM w_param,
313                                            LPARAM l_param) {
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
320   // work.
321   long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
322   ::SetWindowLong(hwnd(), GWL_STYLE,
323                   current_style | WS_VSCROLL | WS_HSCROLL);
324   return 0;
325 }
326
327 }  // namespace content