Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / input / web_input_event_builders_win.cc
blob19e8fc617710be0e013ccb54ac325b041f8cd77e
1 // Copyright 2013 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/input/web_input_event_builders_win.h"
7 #include "base/logging.h"
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
10 using blink::WebInputEvent;
11 using blink::WebKeyboardEvent;
12 using blink::WebMouseEvent;
13 using blink::WebMouseWheelEvent;
15 namespace content {
17 static const unsigned long kDefaultScrollLinesPerWheelDelta = 3;
18 static const unsigned long kDefaultScrollCharsPerWheelDelta = 1;
20 static bool IsKeyDown(WPARAM wparam) {
21 return (GetKeyState(wparam) & 0x8000) != 0;
24 static int GetLocationModifier(WPARAM wparam, LPARAM lparam) {
25 int modifier = 0;
26 switch (wparam) {
27 case VK_RETURN:
28 if ((lparam >> 16) & KF_EXTENDED)
29 modifier = WebInputEvent::IsKeyPad;
30 break;
31 case VK_INSERT:
32 case VK_DELETE:
33 case VK_HOME:
34 case VK_END:
35 case VK_PRIOR:
36 case VK_NEXT:
37 case VK_UP:
38 case VK_DOWN:
39 case VK_LEFT:
40 case VK_RIGHT:
41 if (!((lparam >> 16) & KF_EXTENDED))
42 modifier = WebInputEvent::IsKeyPad;
43 break;
44 case VK_NUMLOCK:
45 case VK_NUMPAD0:
46 case VK_NUMPAD1:
47 case VK_NUMPAD2:
48 case VK_NUMPAD3:
49 case VK_NUMPAD4:
50 case VK_NUMPAD5:
51 case VK_NUMPAD6:
52 case VK_NUMPAD7:
53 case VK_NUMPAD8:
54 case VK_NUMPAD9:
55 case VK_DIVIDE:
56 case VK_MULTIPLY:
57 case VK_SUBTRACT:
58 case VK_ADD:
59 case VK_DECIMAL:
60 case VK_CLEAR:
61 modifier = WebInputEvent::IsKeyPad;
62 break;
63 case VK_SHIFT:
64 if (IsKeyDown(VK_LSHIFT))
65 modifier = WebInputEvent::IsLeft;
66 else if (IsKeyDown(VK_RSHIFT))
67 modifier = WebInputEvent::IsRight;
68 break;
69 case VK_CONTROL:
70 if (IsKeyDown(VK_LCONTROL))
71 modifier = WebInputEvent::IsLeft;
72 else if (IsKeyDown(VK_RCONTROL))
73 modifier = WebInputEvent::IsRight;
74 break;
75 case VK_MENU:
76 if (IsKeyDown(VK_LMENU))
77 modifier = WebInputEvent::IsLeft;
78 else if (IsKeyDown(VK_RMENU))
79 modifier = WebInputEvent::IsRight;
80 break;
81 case VK_LWIN:
82 modifier = WebInputEvent::IsLeft;
83 break;
84 case VK_RWIN:
85 modifier = WebInputEvent::IsRight;
86 break;
89 DCHECK(!modifier
90 || modifier == WebInputEvent::IsKeyPad
91 || modifier == WebInputEvent::IsLeft
92 || modifier == WebInputEvent::IsRight);
93 return modifier;
96 // Loads the state for toggle keys into the event.
97 static void SetToggleKeyState(WebInputEvent* event) {
98 // Low bit set from GetKeyState indicates "toggled".
99 if (::GetKeyState(VK_NUMLOCK) & 1)
100 event->modifiers |= WebInputEvent::NumLockOn;
101 if (::GetKeyState(VK_CAPITAL) & 1)
102 event->modifiers |= WebInputEvent::CapsLockOn;
105 WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd,
106 UINT message,
107 WPARAM wparam,
108 LPARAM lparam,
109 DWORD time_ms) {
110 WebKeyboardEvent result;
112 DCHECK(time_ms);
113 result.timeStampSeconds = time_ms / 1000.0;
115 result.windowsKeyCode = static_cast<int>(wparam);
116 // Record the scan code (along with other context bits) for this key event.
117 result.nativeKeyCode = static_cast<int>(lparam);
119 switch (message) {
120 case WM_SYSKEYDOWN:
121 result.isSystemKey = true;
122 case WM_KEYDOWN:
123 result.type = WebInputEvent::RawKeyDown;
124 break;
125 case WM_SYSKEYUP:
126 result.isSystemKey = true;
127 case WM_KEYUP:
128 result.type = WebInputEvent::KeyUp;
129 break;
130 case WM_IME_CHAR:
131 result.type = WebInputEvent::Char;
132 break;
133 case WM_SYSCHAR:
134 result.isSystemKey = true;
135 result.type = WebInputEvent::Char;
136 case WM_CHAR:
137 result.type = WebInputEvent::Char;
138 break;
139 default:
140 NOTREACHED();
143 if (result.type == WebInputEvent::Char
144 || result.type == WebInputEvent::RawKeyDown) {
145 result.text[0] = result.windowsKeyCode;
146 result.unmodifiedText[0] = result.windowsKeyCode;
148 if (result.type != WebInputEvent::Char) {
149 UpdateWindowsKeyCodeAndKeyIdentifier(
150 &result,
151 static_cast<ui::KeyboardCode>(result.windowsKeyCode));
154 if (::GetKeyState(VK_SHIFT) & 0x8000)
155 result.modifiers |= WebInputEvent::ShiftKey;
156 if (::GetKeyState(VK_CONTROL) & 0x8000)
157 result.modifiers |= WebInputEvent::ControlKey;
158 if (::GetKeyState(VK_MENU) & 0x8000)
159 result.modifiers |= WebInputEvent::AltKey;
160 // NOTE: There doesn't seem to be a way to query the mouse button state in
161 // this case.
163 if (LOWORD(lparam) > 1)
164 result.modifiers |= WebInputEvent::IsAutoRepeat;
166 result.modifiers |= GetLocationModifier(wparam, lparam);
168 SetToggleKeyState(&result);
169 return result;
172 // WebMouseEvent --------------------------------------------------------------
174 static int g_last_click_count = 0;
175 static double g_last_click_time = 0;
177 static LPARAM GetRelativeCursorPos(HWND hwnd) {
178 POINT pos = {-1, -1};
179 GetCursorPos(&pos);
180 ScreenToClient(hwnd, &pos);
181 return MAKELPARAM(pos.x, pos.y);
184 WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd,
185 UINT message,
186 WPARAM wparam,
187 LPARAM lparam,
188 DWORD time_ms) {
189 WebMouseEvent result;
191 switch (message) {
192 case WM_MOUSEMOVE:
193 result.type = WebInputEvent::MouseMove;
194 if (wparam & MK_LBUTTON)
195 result.button = WebMouseEvent::ButtonLeft;
196 else if (wparam & MK_MBUTTON)
197 result.button = WebMouseEvent::ButtonMiddle;
198 else if (wparam & MK_RBUTTON)
199 result.button = WebMouseEvent::ButtonRight;
200 else
201 result.button = WebMouseEvent::ButtonNone;
202 break;
203 case WM_MOUSELEAVE:
204 result.type = WebInputEvent::MouseLeave;
205 result.button = WebMouseEvent::ButtonNone;
206 // set the current mouse position (relative to the client area of the
207 // current window) since none is specified for this event
208 lparam = GetRelativeCursorPos(hwnd);
209 break;
210 case WM_LBUTTONDOWN:
211 case WM_LBUTTONDBLCLK:
212 result.type = WebInputEvent::MouseDown;
213 result.button = WebMouseEvent::ButtonLeft;
214 break;
215 case WM_MBUTTONDOWN:
216 case WM_MBUTTONDBLCLK:
217 result.type = WebInputEvent::MouseDown;
218 result.button = WebMouseEvent::ButtonMiddle;
219 break;
220 case WM_RBUTTONDOWN:
221 case WM_RBUTTONDBLCLK:
222 result.type = WebInputEvent::MouseDown;
223 result.button = WebMouseEvent::ButtonRight;
224 break;
225 case WM_LBUTTONUP:
226 result.type = WebInputEvent::MouseUp;
227 result.button = WebMouseEvent::ButtonLeft;
228 break;
229 case WM_MBUTTONUP:
230 result.type = WebInputEvent::MouseUp;
231 result.button = WebMouseEvent::ButtonMiddle;
232 break;
233 case WM_RBUTTONUP:
234 result.type = WebInputEvent::MouseUp;
235 result.button = WebMouseEvent::ButtonRight;
236 break;
237 default:
238 NOTREACHED();
241 DCHECK(time_ms);
242 result.timeStampSeconds = time_ms / 1000.0;
244 // set position fields:
246 result.x = static_cast<short>(LOWORD(lparam));
247 result.y = static_cast<short>(HIWORD(lparam));
248 result.windowX = result.x;
249 result.windowY = result.y;
251 POINT global_point = { result.x, result.y };
252 ClientToScreen(hwnd, &global_point);
254 result.globalX = global_point.x;
255 result.globalY = global_point.y;
257 // calculate number of clicks:
259 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
260 // where their original code looks buggy.
261 static int last_click_position_x;
262 static int last_click_position_y;
263 static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonLeft;
265 double current_time = result.timeStampSeconds;
266 bool cancel_previous_click =
267 (abs(last_click_position_x - result.x) >
268 (::GetSystemMetrics(SM_CXDOUBLECLK) / 2))
269 || (abs(last_click_position_y - result.y) >
270 (::GetSystemMetrics(SM_CYDOUBLECLK) / 2))
271 || ((current_time - g_last_click_time) * 1000.0 > ::GetDoubleClickTime());
273 if (result.type == WebInputEvent::MouseDown) {
274 if (!cancel_previous_click && (result.button == last_click_button)) {
275 ++g_last_click_count;
276 } else {
277 g_last_click_count = 1;
278 last_click_position_x = result.x;
279 last_click_position_y = result.y;
281 g_last_click_time = current_time;
282 last_click_button = result.button;
283 } else if (result.type == WebInputEvent::MouseMove
284 || result.type == WebInputEvent::MouseLeave) {
285 if (cancel_previous_click) {
286 g_last_click_count = 0;
287 last_click_position_x = 0;
288 last_click_position_y = 0;
289 g_last_click_time = 0;
292 result.clickCount = g_last_click_count;
294 // set modifiers:
296 if (wparam & MK_CONTROL)
297 result.modifiers |= WebInputEvent::ControlKey;
298 if (wparam & MK_SHIFT)
299 result.modifiers |= WebInputEvent::ShiftKey;
300 if (::GetKeyState(VK_MENU) & 0x8000)
301 result.modifiers |= WebInputEvent::AltKey;
302 if (wparam & MK_LBUTTON)
303 result.modifiers |= WebInputEvent::LeftButtonDown;
304 if (wparam & MK_MBUTTON)
305 result.modifiers |= WebInputEvent::MiddleButtonDown;
306 if (wparam & MK_RBUTTON)
307 result.modifiers |= WebInputEvent::RightButtonDown;
309 SetToggleKeyState(&result);
310 return result;
313 // WebMouseWheelEvent ---------------------------------------------------------
315 WebMouseWheelEvent WebMouseWheelEventBuilder::Build(HWND hwnd,
316 UINT message,
317 WPARAM wparam,
318 LPARAM lparam,
319 DWORD time_ms) {
320 WebMouseWheelEvent result;
322 result.type = WebInputEvent::MouseWheel;
324 DCHECK(time_ms);
325 result.timeStampSeconds = time_ms / 1000.0;
327 result.button = WebMouseEvent::ButtonNone;
329 // Get key state, coordinates, and wheel delta from event.
330 typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
331 GetKeyStateFunction get_key_state_func;
332 UINT key_state;
333 float wheel_delta;
334 bool horizontal_scroll = false;
335 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
336 // Synthesize mousewheel event from a scroll event. This is needed to
337 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
338 // for key state since we are synthesizing the input event.
339 get_key_state_func = GetAsyncKeyState;
340 key_state = 0;
341 if (get_key_state_func(VK_SHIFT) & 0x8000)
342 key_state |= MK_SHIFT;
343 if (get_key_state_func(VK_CONTROL) & 0x8000)
344 key_state |= MK_CONTROL;
345 // NOTE: There doesn't seem to be a way to query the mouse button state
346 // in this case.
348 POINT cursor_position = {0};
349 GetCursorPos(&cursor_position);
350 result.globalX = cursor_position.x;
351 result.globalY = cursor_position.y;
353 switch (LOWORD(wparam)) {
354 case SB_LINEUP: // == SB_LINELEFT
355 wheel_delta = WHEEL_DELTA;
356 break;
357 case SB_LINEDOWN: // == SB_LINERIGHT
358 wheel_delta = -WHEEL_DELTA;
359 break;
360 case SB_PAGEUP:
361 wheel_delta = 1;
362 result.scrollByPage = true;
363 break;
364 case SB_PAGEDOWN:
365 wheel_delta = -1;
366 result.scrollByPage = true;
367 break;
368 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
369 wheel_delta = 0;
370 break;
373 if (message == WM_HSCROLL)
374 horizontal_scroll = true;
375 } else {
376 // Non-synthesized event; we can just read data off the event.
377 get_key_state_func = ::GetKeyState;
378 key_state = GET_KEYSTATE_WPARAM(wparam);
380 result.globalX = static_cast<short>(LOWORD(lparam));
381 result.globalY = static_cast<short>(HIWORD(lparam));
383 wheel_delta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
384 if (message == WM_MOUSEHWHEEL) {
385 horizontal_scroll = true;
386 wheel_delta = -wheel_delta; // Windows is <- -/+ ->, WebKit <- +/- ->.
389 if (key_state & MK_SHIFT)
390 horizontal_scroll = true;
392 // Set modifiers based on key state.
393 if (key_state & MK_SHIFT)
394 result.modifiers |= WebInputEvent::ShiftKey;
395 if (key_state & MK_CONTROL)
396 result.modifiers |= WebInputEvent::ControlKey;
397 if (get_key_state_func(VK_MENU) & 0x8000)
398 result.modifiers |= WebInputEvent::AltKey;
399 if (key_state & MK_LBUTTON)
400 result.modifiers |= WebInputEvent::LeftButtonDown;
401 if (key_state & MK_MBUTTON)
402 result.modifiers |= WebInputEvent::MiddleButtonDown;
403 if (key_state & MK_RBUTTON)
404 result.modifiers |= WebInputEvent::RightButtonDown;
406 SetToggleKeyState(&result);
408 // Set coordinates by translating event coordinates from screen to client.
409 POINT client_point = { result.globalX, result.globalY };
410 MapWindowPoints(0, hwnd, &client_point, 1);
411 result.x = client_point.x;
412 result.y = client_point.y;
413 result.windowX = result.x;
414 result.windowY = result.y;
416 // Convert wheel delta amount to a number of pixels to scroll.
418 // How many pixels should we scroll per line? Gecko uses the height of the
419 // current line, which means scroll distance changes as you go through the
420 // page or go to different pages. IE 8 is ~60 px/line, although the value
421 // seems to vary slightly by page and zoom level. Also, IE defaults to
422 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
423 // larger scroll values without feeling as jerky. Here we use 100 px per
424 // three lines (the default scroll amount is three lines per wheel tick).
425 // Even though we have smooth scrolling, we don't make this as large as IE
426 // because subjectively IE feels like it scrolls farther than you want while
427 // reading articles.
428 static const float kScrollbarPixelsPerLine = 100.0f / 3.0f;
429 wheel_delta /= WHEEL_DELTA;
430 float scroll_delta = wheel_delta;
431 if (horizontal_scroll) {
432 unsigned long scroll_chars = kDefaultScrollCharsPerWheelDelta;
433 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scroll_chars, 0);
434 // TODO(pkasting): Should probably have a different multiplier
435 // scrollbarPixelsPerChar here.
436 scroll_delta *= static_cast<float>(scroll_chars) * kScrollbarPixelsPerLine;
437 } else {
438 unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta;
439 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0);
440 if (scroll_lines == WHEEL_PAGESCROLL)
441 result.scrollByPage = true;
442 if (!result.scrollByPage) {
443 scroll_delta *=
444 static_cast<float>(scroll_lines) * kScrollbarPixelsPerLine;
448 // Set scroll amount based on above calculations. WebKit expects positive
449 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
450 if (horizontal_scroll) {
451 result.deltaX = scroll_delta;
452 result.wheelTicksX = wheel_delta;
453 } else {
454 result.deltaY = scroll_delta;
455 result.wheelTicksY = wheel_delta;
458 return result;
461 } // namespace content