Commit 1a9921e9 authored by garykac@chromium.org's avatar garykac@chromium.org

Import GTK WebInputEvent factory to content

(Migrated from crrev.com/21133002)

This imports the GTK WebInputEvent factory into content as
a set of WebInputEventBuilders. The functionality should be exacly the
same except for the handling of the following keycodes which changed due
to moving from the WebCore GDK -> windows keycode mapping to ui/base:

0xD7 (XK_multiply) is mapped to 0xA6 (VKEY_MULTIPLY) instead of 0
(VKEY_UKNOWN).
0xA6 (XK_brokenbar), 0xAB (XK_guillemotleft), 0xB0 (XK_degree),
0xBB (XK_guillemotright), 0xD9 (XK_Ugrave) and 0xF9 (XK_ugrave)
map to 0xE2 (VKEY_OEM_102) instead of 0 (VKEY_UNKNOWN).

I think these are progressions.

BUG=263189

Review URL: https://chromiumcodereview.appspot.com/23815005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223533 0039d316-1c4b-4281-b951-d872f2087c98
parent 57a833c2
...@@ -57,7 +57,6 @@ include_rules = [ ...@@ -57,7 +57,6 @@ include_rules = [
"+third_party/WebKit/public/web/WebTextDirection.h", "+third_party/WebKit/public/web/WebTextDirection.h",
# These should be burned down. http://crbug.com/237267 # These should be burned down. http://crbug.com/237267
"!third_party/WebKit/public/web/gtk/WebInputEventFactory.h",
"!third_party/WebKit/public/web/mac/WebInputEventFactory.h", "!third_party/WebKit/public/web/mac/WebInputEventFactory.h",
# DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!! # DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!!
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "base/logging.h"
#include "content/browser/renderer_host/input/web_input_event_util_posix.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
using WebKit::WebInputEvent;
using WebKit::WebMouseEvent;
using WebKit::WebMouseWheelEvent;
using WebKit::WebKeyboardEvent;
namespace {
// For click count tracking.
static int num_clicks = 0;
static GdkWindow* last_click_event_window = 0;
static gint last_click_time = 0;
static gint last_click_x = 0;
static gint last_click_y = 0;
static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonNone;
bool ShouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y) {
static GtkSettings* settings = gtk_settings_get_default();
if (window != last_click_event_window)
return true;
gint double_click_time = 250;
gint double_click_distance = 5;
g_object_get(G_OBJECT(settings),
"gtk-double-click-time",
&double_click_time,
"gtk-double-click-distance",
&double_click_distance,
NULL);
return (time - last_click_time) > double_click_time ||
std::abs(x - last_click_x) > double_click_distance ||
std::abs(y - last_click_y) > double_click_distance;
}
void ResetClickCountState() {
num_clicks = 0;
last_click_event_window = 0;
last_click_time = 0;
last_click_x = 0;
last_click_y = 0;
last_click_button = WebKit::WebMouseEvent::ButtonNone;
}
bool IsKeyPadKeyval(guint keyval) {
// Keypad keyvals all fall into one range.
return keyval >= GDK_KP_Space && keyval <= GDK_KP_9;
}
double GdkEventTimeToWebEventTime(guint32 time) {
// Convert from time in ms to time in sec.
return time / 1000.0;
}
int GdkStateToWebEventModifiers(guint state) {
int modifiers = 0;
if (state & GDK_SHIFT_MASK)
modifiers |= WebInputEvent::ShiftKey;
if (state & GDK_CONTROL_MASK)
modifiers |= WebInputEvent::ControlKey;
if (state & GDK_MOD1_MASK)
modifiers |= WebInputEvent::AltKey;
if (state & GDK_META_MASK)
modifiers |= WebInputEvent::MetaKey;
if (state & GDK_BUTTON1_MASK)
modifiers |= WebInputEvent::LeftButtonDown;
if (state & GDK_BUTTON2_MASK)
modifiers |= WebInputEvent::MiddleButtonDown;
if (state & GDK_BUTTON3_MASK)
modifiers |= WebInputEvent::RightButtonDown;
if (state & GDK_LOCK_MASK)
modifiers |= WebInputEvent::CapsLockOn;
if (state & GDK_MOD2_MASK)
modifiers |= WebInputEvent::NumLockOn;
// Handle the AltGr (ISO Level3 Shift) key as Control + Alt.
if (state & GDK_MOD5_MASK)
modifiers |= WebInputEvent::ControlKey | WebInputEvent::AltKey;
return modifiers;
}
ui::KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
static const unsigned int kHardwareCodeToGDKKeyval[] = {
0, // 0x00:
0, // 0x01:
0, // 0x02:
0, // 0x03:
0, // 0x04:
0, // 0x05:
0, // 0x06:
0, // 0x07:
0, // 0x08:
0, // 0x09: GDK_Escape
GDK_1, // 0x0A: GDK_1
GDK_2, // 0x0B: GDK_2
GDK_3, // 0x0C: GDK_3
GDK_4, // 0x0D: GDK_4
GDK_5, // 0x0E: GDK_5
GDK_6, // 0x0F: GDK_6
GDK_7, // 0x10: GDK_7
GDK_8, // 0x11: GDK_8
GDK_9, // 0x12: GDK_9
GDK_0, // 0x13: GDK_0
GDK_minus, // 0x14: GDK_minus
GDK_equal, // 0x15: GDK_equal
0, // 0x16: GDK_BackSpace
0, // 0x17: GDK_Tab
GDK_q, // 0x18: GDK_q
GDK_w, // 0x19: GDK_w
GDK_e, // 0x1A: GDK_e
GDK_r, // 0x1B: GDK_r
GDK_t, // 0x1C: GDK_t
GDK_y, // 0x1D: GDK_y
GDK_u, // 0x1E: GDK_u
GDK_i, // 0x1F: GDK_i
GDK_o, // 0x20: GDK_o
GDK_p, // 0x21: GDK_p
GDK_bracketleft, // 0x22: GDK_bracketleft
GDK_bracketright, // 0x23: GDK_bracketright
0, // 0x24: GDK_Return
0, // 0x25: GDK_Control_L
GDK_a, // 0x26: GDK_a
GDK_s, // 0x27: GDK_s
GDK_d, // 0x28: GDK_d
GDK_f, // 0x29: GDK_f
GDK_g, // 0x2A: GDK_g
GDK_h, // 0x2B: GDK_h
GDK_j, // 0x2C: GDK_j
GDK_k, // 0x2D: GDK_k
GDK_l, // 0x2E: GDK_l
GDK_semicolon, // 0x2F: GDK_semicolon
GDK_apostrophe, // 0x30: GDK_apostrophe
GDK_grave, // 0x31: GDK_grave
0, // 0x32: GDK_Shift_L
GDK_backslash, // 0x33: GDK_backslash
GDK_z, // 0x34: GDK_z
GDK_x, // 0x35: GDK_x
GDK_c, // 0x36: GDK_c
GDK_v, // 0x37: GDK_v
GDK_b, // 0x38: GDK_b
GDK_n, // 0x39: GDK_n
GDK_m, // 0x3A: GDK_m
GDK_comma, // 0x3B: GDK_comma
GDK_period, // 0x3C: GDK_period
GDK_slash, // 0x3D: GDK_slash
0, // 0x3E: GDK_Shift_R
0, // 0x3F:
0, // 0x40:
0, // 0x41:
0, // 0x42:
0, // 0x43:
0, // 0x44:
0, // 0x45:
0, // 0x46:
0, // 0x47:
0, // 0x48:
0, // 0x49:
0, // 0x4A:
0, // 0x4B:
0, // 0x4C:
0, // 0x4D:
0, // 0x4E:
0, // 0x4F:
0, // 0x50:
0, // 0x51:
0, // 0x52:
0, // 0x53:
0, // 0x54:
0, // 0x55:
0, // 0x56:
0, // 0x57:
0, // 0x58:
0, // 0x59:
0, // 0x5A:
0, // 0x5B:
0, // 0x5C:
0, // 0x5D:
0, // 0x5E:
0, // 0x5F:
0, // 0x60:
0, // 0x61:
0, // 0x62:
0, // 0x63:
0, // 0x64:
0, // 0x65:
0, // 0x66:
0, // 0x67:
0, // 0x68:
0, // 0x69:
0, // 0x6A:
0, // 0x6B:
0, // 0x6C:
0, // 0x6D:
0, // 0x6E:
0, // 0x6F:
0, // 0x70:
0, // 0x71:
0, // 0x72:
GDK_Super_L, // 0x73: GDK_Super_L
GDK_Super_R, // 0x74: GDK_Super_R
};
// |windows_key_code| has to include a valid virtual-key code even when we
// use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
// on the Hebrew layout, |windows_key_code| should be VK_A.
// On the other hand, |event->keyval| value depends on the current
// GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
// the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
// ui::WindowsKeyCodeForGdkKeyCode() call returns 0.
// To improve compatibilty with Windows, we use |event->hardware_keycode|
// for retrieving its Windows key-code for the keys when the
// WebCore::windows_key_codeForEvent() call returns 0.
// We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
// objects cannot change because |event->hardware_keycode| doesn't change
// even when we change the layout options, e.g. when we swap a control
// key and a caps-lock key, GTK doesn't swap their
// |event->hardware_keycode| values but swap their |event->keyval| values.
ui::KeyboardCode windows_key_code =
ui::WindowsKeyCodeForGdkKeyCode(event->keyval);
if (windows_key_code)
return windows_key_code;
if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) {
int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
if (keyval)
return ui::WindowsKeyCodeForGdkKeyCode(keyval);
}
// This key is one that keyboard-layout drivers cannot change.
// Use |event->keyval| to retrieve its |windows_key_code| value.
return ui::WindowsKeyCodeForGdkKeyCode(event->keyval);
}
// Normalizes event->state to make it Windows/Mac compatible. Since the way
// of setting modifier mask on X is very different than Windows/Mac as shown
// in http://crbug.com/127142#c8, the normalization is necessary.
guint NormalizeEventState(const GdkEventKey* event) {
guint mask = 0;
switch (GdkEventToWindowsKeyCode(event)) {
case ui::VKEY_CONTROL:
case ui::VKEY_LCONTROL:
case ui::VKEY_RCONTROL:
mask = GDK_CONTROL_MASK;
break;
case ui::VKEY_SHIFT:
case ui::VKEY_LSHIFT:
case ui::VKEY_RSHIFT:
mask = GDK_SHIFT_MASK;
break;
case ui::VKEY_MENU:
case ui::VKEY_LMENU:
case ui::VKEY_RMENU:
mask = GDK_MOD1_MASK;
break;
case ui::VKEY_CAPITAL:
mask = GDK_LOCK_MASK;
break;
default:
return event->state;
}
if (event->type == GDK_KEY_PRESS)
return event->state | mask;
return event->state & ~mask;
}
// Gets the corresponding control character of a specified key code. See:
// http://en.wikipedia.org/wiki/Control_characters
// We emulate Windows behavior here.
int GetControlCharacter(ui::KeyboardCode windows_key_code, bool shift) {
if (windows_key_code >= ui::VKEY_A && windows_key_code <= ui::VKEY_Z) {
// ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
return windows_key_code - ui::VKEY_A + 1;
}
if (shift) {
// following graphics chars require shift key to input.
switch (windows_key_code) {
// ctrl-@ maps to \x00 (Null byte)
case ui::VKEY_2:
return 0;
// ctrl-^ maps to \x1E (Record separator, Information separator two)
case ui::VKEY_6:
return 0x1E;
// ctrl-_ maps to \x1F (Unit separator, Information separator one)
case ui::VKEY_OEM_MINUS:
return 0x1F;
// Returns 0 for all other keys to avoid inputting unexpected chars.
default:
return 0;
}
} else {
switch (windows_key_code) {
// ctrl-[ maps to \x1B (Escape)
case ui::VKEY_OEM_4:
return 0x1B;
// ctrl-\ maps to \x1C (File separator, Information separator four)
case ui::VKEY_OEM_5:
return 0x1C;
// ctrl-] maps to \x1D (Group separator, Information separator three)
case ui::VKEY_OEM_6:
return 0x1D;
// ctrl-Enter maps to \x0A (Line feed)
case ui::VKEY_RETURN:
return 0x0A;
// Returns 0 for all other keys to avoid inputting unexpected chars.
default:
return 0;
}
}
}
} // namespace
namespace content {
// WebKeyboardEvent -----------------------------------------------------------
WebKeyboardEvent WebKeyboardEventBuilder::Build(const GdkEventKey* event) {
WebKeyboardEvent result;
result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
result.modifiers = GdkStateToWebEventModifiers(NormalizeEventState(event));
switch (event->type) {
case GDK_KEY_RELEASE:
result.type = WebInputEvent::KeyUp;
break;
case GDK_KEY_PRESS:
result.type = WebInputEvent::RawKeyDown;
break;
default:
NOTREACHED();
}
// According to MSDN:
// http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
// Key events with Alt modifier and F10 are system key events.
// We just emulate this behavior. It's necessary to prevent webkit from
// processing keypress event generated by alt-d, etc.
// F10 is not special on Linux, so don't treat it as system key.
if (result.modifiers & WebInputEvent::AltKey)
result.isSystemKey = true;
// The key code tells us which physical key was pressed (for example, the
// A key went down or up). It does not determine whether A should be lower
// or upper case. This is what text does, which should be the keyval.
ui::KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
result.windowsKeyCode = GetWindowsKeyCodeWithoutLocation(windows_key_code);
result.modifiers |= GetLocationModifiersFromWindowsKeyCode(windows_key_code);
result.nativeKeyCode = event->hardware_keycode;
if (result.windowsKeyCode == ui::VKEY_RETURN) {
// We need to treat the enter key as a key press of character \r. This
// is apparently just how webkit handles it and what it expects.
result.unmodifiedText[0] = '\r';
} else {
// FIXME: fix for non BMP chars
result.unmodifiedText[0] =
static_cast<int>(gdk_keyval_to_unicode(event->keyval));
}
// If ctrl key is pressed down, then control character shall be input.
if (result.modifiers & WebInputEvent::ControlKey) {
result.text[0] =
GetControlCharacter(ui::KeyboardCode(result.windowsKeyCode),
result.modifiers & WebInputEvent::ShiftKey);
} else {
result.text[0] = result.unmodifiedText[0];
}
result.setKeyIdentifierFromWindowsKeyCode();
// FIXME: Do we need to set IsAutoRepeat?
if (IsKeyPadKeyval(event->keyval))
result.modifiers |= WebInputEvent::IsKeyPad;
return result;
}
WebKeyboardEvent WebKeyboardEventBuilder::Build(wchar_t character,
int state,
double timeStampSeconds) {
// keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and
// it is hard to use/ it from signal handlers which don't use GdkEventKey
// objects (e.g. GtkIMContext signal handlers.) For such handlers, this
// function creates a WebInputEvent::Char event without using a
// GdkEventKey object.
WebKeyboardEvent result;
result.type = WebKit::WebInputEvent::Char;
result.timeStampSeconds = timeStampSeconds;
result.modifiers = GdkStateToWebEventModifiers(state);
result.windowsKeyCode = character;
result.nativeKeyCode = character;
result.text[0] = character;
result.unmodifiedText[0] = character;
// According to MSDN:
// http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
// Key events with Alt modifier and F10 are system key events.
// We just emulate this behavior. It's necessary to prevent webkit from
// processing keypress event generated by alt-d, etc.
// F10 is not special on Linux, so don't treat it as system key.
if (result.modifiers & WebInputEvent::AltKey)
result.isSystemKey = true;
return result;
}
// WebMouseEvent --------------------------------------------------------------
WebMouseEvent WebMouseEventBuilder::Build(const GdkEventButton* event) {
WebMouseEvent result;
result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
result.modifiers = GdkStateToWebEventModifiers(event->state);
result.x = static_cast<int>(event->x);
result.y = static_cast<int>(event->y);
result.windowX = result.x;
result.windowY = result.y;
result.globalX = static_cast<int>(event->x_root);
result.globalY = static_cast<int>(event->y_root);
result.clickCount = 0;
switch (event->type) {
case GDK_BUTTON_PRESS:
result.type = WebInputEvent::MouseDown;
break;
case GDK_BUTTON_RELEASE:
result.type = WebInputEvent::MouseUp;
break;
case GDK_3BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
default:
NOTREACHED();
}
result.button = WebMouseEvent::ButtonNone;
if (event->button == 1)
result.button = WebMouseEvent::ButtonLeft;
else if (event->button == 2)
result.button = WebMouseEvent::ButtonMiddle;
else if (event->button == 3)
result.button = WebMouseEvent::ButtonRight;
if (result.type == WebInputEvent::MouseDown) {
bool forgetPreviousClick = ShouldForgetPreviousClick(
event->window, event->time, event->x, event->y);
if (!forgetPreviousClick && result.button == last_click_button) {
++num_clicks;
} else {
num_clicks = 1;
last_click_event_window = event->window;
last_click_x = event->x;
last_click_y = event->y;
last_click_button = result.button;
}
last_click_time = event->time;
}
result.clickCount = num_clicks;
return result;
}
WebMouseEvent WebMouseEventBuilder::Build(const GdkEventMotion* event) {
WebMouseEvent result;
result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
result.modifiers = GdkStateToWebEventModifiers(event->state);
result.x = static_cast<int>(event->x);
result.y = static_cast<int>(event->y);
result.windowX = result.x;
result.windowY = result.y;
result.globalX = static_cast<int>(event->x_root);
result.globalY = static_cast<int>(event->y_root);
switch (event->type) {
case GDK_MOTION_NOTIFY:
result.type = WebInputEvent::MouseMove;
break;
default:
NOTREACHED();
}
result.button = WebMouseEvent::ButtonNone;
if (event->state & GDK_BUTTON1_MASK)
result.button = WebMouseEvent::ButtonLeft;
else if (event->state & GDK_BUTTON2_MASK)
result.button = WebMouseEvent::ButtonMiddle;
else if (event->state & GDK_BUTTON3_MASK)
result.button = WebMouseEvent::ButtonRight;
if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y))
ResetClickCountState();
return result;
}
WebMouseEvent WebMouseEventBuilder::Build(const GdkEventCrossing* event) {
WebMouseEvent result;
result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
result.modifiers = GdkStateToWebEventModifiers(event->state);
result.x = static_cast<int>(event->x);
result.y = static_cast<int>(event->y);
result.windowX = result.x;
result.windowY = result.y;
result.globalX = static_cast<int>(event->x_root);
result.globalY = static_cast<int>(event->y_root);
switch (event->type) {
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
// Note that if we sent MouseEnter or MouseLeave to WebKit, it
// wouldn't work - they don't result in the proper JavaScript events.
// MouseMove does the right thing.
result.type = WebInputEvent::MouseMove;
break;
default:
NOTREACHED();
}
result.button = WebMouseEvent::ButtonNone;
if (event->state & GDK_BUTTON1_MASK)
result.button = WebMouseEvent::ButtonLeft;
else if (event->state & GDK_BUTTON2_MASK)
result.button = WebMouseEvent::ButtonMiddle;
else if (event->state & GDK_BUTTON3_MASK)
result.button = WebMouseEvent::ButtonRight;
if (ShouldForgetPreviousClick(event->window, event->time, event->x, event->y))
ResetClickCountState();
return result;
}
// WebMouseWheelEvent ---------------------------------------------------------
float WebMouseWheelEventBuilder::ScrollbarPixelsPerTick() {
// How much should we scroll per mouse wheel event?
// - Windows uses 3 lines by default and obeys a system setting.
// - Mozilla has a pref that lets you either use the "system" number of lines
// to scroll, or lets the user override it.
// For the "system" number of lines, it appears they've hardcoded 3.
// See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp
// and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp .
// - Gtk makes the scroll amount a function of the size of the scroll bar,
// which is not available to us here.
// Instead, we pick a number that empirically matches Firefox's behavior.
return 160.0f / 3.0f;
}
WebMouseWheelEvent WebMouseWheelEventBuilder::Build(
const GdkEventScroll* event) {
WebMouseWheelEvent result;
result.type = WebInputEvent::MouseWheel;
result.button = WebMouseEvent::ButtonNone;
result.timeStampSeconds = GdkEventTimeToWebEventTime(event->time);
result.modifiers = GdkStateToWebEventModifiers(event->state);
result.x = static_cast<int>(event->x);
result.y = static_cast<int>(event->y);
result.windowX = result.x;
result.windowY = result.y;
result.globalX = static_cast<int>(event->x_root);
result.globalY = static_cast<int>(event->y_root);
static const float scrollbarPixelsPerTick = ScrollbarPixelsPerTick();
switch (event->direction) {
case GDK_SCROLL_UP:
result.deltaY = scrollbarPixelsPerTick;
result.wheelTicksY = 1;
break;
case GDK_SCROLL_DOWN:
result.deltaY = -scrollbarPixelsPerTick;
result.wheelTicksY = -1;
break;
case GDK_SCROLL_LEFT:
result.deltaX = scrollbarPixelsPerTick;
result.wheelTicksX = 1;
break;
case GDK_SCROLL_RIGHT:
result.deltaX = -scrollbarPixelsPerTick;
result.wheelTicksX = -1;
break;
}
return result;
}
} // namespace content
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H_
#include "content/common/content_export.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/keycodes/keyboard_codes.h"
typedef struct _GdkEventButton GdkEventButton;
typedef struct _GdkEventMotion GdkEventMotion;
typedef struct _GdkEventCrossing GdkEventCrossing;
typedef struct _GdkEventScroll GdkEventScroll;
typedef struct _GdkEventKey GdkEventKey;
namespace content {
class CONTENT_EXPORT WebKeyboardEventBuilder {
public:
static WebKit::WebKeyboardEvent Build(const GdkEventKey* event);
static WebKit::WebKeyboardEvent Build(wchar_t character,
int state,
double time_secs);
};
class CONTENT_EXPORT WebMouseEventBuilder {
public:
static WebKit::WebMouseEvent Build(const GdkEventButton* event);
static WebKit::WebMouseEvent Build(const GdkEventMotion* event);
static WebKit::WebMouseEvent Build(const GdkEventCrossing* event);
};
class CONTENT_EXPORT WebMouseWheelEventBuilder {
public:
static float ScrollbarPixelsPerTick();
static WebKit::WebMouseWheelEvent Build(
const GdkEventScroll* event);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_WEB_INPUT_EVENT_BUILDERS_GTK_H
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
using WebKit::WebInputEvent;
using WebKit::WebKeyboardEvent;
using WebKit::WebMouseEvent;
using content::WebMouseEventBuilder;
using content::WebKeyboardEventBuilder;
namespace {
TEST(WebMouseEventBuilderTest, DoubleClick) {
GdkEventButton first_click;
first_click.type = GDK_BUTTON_PRESS;
first_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1));
first_click.x = first_click.y = first_click.x_root = first_click.y_root = 100;
first_click.state = 0;
first_click.time = 0;
first_click.button = 1;
// Single click works.
WebMouseEvent first_click_events =
WebMouseEventBuilder::Build(&first_click);
EXPECT_EQ(1, first_click_events.clickCount);
// Make sure double click works.
GdkEventButton second_click = first_click;
second_click.time = first_click.time + 100;
WebMouseEvent second_click_events =
WebMouseEventBuilder::Build(&second_click);
EXPECT_EQ(2, second_click_events.clickCount);
// Reset the click count.
first_click.time += 10000;
first_click_events = WebMouseEventBuilder::Build(&first_click);
EXPECT_EQ(1, first_click_events.clickCount);
// Two clicks with a long gap in between aren't counted as a double click.
second_click = first_click;
second_click.time = first_click.time + 1000;
second_click_events = WebMouseEventBuilder::Build(&second_click);
EXPECT_EQ(1, second_click_events.clickCount);
// Reset the click count.
first_click.time += 10000;
first_click_events = WebMouseEventBuilder::Build(&first_click);
EXPECT_EQ(1, first_click_events.clickCount);
// Two clicks far apart (horizontally) aren't counted as a double click.
second_click = first_click;
second_click.time = first_click.time + 1;
second_click.x = first_click.x + 100;
second_click_events = WebMouseEventBuilder::Build(&second_click);
EXPECT_EQ(1, second_click_events.clickCount);
// Reset the click count.
first_click.time += 10000;
first_click_events = WebMouseEventBuilder::Build(&first_click);
EXPECT_EQ(1, first_click_events.clickCount);
// Two clicks far apart (vertically) aren't counted as a double click.
second_click = first_click;
second_click.time = first_click.time + 1;
second_click.x = first_click.y + 100;
second_click_events = WebMouseEventBuilder::Build(&second_click);
EXPECT_EQ(1, second_click_events.clickCount);
// Reset the click count.
first_click.time += 10000;
first_click_events = WebMouseEventBuilder::Build(&first_click);
EXPECT_EQ(1, first_click_events.clickCount);
// Two clicks on different windows aren't a double click.
second_click = first_click;
second_click.time = first_click.time + 1;
second_click.window = static_cast<GdkWindow*>(GINT_TO_POINTER(2));
second_click_events = WebMouseEventBuilder::Build(&second_click);
EXPECT_EQ(1, second_click_events.clickCount);
}
TEST(WebMouseEventBuilderTest, MouseUpClickCount) {
GdkEventButton mouse_down;
memset(&mouse_down, 0, sizeof(mouse_down));
mouse_down.type = GDK_BUTTON_PRESS;
mouse_down.window = static_cast<GdkWindow*>(GINT_TO_POINTER(1));
mouse_down.x = mouse_down.y = mouse_down.x_root = mouse_down.y_root = 100;
mouse_down.time = 0;
mouse_down.button = 1;
// Properly set the last click time, so that the internal state won't be
// affected by previous tests.
WebMouseEventBuilder::Build(&mouse_down);
mouse_down.time += 10000;
GdkEventButton mouse_up = mouse_down;
mouse_up.type = GDK_BUTTON_RELEASE;
WebMouseEvent mouse_down_event;
WebMouseEvent mouse_up_event;
// Click for three times.
for (int i = 1; i < 4; ++i) {
mouse_down.time += 100;
mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
EXPECT_EQ(i, mouse_down_event.clickCount);
mouse_up.time = mouse_down.time + 50;
mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
EXPECT_EQ(i, mouse_up_event.clickCount);
}
// Reset the click count.
mouse_down.time += 10000;
mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
EXPECT_EQ(1, mouse_down_event.clickCount);
// Moving the cursor for a significant distance will reset the click count to
// 0.
GdkEventMotion mouse_move;
memset(&mouse_move, 0, sizeof(mouse_move));
mouse_move.type = GDK_MOTION_NOTIFY;
mouse_move.window = mouse_down.window;
mouse_move.time = mouse_down.time;
mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root =
mouse_down.x + 100;
WebMouseEventBuilder::Build(&mouse_move);
mouse_up.time = mouse_down.time + 50;
mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
EXPECT_EQ(0, mouse_up_event.clickCount);
// Reset the click count.
mouse_down.time += 10000;
mouse_down_event = WebMouseEventBuilder::Build(&mouse_down);
EXPECT_EQ(1, mouse_down_event.clickCount);
// Moving the cursor with a significant delay will reset the click count to 0.
mouse_move.time = mouse_down.time + 1000;
mouse_move.x = mouse_move.y = mouse_move.x_root = mouse_move.y_root =
mouse_down.x;
WebMouseEventBuilder::Build(&mouse_move);
mouse_up.time = mouse_move.time + 50;
mouse_up_event = WebMouseEventBuilder::Build(&mouse_up);
EXPECT_EQ(0, mouse_up_event.clickCount);
}
TEST(WebKeyboardEventBuilderTest, NumPadConversion) {
// Construct a GDK input event for the numpad "5" key.
char five[] = "5";
GdkEventKey gdk_event;
memset(&gdk_event, 0, sizeof(GdkEventKey));
gdk_event.type = GDK_KEY_PRESS;
gdk_event.keyval = GDK_KP_5;
gdk_event.string = five;
// Numpad flag should be set on the WebKeyboardEvent.
WebKeyboardEvent web_event = WebKeyboardEventBuilder::Build(&gdk_event);
EXPECT_TRUE(web_event.modifiers & WebInputEvent::IsKeyPad);
}
} // anonymous namespace
...@@ -6,9 +6,7 @@ ...@@ -6,9 +6,7 @@
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h" #include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
using WebKit::WebInputEventFactory;
namespace { namespace {
...@@ -32,7 +30,7 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent() ...@@ -32,7 +30,7 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent()
} }
NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
: WebKeyboardEvent(WebInputEventFactory::keyboardEvent(&native_event->key)), : WebKeyboardEvent(WebKeyboardEventBuilder::Build(&native_event->key)),
skip_in_browser(false), skip_in_browser(false),
match_edit_command(false) { match_edit_command(false) {
CopyEventTo(native_event, &os_event); CopyEventTo(native_event, &os_event);
...@@ -41,9 +39,9 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event) ...@@ -41,9 +39,9 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(gfx::NativeEvent native_event)
NativeWebKeyboardEvent::NativeWebKeyboardEvent(wchar_t character, NativeWebKeyboardEvent::NativeWebKeyboardEvent(wchar_t character,
int state, int state,
double time_stamp_seconds) double time_stamp_seconds)
: WebKeyboardEvent(WebInputEventFactory::keyboardEvent(character, : WebKeyboardEvent(WebKeyboardEventBuilder::Build(character,
state, state,
time_stamp_seconds)), time_stamp_seconds)),
os_event(NULL), os_event(NULL),
skip_in_browser(false), skip_in_browser(false),
match_edit_command(false) { match_edit_command(false) {
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "content/browser/renderer_host/gtk_im_context_wrapper.h" #include "content/browser/renderer_host/gtk_im_context_wrapper.h"
#include "content/browser/renderer_host/gtk_key_bindings_handler.h" #include "content/browser/renderer_host/gtk_key_bindings_handler.h"
#include "content/browser/renderer_host/gtk_window_utils.h" #include "content/browser/renderer_host/gtk_window_utils.h"
#include "content/browser/renderer_host/input/web_input_event_builders_gtk.h"
#include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/gpu/gpu_messages.h" #include "content/common/gpu/gpu_messages.h"
...@@ -40,7 +41,6 @@ ...@@ -40,7 +41,6 @@
#include "skia/ext/platform_canvas.h" #include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebInputEvent.h"
#include "third_party/WebKit/public/web/WebScreenInfo.h" #include "third_party/WebKit/public/web/WebScreenInfo.h"
#include "third_party/WebKit/public/web/gtk/WebInputEventFactory.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/x/active_window_watcher_x.h" #include "ui/base/x/active_window_watcher_x.h"
#include "ui/base/x/x11_util.h" #include "ui/base/x/x11_util.h"
...@@ -50,7 +50,6 @@ ...@@ -50,7 +50,6 @@
#include "ui/gfx/text_elider.h" #include "ui/gfx/text_elider.h"
#include "webkit/common/cursors/webcursor_gtk_data.h" #include "webkit/common/cursors/webcursor_gtk_data.h"
using WebKit::WebInputEventFactory;
using WebKit::WebMouseWheelEvent; using WebKit::WebMouseWheelEvent;
using WebKit::WebScreenInfo; using WebKit::WebScreenInfo;
...@@ -70,10 +69,6 @@ namespace { ...@@ -70,10 +69,6 @@ namespace {
const int kMaxWindowWidth = 10000; const int kMaxWindowWidth = 10000;
const int kMaxWindowHeight = 10000; const int kMaxWindowHeight = 10000;
// See WebInputEventFactor.cpp for a reason for this being the default
// scroll size for linux.
const float kDefaultScrollPixelsPerTick = 160.0f / 3.0f;
const GdkColor kBGColor = const GdkColor kBGColor =
#if defined(NDEBUG) #if defined(NDEBUG)
{ 0, 0xff * 257, 0xff * 257, 0xff * 257 }; { 0, 0xff * 257, 0xff * 257, 0xff * 257 };
...@@ -354,7 +349,7 @@ class RenderWidgetHostViewGtkWidget { ...@@ -354,7 +349,7 @@ class RenderWidgetHostViewGtkWidget {
RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl* widget_host =
RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
if (widget_host) if (widget_host)
widget_host->ForwardMouseEvent(WebInputEventFactory::mouseEvent(event)); widget_host->ForwardMouseEvent(WebMouseEventBuilder::Build(event));
// Although we did handle the mouse event, we need to let other handlers // Although we did handle the mouse event, we need to let other handlers
// run (in particular the one installed by WebContentsViewGtk). // run (in particular the one installed by WebContentsViewGtk).
...@@ -380,8 +375,7 @@ class RenderWidgetHostViewGtkWidget { ...@@ -380,8 +375,7 @@ class RenderWidgetHostViewGtkWidget {
host_view->ModifyEventForEdgeDragging(widget, event); host_view->ModifyEventForEdgeDragging(widget, event);
WebKit::WebMouseEvent mouse_event = WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
WebInputEventFactory::mouseEvent(event);
if (host_view->mouse_locked_) { if (host_view->mouse_locked_) {
gfx::Point center = host_view->GetWidgetCenter(); gfx::Point center = host_view->GetWidgetCenter();
...@@ -431,8 +425,7 @@ class RenderWidgetHostViewGtkWidget { ...@@ -431,8 +425,7 @@ class RenderWidgetHostViewGtkWidget {
// additionally send this crossing event with the state indicating the // additionally send this crossing event with the state indicating the
// button is down, it causes problems with drag and drop in WebKit.) // button is down, it causes problems with drag and drop in WebKit.)
if (!(event->state & any_button_mask)) { if (!(event->state & any_button_mask)) {
WebKit::WebMouseEvent mouse_event = WebKit::WebMouseEvent mouse_event = WebMouseEventBuilder::Build(event);
WebInputEventFactory::mouseEvent(event);
host_view->ModifyEventMovementAndCoords(&mouse_event); host_view->ModifyEventMovementAndCoords(&mouse_event);
// When crossing out and back into a render view the movement values // When crossing out and back into a render view the movement values
// must represent the instantaneous movement of the mouse, not the jump // must represent the instantaneous movement of the mouse, not the jump
...@@ -500,7 +493,7 @@ class RenderWidgetHostViewGtkWidget { ...@@ -500,7 +493,7 @@ class RenderWidgetHostViewGtkWidget {
gdk_event_put(event); gdk_event_put(event);
gdk_event_free(event); gdk_event_free(event);
} }
return num_clicks * kDefaultScrollPixelsPerTick; return num_clicks * WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
} }
static gboolean OnMouseScrollEvent(GtkWidget* widget, static gboolean OnMouseScrollEvent(GtkWidget* widget,
...@@ -518,21 +511,23 @@ class RenderWidgetHostViewGtkWidget { ...@@ -518,21 +511,23 @@ class RenderWidgetHostViewGtkWidget {
event->direction = GDK_SCROLL_RIGHT; event->direction = GDK_SCROLL_RIGHT;
} }
WebMouseWheelEvent web_event = WebInputEventFactory::mouseWheelEvent(event); WebMouseWheelEvent web_event = WebMouseWheelEventBuilder::Build(event);
const float pixelsPerTick =
WebMouseWheelEventBuilder::ScrollbarPixelsPerTick();
// We peek ahead at the top of the queue to look for additional pending // We peek ahead at the top of the queue to look for additional pending
// scroll events. // scroll events.
if (event->direction == GDK_SCROLL_UP || if (event->direction == GDK_SCROLL_UP ||
event->direction == GDK_SCROLL_DOWN) { event->direction == GDK_SCROLL_DOWN) {
if (event->direction == GDK_SCROLL_UP) if (event->direction == GDK_SCROLL_UP)
web_event.deltaY = kDefaultScrollPixelsPerTick; web_event.deltaY = pixelsPerTick;
else else
web_event.deltaY = -kDefaultScrollPixelsPerTick; web_event.deltaY = -pixelsPerTick;
web_event.deltaY += GetPendingScrollDelta(true, event->state); web_event.deltaY += GetPendingScrollDelta(true, event->state);
} else { } else {
if (event->direction == GDK_SCROLL_LEFT) if (event->direction == GDK_SCROLL_LEFT)
web_event.deltaX = kDefaultScrollPixelsPerTick; web_event.deltaX = pixelsPerTick;
else else
web_event.deltaX = -kDefaultScrollPixelsPerTick; web_event.deltaX = -pixelsPerTick;
web_event.deltaX += GetPendingScrollDelta(false, event->state); web_event.deltaX += GetPendingScrollDelta(false, event->state);
} }
RenderWidgetHostImpl::From( RenderWidgetHostImpl::From(
......
...@@ -844,6 +844,8 @@ ...@@ -844,6 +844,8 @@
'browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc', 'browser/renderer_host/input/touchscreen_tap_suppression_controller_stub.cc',
'browser/renderer_host/input/web_input_event_builders_android.cc', 'browser/renderer_host/input/web_input_event_builders_android.cc',
'browser/renderer_host/input/web_input_event_builders_android.h', 'browser/renderer_host/input/web_input_event_builders_android.h',
'browser/renderer_host/input/web_input_event_builders_gtk.cc',
'browser/renderer_host/input/web_input_event_builders_gtk.h',
'browser/renderer_host/input/web_input_event_builders_win.cc', 'browser/renderer_host/input/web_input_event_builders_win.cc',
'browser/renderer_host/input/web_input_event_builders_win.h', 'browser/renderer_host/input/web_input_event_builders_win.h',
'browser/renderer_host/input/web_input_event_util.cc', 'browser/renderer_host/input/web_input_event_util.cc',
......
...@@ -411,6 +411,7 @@ ...@@ -411,6 +411,7 @@
'browser/renderer_host/synthetic_gesture_controller_unittest.cc', 'browser/renderer_host/synthetic_gesture_controller_unittest.cc',
'browser/renderer_host/text_input_client_mac_unittest.mm', 'browser/renderer_host/text_input_client_mac_unittest.mm',
'browser/renderer_host/web_input_event_aura_unittest.cc', 'browser/renderer_host/web_input_event_aura_unittest.cc',
'browser/renderer_host/input/web_input_event_builders_gtk_unittest.cc',
'browser/resolve_proxy_msg_helper_unittest.cc', 'browser/resolve_proxy_msg_helper_unittest.cc',
'browser/site_instance_impl_unittest.cc', 'browser/site_instance_impl_unittest.cc',
'browser/speech/chunked_byte_buffer_unittest.cc', 'browser/speech/chunked_byte_buffer_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment