1 /*
2 * Copyright (C) 2006-2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "WebInputEventFactory.h"
33
34 #include "KeyboardCodes.h"
35 #include "KeyCodeConversion.h"
36
37 #include "WebInputEvent.h"
38
39 #include <gdk/gdk.h>
40 #include <gdk/gdkkeysyms.h>
41 #include <gtk/gtk.h>
42 #include <gtk/gtkversion.h>
43
44 #include <wtf/Assertions.h>
45
46 namespace {
47
48 // For click count tracking.
49 static int gNumClicks = 0;
50 static GdkWindow* gLastClickEventWindow = 0;
51 static gint gLastClickTime = 0;
52 static gint gLastClickX = 0;
53 static gint gLastClickY = 0;
54 static WebKit::WebMouseEvent::Button gLastClickButton = WebKit::WebMouseEvent::ButtonNone;
55
shouldForgetPreviousClick(GdkWindow * window,gint time,gint x,gint y)56 bool shouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y)
57 {
58 static GtkSettings* settings = gtk_settings_get_default();
59
60 if (window != gLastClickEventWindow)
61 return true;
62
63 gint doubleClickTime = 250;
64 gint doubleClickDistance = 5;
65 g_object_get(G_OBJECT(settings),
66 "gtk-double-click-time", &doubleClickTime,
67 "gtk-double-click-distance", &doubleClickDistance, NULL);
68 return (time - gLastClickTime) > doubleClickTime
69 || abs(x - gLastClickX) > doubleClickDistance
70 || abs(y - gLastClickY) > doubleClickDistance;
71 }
72
resetClickCountState()73 void resetClickCountState()
74 {
75 gNumClicks = 0;
76 gLastClickEventWindow = 0;
77 gLastClickTime = 0;
78 gLastClickX = 0;
79 gLastClickY = 0;
80 gLastClickButton = WebKit::WebMouseEvent::ButtonNone;
81 }
82
83 } // namespace
84
85 namespace WebKit {
86
gdkEventTimeToWebEventTime(guint32 time)87 static double gdkEventTimeToWebEventTime(guint32 time)
88 {
89 // Convert from time in ms to time in sec.
90 return time / 1000.0;
91 }
92
gdkStateToWebEventModifiers(guint state)93 static int gdkStateToWebEventModifiers(guint state)
94 {
95 int modifiers = 0;
96 if (state & GDK_SHIFT_MASK)
97 modifiers |= WebInputEvent::ShiftKey;
98 if (state & GDK_CONTROL_MASK)
99 modifiers |= WebInputEvent::ControlKey;
100 if (state & GDK_MOD1_MASK)
101 modifiers |= WebInputEvent::AltKey;
102 #if GTK_CHECK_VERSION(2, 10, 0)
103 if (state & GDK_META_MASK)
104 modifiers |= WebInputEvent::MetaKey;
105 #endif
106 if (state & GDK_BUTTON1_MASK)
107 modifiers |= WebInputEvent::LeftButtonDown;
108 if (state & GDK_BUTTON2_MASK)
109 modifiers |= WebInputEvent::MiddleButtonDown;
110 if (state & GDK_BUTTON3_MASK)
111 modifiers |= WebInputEvent::RightButtonDown;
112 if (state & GDK_LOCK_MASK)
113 modifiers |= WebInputEvent::CapsLockOn;
114 if (state & GDK_MOD2_MASK)
115 modifiers |= WebInputEvent::NumLockOn;
116 return modifiers;
117 }
118
gdkEventToWindowsKeyCode(const GdkEventKey * event)119 static int gdkEventToWindowsKeyCode(const GdkEventKey* event)
120 {
121 static const unsigned int hardwareCodeToGDKKeyval[] = {
122 0, // 0x00:
123 0, // 0x01:
124 0, // 0x02:
125 0, // 0x03:
126 0, // 0x04:
127 0, // 0x05:
128 0, // 0x06:
129 0, // 0x07:
130 0, // 0x08:
131 0, // 0x09: GDK_Escape
132 GDK_1, // 0x0A: GDK_1
133 GDK_2, // 0x0B: GDK_2
134 GDK_3, // 0x0C: GDK_3
135 GDK_4, // 0x0D: GDK_4
136 GDK_5, // 0x0E: GDK_5
137 GDK_6, // 0x0F: GDK_6
138 GDK_7, // 0x10: GDK_7
139 GDK_8, // 0x11: GDK_8
140 GDK_9, // 0x12: GDK_9
141 GDK_0, // 0x13: GDK_0
142 GDK_minus, // 0x14: GDK_minus
143 GDK_equal, // 0x15: GDK_equal
144 0, // 0x16: GDK_BackSpace
145 0, // 0x17: GDK_Tab
146 GDK_q, // 0x18: GDK_q
147 GDK_w, // 0x19: GDK_w
148 GDK_e, // 0x1A: GDK_e
149 GDK_r, // 0x1B: GDK_r
150 GDK_t, // 0x1C: GDK_t
151 GDK_y, // 0x1D: GDK_y
152 GDK_u, // 0x1E: GDK_u
153 GDK_i, // 0x1F: GDK_i
154 GDK_o, // 0x20: GDK_o
155 GDK_p, // 0x21: GDK_p
156 GDK_bracketleft, // 0x22: GDK_bracketleft
157 GDK_bracketright, // 0x23: GDK_bracketright
158 0, // 0x24: GDK_Return
159 0, // 0x25: GDK_Control_L
160 GDK_a, // 0x26: GDK_a
161 GDK_s, // 0x27: GDK_s
162 GDK_d, // 0x28: GDK_d
163 GDK_f, // 0x29: GDK_f
164 GDK_g, // 0x2A: GDK_g
165 GDK_h, // 0x2B: GDK_h
166 GDK_j, // 0x2C: GDK_j
167 GDK_k, // 0x2D: GDK_k
168 GDK_l, // 0x2E: GDK_l
169 GDK_semicolon, // 0x2F: GDK_semicolon
170 GDK_apostrophe, // 0x30: GDK_apostrophe
171 GDK_grave, // 0x31: GDK_grave
172 0, // 0x32: GDK_Shift_L
173 GDK_backslash, // 0x33: GDK_backslash
174 GDK_z, // 0x34: GDK_z
175 GDK_x, // 0x35: GDK_x
176 GDK_c, // 0x36: GDK_c
177 GDK_v, // 0x37: GDK_v
178 GDK_b, // 0x38: GDK_b
179 GDK_n, // 0x39: GDK_n
180 GDK_m, // 0x3A: GDK_m
181 GDK_comma, // 0x3B: GDK_comma
182 GDK_period, // 0x3C: GDK_period
183 GDK_slash, // 0x3D: GDK_slash
184 0, // 0x3E: GDK_Shift_R
185 0, // 0x3F:
186 0, // 0x40:
187 0, // 0x41:
188 0, // 0x42:
189 0, // 0x43:
190 0, // 0x44:
191 0, // 0x45:
192 0, // 0x46:
193 0, // 0x47:
194 0, // 0x48:
195 0, // 0x49:
196 0, // 0x4A:
197 0, // 0x4B:
198 0, // 0x4C:
199 0, // 0x4D:
200 0, // 0x4E:
201 0, // 0x4F:
202 0, // 0x50:
203 0, // 0x51:
204 0, // 0x52:
205 0, // 0x53:
206 0, // 0x54:
207 0, // 0x55:
208 0, // 0x56:
209 0, // 0x57:
210 0, // 0x58:
211 0, // 0x59:
212 0, // 0x5A:
213 0, // 0x5B:
214 0, // 0x5C:
215 0, // 0x5D:
216 0, // 0x5E:
217 0, // 0x5F:
218 0, // 0x60:
219 0, // 0x61:
220 0, // 0x62:
221 0, // 0x63:
222 0, // 0x64:
223 0, // 0x65:
224 0, // 0x66:
225 0, // 0x67:
226 0, // 0x68:
227 0, // 0x69:
228 0, // 0x6A:
229 0, // 0x6B:
230 0, // 0x6C:
231 0, // 0x6D:
232 0, // 0x6E:
233 0, // 0x6F:
234 0, // 0x70:
235 0, // 0x71:
236 0, // 0x72:
237 GDK_Super_L, // 0x73: GDK_Super_L
238 GDK_Super_R, // 0x74: GDK_Super_R
239 };
240
241 // |windowsKeyCode| has to include a valid virtual-key code even when we
242 // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
243 // on the Hebrew layout, |windowsKeyCode| should be VK_A.
244 // On the other hand, |event->keyval| value depends on the current
245 // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
246 // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
247 // WebCore::windowsKeyCodeForKeyEvent() call returns 0.
248 // To improve compatibilty with Windows, we use |event->hardware_keycode|
249 // for retrieving its Windows key-code for the keys when the
250 // WebCore::windowsKeyCodeForEvent() call returns 0.
251 // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
252 // objects cannot change because |event->hardware_keycode| doesn't change
253 // even when we change the layout options, e.g. when we swap a control
254 // key and a caps-lock key, GTK doesn't swap their
255 // |event->hardware_keycode| values but swap their |event->keyval| values.
256 int windowsKeyCode = WebCore::windowsKeyCodeForKeyEvent(event->keyval);
257 if (windowsKeyCode)
258 return windowsKeyCode;
259
260 const int tableSize = sizeof(hardwareCodeToGDKKeyval) / sizeof(hardwareCodeToGDKKeyval[0]);
261 if (event->hardware_keycode < tableSize) {
262 int keyval = hardwareCodeToGDKKeyval[event->hardware_keycode];
263 if (keyval)
264 return WebCore::windowsKeyCodeForKeyEvent(keyval);
265 }
266
267 // This key is one that keyboard-layout drivers cannot change.
268 // Use |event->keyval| to retrieve its |windowsKeyCode| value.
269 return WebCore::windowsKeyCodeForKeyEvent(event->keyval);
270 }
271
272 // Gets the corresponding control character of a specified key code. See:
273 // http://en.wikipedia.org/wiki/Control_characters
274 // We emulate Windows behavior here.
getControlCharacter(int windowsKeyCode,bool shift)275 static WebUChar getControlCharacter(int windowsKeyCode, bool shift)
276 {
277 if (windowsKeyCode >= WebCore::VKEY_A && windowsKeyCode <= WebCore::VKEY_Z) {
278 // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
279 return windowsKeyCode - WebCore::VKEY_A + 1;
280 }
281 if (shift) {
282 // following graphics chars require shift key to input.
283 switch (windowsKeyCode) {
284 // ctrl-@ maps to \x00 (Null byte)
285 case WebCore::VKEY_2:
286 return 0;
287 // ctrl-^ maps to \x1E (Record separator, Information separator two)
288 case WebCore::VKEY_6:
289 return 0x1E;
290 // ctrl-_ maps to \x1F (Unit separator, Information separator one)
291 case WebCore::VKEY_OEM_MINUS:
292 return 0x1F;
293 // Returns 0 for all other keys to avoid inputting unexpected chars.
294 default:
295 return 0;
296 }
297 } else {
298 switch (windowsKeyCode) {
299 // ctrl-[ maps to \x1B (Escape)
300 case WebCore::VKEY_OEM_4:
301 return 0x1B;
302 // ctrl-\ maps to \x1C (File separator, Information separator four)
303 case WebCore::VKEY_OEM_5:
304 return 0x1C;
305 // ctrl-] maps to \x1D (Group separator, Information separator three)
306 case WebCore::VKEY_OEM_6:
307 return 0x1D;
308 // ctrl-Enter maps to \x0A (Line feed)
309 case WebCore::VKEY_RETURN:
310 return 0x0A;
311 // Returns 0 for all other keys to avoid inputting unexpected chars.
312 default:
313 return 0;
314 }
315 }
316 }
317
318 // WebKeyboardEvent -----------------------------------------------------------
319
keyboardEvent(const GdkEventKey * event)320 WebKeyboardEvent WebInputEventFactory::keyboardEvent(const GdkEventKey* event)
321 {
322 WebKeyboardEvent result;
323
324 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
325 result.modifiers = gdkStateToWebEventModifiers(event->state);
326
327 switch (event->type) {
328 case GDK_KEY_RELEASE:
329 result.type = WebInputEvent::KeyUp;
330 break;
331 case GDK_KEY_PRESS:
332 result.type = WebInputEvent::RawKeyDown;
333 break;
334 default:
335 ASSERT_NOT_REACHED();
336 }
337
338 // According to MSDN:
339 // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
340 // Key events with Alt modifier and F10 are system key events.
341 // We just emulate this behavior. It's necessary to prevent webkit from
342 // processing keypress event generated by alt-d, etc.
343 // F10 is not special on Linux, so don't treat it as system key.
344 if (result.modifiers & WebInputEvent::AltKey)
345 result.isSystemKey = true;
346
347 // The key code tells us which physical key was pressed (for example, the
348 // A key went down or up). It does not determine whether A should be lower
349 // or upper case. This is what text does, which should be the keyval.
350 result.windowsKeyCode = gdkEventToWindowsKeyCode(event);
351 result.nativeKeyCode = event->hardware_keycode;
352
353 if (result.windowsKeyCode == WebCore::VKEY_RETURN)
354 // We need to treat the enter key as a key press of character \r. This
355 // is apparently just how webkit handles it and what it expects.
356 result.unmodifiedText[0] = '\r';
357 else
358 // FIXME: fix for non BMP chars
359 result.unmodifiedText[0] =
360 static_cast<WebUChar>(gdk_keyval_to_unicode(event->keyval));
361
362 // If ctrl key is pressed down, then control character shall be input.
363 if (result.modifiers & WebInputEvent::ControlKey)
364 result.text[0] = getControlCharacter(
365 result.windowsKeyCode, result.modifiers & WebInputEvent::ShiftKey);
366 else
367 result.text[0] = result.unmodifiedText[0];
368
369 result.setKeyIdentifierFromWindowsKeyCode();
370
371 // FIXME: Do we need to set IsAutoRepeat or IsKeyPad?
372
373 return result;
374 }
375
keyboardEvent(wchar_t character,int state,double timeStampSeconds)376 WebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character, int state, double timeStampSeconds)
377 {
378 // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and
379 // it is hard to use/ it from signal handlers which don't use GdkEventKey
380 // objects (e.g. GtkIMContext signal handlers.) For such handlers, this
381 // function creates a WebInputEvent::Char event without using a
382 // GdkEventKey object.
383 WebKeyboardEvent result;
384 result.type = WebKit::WebInputEvent::Char;
385 result.timeStampSeconds = timeStampSeconds;
386 result.modifiers = gdkStateToWebEventModifiers(state);
387 result.windowsKeyCode = character;
388 result.nativeKeyCode = character;
389 result.text[0] = character;
390 result.unmodifiedText[0] = character;
391
392 // According to MSDN:
393 // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
394 // Key events with Alt modifier and F10 are system key events.
395 // We just emulate this behavior. It's necessary to prevent webkit from
396 // processing keypress event generated by alt-d, etc.
397 // F10 is not special on Linux, so don't treat it as system key.
398 if (result.modifiers & WebInputEvent::AltKey)
399 result.isSystemKey = true;
400
401 return result;
402 }
403
404 // WebMouseEvent --------------------------------------------------------------
405
mouseEvent(const GdkEventButton * event)406 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventButton* event)
407 {
408 WebMouseEvent result;
409
410 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
411
412 result.modifiers = gdkStateToWebEventModifiers(event->state);
413 result.x = static_cast<int>(event->x);
414 result.y = static_cast<int>(event->y);
415 result.windowX = result.x;
416 result.windowY = result.y;
417 result.globalX = static_cast<int>(event->x_root);
418 result.globalY = static_cast<int>(event->y_root);
419 result.clickCount = 0;
420
421 switch (event->type) {
422 case GDK_BUTTON_PRESS:
423 result.type = WebInputEvent::MouseDown;
424 break;
425 case GDK_BUTTON_RELEASE:
426 result.type = WebInputEvent::MouseUp;
427 break;
428 case GDK_3BUTTON_PRESS:
429 case GDK_2BUTTON_PRESS:
430 default:
431 ASSERT_NOT_REACHED();
432 };
433
434 result.button = WebMouseEvent::ButtonNone;
435 if (event->button == 1)
436 result.button = WebMouseEvent::ButtonLeft;
437 else if (event->button == 2)
438 result.button = WebMouseEvent::ButtonMiddle;
439 else if (event->button == 3)
440 result.button = WebMouseEvent::ButtonRight;
441
442 if (result.type == WebInputEvent::MouseDown) {
443 bool forgetPreviousClick = shouldForgetPreviousClick(event->window, event->time, event->x, event->y);
444
445 if (!forgetPreviousClick && result.button == gLastClickButton)
446 ++gNumClicks;
447 else {
448 gNumClicks = 1;
449
450 gLastClickEventWindow = event->window;
451 gLastClickX = event->x;
452 gLastClickY = event->y;
453 gLastClickButton = result.button;
454 }
455 gLastClickTime = event->time;
456 }
457 result.clickCount = gNumClicks;
458
459 return result;
460 }
461
mouseEvent(const GdkEventMotion * event)462 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventMotion* event)
463 {
464 WebMouseEvent result;
465
466 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
467 result.modifiers = gdkStateToWebEventModifiers(event->state);
468 result.x = static_cast<int>(event->x);
469 result.y = static_cast<int>(event->y);
470 result.windowX = result.x;
471 result.windowY = result.y;
472 result.globalX = static_cast<int>(event->x_root);
473 result.globalY = static_cast<int>(event->y_root);
474
475 switch (event->type) {
476 case GDK_MOTION_NOTIFY:
477 result.type = WebInputEvent::MouseMove;
478 break;
479 default:
480 ASSERT_NOT_REACHED();
481 }
482
483 result.button = WebMouseEvent::ButtonNone;
484 if (event->state & GDK_BUTTON1_MASK)
485 result.button = WebMouseEvent::ButtonLeft;
486 else if (event->state & GDK_BUTTON2_MASK)
487 result.button = WebMouseEvent::ButtonMiddle;
488 else if (event->state & GDK_BUTTON3_MASK)
489 result.button = WebMouseEvent::ButtonRight;
490
491 if (shouldForgetPreviousClick(event->window, event->time, event->x, event->y))
492 resetClickCountState();
493
494 return result;
495 }
496
mouseEvent(const GdkEventCrossing * event)497 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventCrossing* event)
498 {
499 WebMouseEvent result;
500
501 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
502 result.modifiers = gdkStateToWebEventModifiers(event->state);
503 result.x = static_cast<int>(event->x);
504 result.y = static_cast<int>(event->y);
505 result.windowX = result.x;
506 result.windowY = result.y;
507 result.globalX = static_cast<int>(event->x_root);
508 result.globalY = static_cast<int>(event->y_root);
509
510 switch (event->type) {
511 case GDK_ENTER_NOTIFY:
512 case GDK_LEAVE_NOTIFY:
513 // Note that if we sent MouseEnter or MouseLeave to WebKit, it
514 // wouldn't work - they don't result in the proper JavaScript events.
515 // MouseMove does the right thing.
516 result.type = WebInputEvent::MouseMove;
517 break;
518 default:
519 ASSERT_NOT_REACHED();
520 }
521
522 result.button = WebMouseEvent::ButtonNone;
523 if (event->state & GDK_BUTTON1_MASK)
524 result.button = WebMouseEvent::ButtonLeft;
525 else if (event->state & GDK_BUTTON2_MASK)
526 result.button = WebMouseEvent::ButtonMiddle;
527 else if (event->state & GDK_BUTTON3_MASK)
528 result.button = WebMouseEvent::ButtonRight;
529
530 if (shouldForgetPreviousClick(event->window, event->time, event->x, event->y))
531 resetClickCountState();
532
533 return result;
534 }
535
536 // WebMouseWheelEvent ---------------------------------------------------------
537
mouseWheelEvent(const GdkEventScroll * event)538 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(const GdkEventScroll* event)
539 {
540 WebMouseWheelEvent result;
541
542 result.type = WebInputEvent::MouseWheel;
543 result.button = WebMouseEvent::ButtonNone;
544
545 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
546 result.modifiers = gdkStateToWebEventModifiers(event->state);
547 result.x = static_cast<int>(event->x);
548 result.y = static_cast<int>(event->y);
549 result.windowX = result.x;
550 result.windowY = result.y;
551 result.globalX = static_cast<int>(event->x_root);
552 result.globalY = static_cast<int>(event->y_root);
553
554 // How much should we scroll per mouse wheel event?
555 // - Windows uses 3 lines by default and obeys a system setting.
556 // - Mozilla has a pref that lets you either use the "system" number of lines
557 // to scroll, or lets the user override it.
558 // For the "system" number of lines, it appears they've hardcoded 3.
559 // See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp
560 // and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp .
561 // - Gtk makes the scroll amount a function of the size of the scroll bar,
562 // which is not available to us here.
563 // Instead, we pick a number that empirically matches Firefox's behavior.
564 static const float scrollbarPixelsPerTick = 160.0f / 3.0f;
565
566 switch (event->direction) {
567 case GDK_SCROLL_UP:
568 result.deltaY = scrollbarPixelsPerTick;
569 result.wheelTicksY = 1;
570 break;
571 case GDK_SCROLL_DOWN:
572 result.deltaY = -scrollbarPixelsPerTick;
573 result.wheelTicksY = -1;
574 break;
575 case GDK_SCROLL_LEFT:
576 result.deltaX = scrollbarPixelsPerTick;
577 result.wheelTicksX = 1;
578 break;
579 case GDK_SCROLL_RIGHT:
580 result.deltaX = -scrollbarPixelsPerTick;
581 result.wheelTicksX = -1;
582 break;
583 }
584
585 return result;
586 }
587
588 } // namespace WebKit
589