1// Copyright (c) 2012 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/web_input_event_aura.h"
6
7#include "content/browser/renderer_host/input/web_input_event_util.h"
8#include "content/browser/renderer_host/ui_events_helper.h"
9#include "ui/aura/window.h"
10#include "ui/events/event.h"
11#include "ui/events/event_utils.h"
12
13#if defined(USE_X11) || defined(USE_OZONE)
14#include "ui/events/keycodes/dom4/keycode_converter.h"
15#endif
16
17namespace content {
18
19#if defined(OS_WIN)
20blink::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent(
21    const base::NativeEvent& native_event);
22blink::WebMouseWheelEvent MakeUntranslatedWebMouseWheelEventFromNativeEvent(
23    const base::NativeEvent& native_event);
24blink::WebKeyboardEvent MakeWebKeyboardEventFromNativeEvent(
25    const base::NativeEvent& native_event);
26blink::WebGestureEvent MakeWebGestureEventFromNativeEvent(
27    const base::NativeEvent& native_event);
28#endif
29#if defined(USE_X11) || defined(USE_OZONE)
30blink::WebKeyboardEvent MakeWebKeyboardEventFromAuraEvent(
31    ui::KeyEvent* event) {
32  blink::WebKeyboardEvent webkit_event;
33
34  webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
35  webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
36
37  switch (event->type()) {
38    case ui::ET_KEY_PRESSED:
39      webkit_event.type = event->is_char() ? blink::WebInputEvent::Char :
40          blink::WebInputEvent::RawKeyDown;
41      break;
42    case ui::ET_KEY_RELEASED:
43      webkit_event.type = blink::WebInputEvent::KeyUp;
44      break;
45    default:
46      NOTREACHED();
47  }
48
49  if (webkit_event.modifiers & blink::WebInputEvent::AltKey)
50    webkit_event.isSystemKey = true;
51
52  webkit_event.windowsKeyCode = event->GetLocatedWindowsKeyboardCode();
53  webkit_event.nativeKeyCode =
54    ui::KeycodeConverter::CodeToNativeKeycode(event->code().c_str());
55  webkit_event.unmodifiedText[0] = event->GetUnmodifiedText();
56  webkit_event.text[0] = event->GetText();
57
58  webkit_event.setKeyIdentifierFromWindowsKeyCode();
59
60  return webkit_event;
61}
62
63blink::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent(
64    ui::ScrollEvent* event) {
65  blink::WebMouseWheelEvent webkit_event;
66
67  webkit_event.type = blink::WebInputEvent::MouseWheel;
68  webkit_event.button = blink::WebMouseEvent::ButtonNone;
69  webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
70  webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
71  webkit_event.hasPreciseScrollingDeltas = true;
72
73  float offset_ordinal_x = 0.f;
74  float offset_ordinal_y = 0.f;
75  if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 && event->x_offset() == 0) {
76    webkit_event.deltaX = event->y_offset();
77    webkit_event.deltaY = 0;
78    offset_ordinal_x = event->y_offset_ordinal();
79    offset_ordinal_y = event->x_offset_ordinal();
80  } else {
81    webkit_event.deltaX = event->x_offset();
82    webkit_event.deltaY = event->y_offset();
83    offset_ordinal_x = event->x_offset_ordinal();
84    offset_ordinal_y = event->y_offset_ordinal();
85  }
86
87  if (offset_ordinal_x != 0.f && webkit_event.deltaX != 0.f)
88    webkit_event.accelerationRatioX = offset_ordinal_x / webkit_event.deltaX;
89  webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick;
90  webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick;
91  if (offset_ordinal_y != 0.f && webkit_event.deltaY != 0.f)
92    webkit_event.accelerationRatioY = offset_ordinal_y / webkit_event.deltaY;
93  return webkit_event;
94}
95
96blink::WebGestureEvent MakeWebGestureEventFromAuraEvent(
97    ui::ScrollEvent* event) {
98  blink::WebGestureEvent webkit_event;
99
100  switch (event->type()) {
101    case ui::ET_SCROLL_FLING_START:
102      webkit_event.type = blink::WebInputEvent::GestureFlingStart;
103      webkit_event.data.flingStart.velocityX = event->x_offset();
104      webkit_event.data.flingStart.velocityY = event->y_offset();
105      break;
106    case ui::ET_SCROLL_FLING_CANCEL:
107      webkit_event.type = blink::WebInputEvent::GestureFlingCancel;
108      break;
109    case ui::ET_SCROLL:
110      NOTREACHED() << "Invalid gesture type: " << event->type();
111      break;
112    default:
113      NOTREACHED() << "Unknown gesture type: " << event->type();
114  }
115
116  webkit_event.sourceDevice = blink::WebGestureDeviceTouchpad;
117  webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
118  webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
119  return webkit_event;
120}
121
122#endif
123
124blink::WebMouseEvent MakeWebMouseEventFromAuraEvent(
125    ui::MouseEvent* event);
126blink::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent(
127    ui::MouseWheelEvent* event);
128
129// General approach:
130//
131// ui::Event only carries a subset of possible event data provided to Aura by
132// the host platform. WebKit utilizes a larger subset of that information than
133// Aura itself. WebKit includes some built in cracking functionality that we
134// rely on to obtain this information cleanly and consistently.
135//
136// The only place where an ui::Event's data differs from what the underlying
137// base::NativeEvent would provide is position data, since we would like to
138// provide coordinates relative to the aura::Window that is hosting the
139// renderer, not the top level platform window.
140//
141// The approach is to fully construct a blink::WebInputEvent from the
142// ui::Event's base::NativeEvent, and then replace the coordinate fields with
143// the translated values from the ui::Event.
144//
145// The exception is mouse events on linux. The ui::MouseEvent contains enough
146// necessary information to construct a WebMouseEvent. So instead of extracting
147// the information from the XEvent, which can be tricky when supporting both
148// XInput2 and XInput, the WebMouseEvent is constructed from the
149// ui::MouseEvent. This will not be necessary once only XInput2 is supported.
150//
151
152blink::WebMouseEvent MakeWebMouseEvent(ui::MouseEvent* event) {
153  // Construct an untranslated event from the platform event data.
154  blink::WebMouseEvent webkit_event =
155#if defined(OS_WIN)
156  // On Windows we have WM_ events comming from desktop and pure aura
157  // events comming from metro mode.
158  event->native_event().message ?
159      MakeUntranslatedWebMouseEventFromNativeEvent(event->native_event()) :
160      MakeWebMouseEventFromAuraEvent(event);
161#else
162  MakeWebMouseEventFromAuraEvent(event);
163#endif
164  // Replace the event's coordinate fields with translated position data from
165  // |event|.
166  webkit_event.windowX = webkit_event.x = event->x();
167  webkit_event.windowY = webkit_event.y = event->y();
168
169#if defined(OS_WIN)
170  if (event->native_event().message)
171    return webkit_event;
172#endif
173  const gfx::Point root_point = event->root_location();
174  webkit_event.globalX = root_point.x();
175  webkit_event.globalY = root_point.y();
176
177  return webkit_event;
178}
179
180blink::WebMouseWheelEvent MakeWebMouseWheelEvent(ui::MouseWheelEvent* event) {
181#if defined(OS_WIN)
182  // Construct an untranslated event from the platform event data.
183  blink::WebMouseWheelEvent webkit_event = event->native_event().message ?
184      MakeUntranslatedWebMouseWheelEventFromNativeEvent(event->native_event()) :
185      MakeWebMouseWheelEventFromAuraEvent(event);
186#else
187  blink::WebMouseWheelEvent webkit_event =
188      MakeWebMouseWheelEventFromAuraEvent(event);
189#endif
190
191  // Replace the event's coordinate fields with translated position data from
192  // |event|.
193  webkit_event.windowX = webkit_event.x = event->x();
194  webkit_event.windowY = webkit_event.y = event->y();
195
196  const gfx::Point root_point = event->root_location();
197  webkit_event.globalX = root_point.x();
198  webkit_event.globalY = root_point.y();
199
200  return webkit_event;
201}
202
203blink::WebMouseWheelEvent MakeWebMouseWheelEvent(ui::ScrollEvent* event) {
204#if defined(OS_WIN)
205  // Construct an untranslated event from the platform event data.
206  blink::WebMouseWheelEvent webkit_event =
207      MakeUntranslatedWebMouseWheelEventFromNativeEvent(event->native_event());
208#else
209  blink::WebMouseWheelEvent webkit_event =
210      MakeWebMouseWheelEventFromAuraEvent(event);
211#endif
212
213  // Replace the event's coordinate fields with translated position data from
214  // |event|.
215  webkit_event.windowX = webkit_event.x = event->x();
216  webkit_event.windowY = webkit_event.y = event->y();
217
218  const gfx::Point root_point = event->root_location();
219  webkit_event.globalX = root_point.x();
220  webkit_event.globalY = root_point.y();
221
222  return webkit_event;
223}
224
225blink::WebKeyboardEvent MakeWebKeyboardEvent(ui::KeyEvent* event) {
226  // Windows can figure out whether or not to construct a RawKeyDown or a Char
227  // WebInputEvent based on the type of message carried in
228  // event->native_event(). X11 is not so fortunate, there is no separate
229  // translated event type, so DesktopHostLinux sends an extra KeyEvent with
230  // is_char() == true. We need to pass the ui::KeyEvent to the X11 function
231  // to detect this case so the right event type can be constructed.
232#if defined(OS_WIN)
233  if (!event->HasNativeEvent())
234    return blink::WebKeyboardEvent();
235
236  // Key events require no translation by the aura system.
237  return MakeWebKeyboardEventFromNativeEvent(event->native_event());
238#else
239  return MakeWebKeyboardEventFromAuraEvent(event);
240#endif
241}
242
243blink::WebGestureEvent MakeWebGestureEvent(ui::GestureEvent* event) {
244  blink::WebGestureEvent gesture_event;
245#if defined(OS_WIN)
246  if (event->HasNativeEvent())
247    gesture_event = MakeWebGestureEventFromNativeEvent(event->native_event());
248  else
249    gesture_event = MakeWebGestureEventFromUIEvent(*event);
250#else
251  gesture_event = MakeWebGestureEventFromUIEvent(*event);
252#endif
253
254  gesture_event.x = event->x();
255  gesture_event.y = event->y();
256
257  const gfx::Point root_point = event->root_location();
258  gesture_event.globalX = root_point.x();
259  gesture_event.globalY = root_point.y();
260
261  return gesture_event;
262}
263
264blink::WebGestureEvent MakeWebGestureEvent(ui::ScrollEvent* event) {
265  blink::WebGestureEvent gesture_event;
266
267#if defined(OS_WIN)
268  gesture_event = MakeWebGestureEventFromNativeEvent(event->native_event());
269#else
270  gesture_event = MakeWebGestureEventFromAuraEvent(event);
271#endif
272
273  gesture_event.x = event->x();
274  gesture_event.y = event->y();
275
276  const gfx::Point root_point = event->root_location();
277  gesture_event.globalX = root_point.x();
278  gesture_event.globalY = root_point.y();
279
280  return gesture_event;
281}
282
283blink::WebGestureEvent MakeWebGestureEventFlingCancel() {
284  blink::WebGestureEvent gesture_event;
285
286  // All other fields are ignored on a GestureFlingCancel event.
287  gesture_event.type = blink::WebInputEvent::GestureFlingCancel;
288  gesture_event.sourceDevice = blink::WebGestureDeviceTouchpad;
289  return gesture_event;
290}
291
292blink::WebMouseEvent MakeWebMouseEventFromAuraEvent(ui::MouseEvent* event) {
293  blink::WebMouseEvent webkit_event;
294
295  webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
296  webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
297
298  webkit_event.button = blink::WebMouseEvent::ButtonNone;
299  int button_flags = event->flags();
300  if (event->type() == ui::ET_MOUSE_PRESSED ||
301      event->type() == ui::ET_MOUSE_RELEASED) {
302    // We want to use changed_button_flags() for mouse pressed & released.
303    // These flags can be used only if they are set which is not always the case
304    // (see e.g. GetChangedMouseButtonFlagsFromNative() in events_win.cc).
305    if (event->changed_button_flags())
306      button_flags = event->changed_button_flags();
307  }
308  if (button_flags & ui::EF_LEFT_MOUSE_BUTTON)
309    webkit_event.button = blink::WebMouseEvent::ButtonLeft;
310  if (button_flags & ui::EF_MIDDLE_MOUSE_BUTTON)
311    webkit_event.button = blink::WebMouseEvent::ButtonMiddle;
312  if (button_flags & ui::EF_RIGHT_MOUSE_BUTTON)
313    webkit_event.button = blink::WebMouseEvent::ButtonRight;
314
315  switch (event->type()) {
316    case ui::ET_MOUSE_PRESSED:
317      webkit_event.type = blink::WebInputEvent::MouseDown;
318      webkit_event.clickCount = event->GetClickCount();
319      break;
320    case ui::ET_MOUSE_RELEASED:
321      webkit_event.type = blink::WebInputEvent::MouseUp;
322      webkit_event.clickCount = event->GetClickCount();
323      break;
324    case ui::ET_MOUSE_ENTERED:
325    case ui::ET_MOUSE_EXITED:
326    case ui::ET_MOUSE_MOVED:
327    case ui::ET_MOUSE_DRAGGED:
328      webkit_event.type = blink::WebInputEvent::MouseMove;
329      break;
330    default:
331      NOTIMPLEMENTED() << "Received unexpected event: " << event->type();
332      break;
333  }
334
335  return webkit_event;
336}
337
338blink::WebMouseWheelEvent MakeWebMouseWheelEventFromAuraEvent(
339    ui::MouseWheelEvent* event) {
340  blink::WebMouseWheelEvent webkit_event;
341
342  webkit_event.type = blink::WebInputEvent::MouseWheel;
343  webkit_event.button = blink::WebMouseEvent::ButtonNone;
344  webkit_event.modifiers = EventFlagsToWebEventModifiers(event->flags());
345  webkit_event.timeStampSeconds = event->time_stamp().InSecondsF();
346
347  if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 && event->x_offset() == 0) {
348    webkit_event.deltaX = event->y_offset();
349    webkit_event.deltaY = 0;
350  } else {
351    webkit_event.deltaX = event->x_offset();
352    webkit_event.deltaY = event->y_offset();
353  }
354
355  webkit_event.wheelTicksX = webkit_event.deltaX / kPixelsPerTick;
356  webkit_event.wheelTicksY = webkit_event.deltaY / kPixelsPerTick;
357
358  return webkit_event;
359}
360
361}  // namespace content
362