<http://webkit.org/b/91015> Remove BUILDING_ON / TARGETING macros in favor of system...
[WebKit-https.git] / Source / WebKit / chromium / src / mac / WebInputEventFactory.mm
1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2006-2009 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "WebInputEventFactory.h"
29
30 #include <ApplicationServices/ApplicationServices.h>
31 #import <Cocoa/Cocoa.h>
32
33 #import "KeyEventCocoa.h"
34 #include "WebInputEvent.h"
35 #include <wtf/ASCIICType.h>
36
37 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1060
38
39 // Additional Lion APIs.
40 enum {
41     NSEventPhaseNone        = 0,
42     NSEventPhaseBegan       = 0x1 << 0,
43     NSEventPhaseStationary  = 0x1 << 1,
44     NSEventPhaseChanged     = 0x1 << 2,
45     NSEventPhaseEnded       = 0x1 << 3,
46     NSEventPhaseCancelled   = 0x1 << 4,
47     NSEventPhaseMayBegin    = 0x1 << 5
48 };
49 typedef NSUInteger NSEventPhase;
50
51 @interface NSEvent (LionSDKDeclarations)
52 - (NSEventPhase)phase;
53 - (NSEventPhase)momentumPhase;
54 @end
55
56 #endif  // __MAC_OS_X_VERSION_MIN_REQUIRED <= 1060
57
58 #if __MAC_OS_X_VERSION_MIN_REQUIRED == 1050
59
60 // These are not defined in the 10.5 SDK but are defined in later SDKs inside
61 // a MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 #ifdef.
62 enum {
63     NSEventTypeBeginGesture     = 19,
64     NSEventTypeEndGesture       = 20
65 };
66
67 #endif  // __MAC_OS_X_VERSION_MIN_REQUIRED == 1050
68
69 namespace WebKit {
70
71 // WebKeyboardEvent -----------------------------------------------------------
72
73 // ----------------------------------------------------------------------------
74 // Begin Apple code, copied from KeyEventMac.mm
75 //
76 // We can share some of this code if we factored it out of KeyEventMac, but
77 // the main problem is that it relies on the NSString ctor on String for
78 // conversions, and since we're building without PLATFORM(MAC), we don't have
79 // that. As a result we have to use NSString here exclusively and thus tweak
80 // the code so it's not re-usable as-is. One possiblity would be to make the
81 // upstream code only use NSString, but I'm not certain how far that change
82 // would propagate.
83
84 static inline bool isKeyUpEvent(NSEvent* event)
85 {
86     if ([event type] != NSFlagsChanged)
87         return [event type] == NSKeyUp;
88     // FIXME: This logic fails if the user presses both Shift keys at once, for example:
89     // we treat releasing one of them as keyDown.
90     switch ([event keyCode]) {
91     case 54: // Right Command
92     case 55: // Left Command
93         return ([event modifierFlags] & NSCommandKeyMask) == 0;
94
95     case 57: // Capslock
96         return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
97
98     case 56: // Left Shift
99     case 60: // Right Shift
100         return ([event modifierFlags] & NSShiftKeyMask) == 0;
101
102     case 58: // Left Alt
103     case 61: // Right Alt
104         return ([event modifierFlags] & NSAlternateKeyMask) == 0;
105
106     case 59: // Left Ctrl
107     case 62: // Right Ctrl
108         return ([event modifierFlags] & NSControlKeyMask) == 0;
109
110     case 63: // Function
111         return ([event modifierFlags] & NSFunctionKeyMask) == 0;
112     }
113     return false;
114 }
115
116 static bool isKeypadEvent(NSEvent* event)
117 {
118     // Check that this is the type of event that has a keyCode.
119     switch ([event type]) {
120     case NSKeyDown:
121     case NSKeyUp:
122     case NSFlagsChanged:
123         break;
124     default:
125         return false;
126     }
127
128     if ([event modifierFlags] & NSNumericPadKeyMask)
129         return true;
130
131     switch ([event keyCode]) {
132     case 71: // Clear
133     case 81: // =
134     case 75: // /
135     case 67: // *
136     case 78: // -
137     case 69: // +
138     case 76: // Enter
139     case 65: // .
140     case 82: // 0
141     case 83: // 1
142     case 84: // 2
143     case 85: // 3
144     case 86: // 4
145     case 87: // 5
146     case 88: // 6
147     case 89: // 7
148     case 91: // 8
149     case 92: // 9
150         return true;
151     }
152
153     return false;
154 }
155
156 static int windowsKeyCodeForKeyEvent(NSEvent* event)
157 {
158     int code = 0;
159     // There are several kinds of characters for which we produce key code from char code:
160     // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these,
161     //    so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts.
162     // 2. Keys for which there is no known Mac virtual key codes, like PrintScreen.
163     // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout,
164     //    but see comment in windowsKeyCodeForCharCode().
165     if ([event type] == NSKeyDown || [event type] == NSKeyUp) {
166         // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first.
167         NSString* s = [event characters];
168         code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
169         if (code)
170             return code;
171
172         // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[NSEvent keyCode] below.
173         s = [event charactersIgnoringModifiers];
174         code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
175         if (code)
176             return code;
177     }
178
179     // Map Mac virtual key code directly to Windows one for any keys not handled above.
180     // E.g. the key next to Caps Lock has the same Event.keyCode on U.S. keyboard ('A') and on Russian keyboard (CYRILLIC LETTER EF).
181     return WebCore::windowsKeyCodeForKeyCode([event keyCode]);
182 }
183
184 static WebInputEvent::Type gestureEventTypeForEvent(NSEvent *event)
185 {
186     switch ([event type]) {
187     case NSEventTypeBeginGesture:
188         return WebInputEvent::GestureScrollBegin;
189     case NSEventTypeEndGesture:
190         return WebInputEvent::GestureScrollEnd;
191     default:
192         ASSERT_NOT_REACHED();
193         return WebInputEvent::GestureScrollEnd;
194     }
195 }
196
197 static inline NSString* textFromEvent(NSEvent* event)
198 {
199     if ([event type] == NSFlagsChanged)
200         return @"";
201     return [event characters];
202 }
203
204 static inline NSString* unmodifiedTextFromEvent(NSEvent* event)
205 {
206     if ([event type] == NSFlagsChanged)
207         return @"";
208     return [event charactersIgnoringModifiers];
209 }
210
211 static NSString* keyIdentifierForKeyEvent(NSEvent* event)
212 {
213     if ([event type] == NSFlagsChanged) {
214         switch ([event keyCode]) {
215         case 54: // Right Command
216         case 55: // Left Command
217             return @"Meta";
218
219         case 57: // Capslock
220             return @"CapsLock";
221
222         case 56: // Left Shift
223         case 60: // Right Shift
224             return @"Shift";
225
226         case 58: // Left Alt
227         case 61: // Right Alt
228             return @"Alt";
229
230         case 59: // Left Ctrl
231         case 62: // Right Ctrl
232             return @"Control";
233
234 // Begin non-Apple addition/modification --------------------------------------
235         case 63: // Function
236             return @"Function";
237
238         default: // Unknown, but this may be a strange/new keyboard.
239             return @"Unidentified";
240 // End non-Apple addition/modification ----------------------------------------
241         }
242     }
243
244     NSString* s = [event charactersIgnoringModifiers];
245     if ([s length] != 1)
246         return @"Unidentified";
247
248     unichar c = [s characterAtIndex:0];
249     switch (c) {
250     // Each identifier listed in the DOM spec is listed here.
251     // Many are simply commented out since they do not appear on standard Macintosh keyboards
252     // or are on a key that doesn't have a corresponding character.
253
254     // "Accept"
255     // "AllCandidates"
256
257     // "Alt"
258     case NSMenuFunctionKey:
259         return @"Alt";
260
261     // "Apps"
262     // "BrowserBack"
263     // "BrowserForward"
264     // "BrowserHome"
265     // "BrowserRefresh"
266     // "BrowserSearch"
267     // "BrowserStop"
268     // "CapsLock"
269
270     // "Clear"
271     case NSClearLineFunctionKey:
272         return @"Clear";
273
274     // "CodeInput"
275     // "Compose"
276     // "Control"
277     // "Crsel"
278     // "Convert"
279     // "Copy"
280     // "Cut"
281
282     // "Down"
283     case NSDownArrowFunctionKey:
284         return @"Down";
285     // "End"
286     case NSEndFunctionKey:
287         return @"End";
288     // "Enter"
289     case 0x3: case 0xA: case 0xD: // Macintosh calls the one on the main keyboard Return, but Windows calls it Enter, so we'll do the same for the DOM
290         return @"Enter";
291
292     // "EraseEof"
293
294     // "Execute"
295     case NSExecuteFunctionKey:
296         return @"Execute";
297
298     // "Exsel"
299
300     // "F1"
301     case NSF1FunctionKey:
302         return @"F1";
303     // "F2"
304     case NSF2FunctionKey:
305         return @"F2";
306     // "F3"
307     case NSF3FunctionKey:
308         return @"F3";
309     // "F4"
310     case NSF4FunctionKey:
311         return @"F4";
312     // "F5"
313     case NSF5FunctionKey:
314         return @"F5";
315     // "F6"
316     case NSF6FunctionKey:
317         return @"F6";
318     // "F7"
319     case NSF7FunctionKey:
320         return @"F7";
321     // "F8"
322     case NSF8FunctionKey:
323         return @"F8";
324     // "F9"
325     case NSF9FunctionKey:
326         return @"F9";
327     // "F10"
328     case NSF10FunctionKey:
329         return @"F10";
330     // "F11"
331     case NSF11FunctionKey:
332         return @"F11";
333     // "F12"
334     case NSF12FunctionKey:
335         return @"F12";
336     // "F13"
337     case NSF13FunctionKey:
338         return @"F13";
339     // "F14"
340     case NSF14FunctionKey:
341         return @"F14";
342     // "F15"
343     case NSF15FunctionKey:
344         return @"F15";
345     // "F16"
346     case NSF16FunctionKey:
347         return @"F16";
348     // "F17"
349     case NSF17FunctionKey:
350         return @"F17";
351     // "F18"
352     case NSF18FunctionKey:
353         return @"F18";
354     // "F19"
355     case NSF19FunctionKey:
356         return @"F19";
357     // "F20"
358     case NSF20FunctionKey:
359         return @"F20";
360     // "F21"
361     case NSF21FunctionKey:
362         return @"F21";
363     // "F22"
364     case NSF22FunctionKey:
365         return @"F22";
366     // "F23"
367     case NSF23FunctionKey:
368         return @"F23";
369     // "F24"
370     case NSF24FunctionKey:
371         return @"F24";
372
373     // "FinalMode"
374
375     // "Find"
376     case NSFindFunctionKey:
377         return @"Find";
378
379     // "FullWidth"
380     // "HalfWidth"
381     // "HangulMode"
382     // "HanjaMode"
383
384     // "Help"
385     case NSHelpFunctionKey:
386         return @"Help";
387
388     // "Hiragana"
389
390     // "Home"
391     case NSHomeFunctionKey:
392         return @"Home";
393     // "Insert"
394     case NSInsertFunctionKey:
395         return @"Insert";
396
397     // "JapaneseHiragana"
398     // "JapaneseKatakana"
399     // "JapaneseRomaji"
400     // "JunjaMode"
401     // "KanaMode"
402     // "KanjiMode"
403     // "Katakana"
404     // "LaunchApplication1"
405     // "LaunchApplication2"
406     // "LaunchMail"
407
408     // "Left"
409     case NSLeftArrowFunctionKey:
410         return @"Left";
411
412     // "Meta"
413     // "MediaNextTrack"
414     // "MediaPlayPause"
415     // "MediaPreviousTrack"
416     // "MediaStop"
417
418     // "ModeChange"
419     case NSModeSwitchFunctionKey:
420         return @"ModeChange";
421
422     // "Nonconvert"
423     // "NumLock"
424
425     // "PageDown"
426     case NSPageDownFunctionKey:
427         return @"PageDown";
428     // "PageUp"
429     case NSPageUpFunctionKey:
430         return @"PageUp";
431
432     // "Paste"
433
434     // "Pause"
435     case NSPauseFunctionKey:
436         return @"Pause";
437
438     // "Play"
439     // "PreviousCandidate"
440
441     // "PrintScreen"
442     case NSPrintScreenFunctionKey:
443         return @"PrintScreen";
444
445     // "Process"
446     // "Props"
447
448     // "Right"
449     case NSRightArrowFunctionKey:
450         return @"Right";
451
452     // "RomanCharacters"
453
454     // "Scroll"
455     case NSScrollLockFunctionKey:
456         return @"Scroll";
457     // "Select"
458     case NSSelectFunctionKey:
459         return @"Select";
460
461     // "SelectMedia"
462     // "Shift"
463
464     // "Stop"
465     case NSStopFunctionKey:
466         return @"Stop";
467     // "Up"
468     case NSUpArrowFunctionKey:
469         return @"Up";
470     // "Undo"
471     case NSUndoFunctionKey:
472         return @"Undo";
473
474     // "VolumeDown"
475     // "VolumeMute"
476     // "VolumeUp"
477     // "Win"
478     // "Zoom"
479
480     // More function keys, not in the key identifier specification.
481     case NSF25FunctionKey:
482         return @"F25";
483     case NSF26FunctionKey:
484         return @"F26";
485     case NSF27FunctionKey:
486         return @"F27";
487     case NSF28FunctionKey:
488         return @"F28";
489     case NSF29FunctionKey:
490         return @"F29";
491     case NSF30FunctionKey:
492         return @"F30";
493     case NSF31FunctionKey:
494         return @"F31";
495     case NSF32FunctionKey:
496         return @"F32";
497     case NSF33FunctionKey:
498         return @"F33";
499     case NSF34FunctionKey:
500         return @"F34";
501     case NSF35FunctionKey:
502         return @"F35";
503
504     // Turn 0x7F into 0x08, because backspace needs to always be 0x08.
505     case 0x7F:
506         return @"U+0008";
507     // Standard says that DEL becomes U+007F.
508     case NSDeleteFunctionKey:
509         return @"U+007F";
510
511     // Always use 0x09 for tab instead of AppKit's backtab character.
512     case NSBackTabCharacter:
513         return @"U+0009";
514
515     case NSBeginFunctionKey:
516     case NSBreakFunctionKey:
517     case NSClearDisplayFunctionKey:
518     case NSDeleteCharFunctionKey:
519     case NSDeleteLineFunctionKey:
520     case NSInsertCharFunctionKey:
521     case NSInsertLineFunctionKey:
522     case NSNextFunctionKey:
523     case NSPrevFunctionKey:
524     case NSPrintFunctionKey:
525     case NSRedoFunctionKey:
526     case NSResetFunctionKey:
527     case NSSysReqFunctionKey:
528     case NSSystemFunctionKey:
529     case NSUserFunctionKey:
530         // FIXME: We should use something other than the vendor-area Unicode values for the above keys.
531         // For now, just fall through to the default.
532     default:
533         return [NSString stringWithFormat:@"U+%04X", WTF::toASCIIUpper(c)];
534     }
535 }
536
537 // End Apple code.
538 // ----------------------------------------------------------------------------
539
540 static inline int modifiersFromEvent(NSEvent* event) {
541     int modifiers = 0;
542
543     if ([event modifierFlags] & NSControlKeyMask)
544         modifiers |= WebInputEvent::ControlKey;
545     if ([event modifierFlags] & NSShiftKeyMask)
546         modifiers |= WebInputEvent::ShiftKey;
547     if ([event modifierFlags] & NSAlternateKeyMask)
548         modifiers |= WebInputEvent::AltKey;
549     if ([event modifierFlags] & NSCommandKeyMask)
550         modifiers |= WebInputEvent::MetaKey;
551     if ([event modifierFlags] & NSAlphaShiftKeyMask)
552         modifiers |= WebInputEvent::CapsLockOn;
553     // TODO(port): Set mouse button states
554
555     return modifiers;
556 }
557
558 static inline void setWebEventLocationFromEventInView(WebMouseEvent* result,
559                                                       NSEvent* event,
560                                                       NSView* view) {
561     NSPoint windowLocal = [event locationInWindow];
562
563     NSPoint screenLocal = [[view window] convertBaseToScreen:windowLocal];
564     result->globalX = screenLocal.x;
565     // Flip y.
566     NSScreen* primaryScreen = ([[NSScreen screens] count] > 0) ?
567         [[NSScreen screens] objectAtIndex:0] : nil;
568     if (primaryScreen)
569         result->globalY = [primaryScreen frame].size.height - screenLocal.y;
570     else
571         result->globalY = screenLocal.y;
572
573     NSPoint contentLocal = [view convertPoint:windowLocal fromView:nil];
574     result->x = contentLocal.x;
575     result->y = [view frame].size.height - contentLocal.y;  // Flip y.
576
577     result->windowX = result->x;
578     result->windowY = result->y;
579
580     result->movementX = [event deltaX];
581     result->movementY = [event deltaY];
582 }
583
584 WebKeyboardEvent WebInputEventFactory::keyboardEvent(NSEvent* event)
585 {
586     WebKeyboardEvent result;
587
588     result.type =
589         isKeyUpEvent(event) ? WebInputEvent::KeyUp : WebInputEvent::RawKeyDown;
590
591     result.modifiers = modifiersFromEvent(event);
592
593     if (isKeypadEvent(event))
594         result.modifiers |= WebInputEvent::IsKeyPad;
595
596     if (([event type] != NSFlagsChanged) && [event isARepeat])
597         result.modifiers |= WebInputEvent::IsAutoRepeat;
598
599     result.windowsKeyCode = windowsKeyCodeForKeyEvent(event);
600     result.nativeKeyCode = [event keyCode];
601
602     NSString* textStr = textFromEvent(event);
603     NSString* unmodifiedStr = unmodifiedTextFromEvent(event);
604     NSString* identifierStr = keyIdentifierForKeyEvent(event);
605
606     // Begin Apple code, copied from KeyEventMac.mm
607
608     // Always use 13 for Enter/Return -- we don't want to use AppKit's
609     // different character for Enter.
610     if (result.windowsKeyCode == '\r') {
611         textStr = @"\r";
612         unmodifiedStr = @"\r";
613     }
614
615     // The adjustments below are only needed in backward compatibility mode,
616     // but we cannot tell what mode we are in from here.
617
618     // Turn 0x7F into 8, because backspace needs to always be 8.
619     if ([textStr isEqualToString:@"\x7F"])
620         textStr = @"\x8";
621     if ([unmodifiedStr isEqualToString:@"\x7F"])
622         unmodifiedStr = @"\x8";
623     // Always use 9 for tab -- we don't want to use AppKit's different character
624     // for shift-tab.
625     if (result.windowsKeyCode == 9) {
626         textStr = @"\x9";
627         unmodifiedStr = @"\x9";
628     }
629
630     // End Apple code.
631
632     if ([textStr length] < WebKeyboardEvent::textLengthCap &&
633         [unmodifiedStr length] < WebKeyboardEvent::textLengthCap) {
634         [textStr getCharacters:&result.text[0]];
635         [unmodifiedStr getCharacters:&result.unmodifiedText[0]];
636     } else
637         ASSERT_NOT_REACHED();
638
639     [identifierStr getCString:&result.keyIdentifier[0]
640                     maxLength:sizeof(result.keyIdentifier)
641                      encoding:NSASCIIStringEncoding];
642
643     result.timeStampSeconds = [event timestamp];
644
645     // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
646     // flag to decide if it should handle a key or not. E.g. alt-left/right
647     // shouldn't be used by WebKit to scroll the current page, because we want
648     // to get that key back for it to do history navigation. Hence, the
649     // corresponding situation on OS X is to set this for cmd key presses.
650     if (result.modifiers & WebInputEvent::MetaKey)
651         result.isSystemKey = true;
652
653     return result;
654 }
655
656 WebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character,
657                                                      int modifiers,
658                                                      double timeStampSeconds)
659 {
660     // keyboardEvent(NSEvent*) depends on the NSEvent object and
661     // it is hard to use it from methods of the NSTextInput protocol. For
662     // such methods, this function creates a WebInputEvent::Char event without
663     // using a NSEvent object.
664     WebKeyboardEvent result;
665     result.type = WebKit::WebInputEvent::Char;
666     result.timeStampSeconds = timeStampSeconds;
667     result.modifiers = modifiers;
668     result.windowsKeyCode = character;
669     result.nativeKeyCode = character;
670     result.text[0] = character;
671     result.unmodifiedText[0] = character;
672
673     // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
674     // flag to decide if it should handle a key or not. E.g. alt-left/right
675     // shouldn't be used by WebKit to scroll the current page, because we want
676     // to get that key back for it to do history navigation. Hence, the
677     // corresponding situation on OS X is to set this for cmd key presses.
678     if (result.modifiers & WebInputEvent::MetaKey)
679         result.isSystemKey = true;
680
681     return result;
682 }
683
684 // WebMouseEvent --------------------------------------------------------------
685
686 WebMouseEvent WebInputEventFactory::mouseEvent(NSEvent* event, NSView* view)
687 {
688     WebMouseEvent result;
689
690     result.clickCount = 0;
691
692     switch ([event type]) {
693     case NSMouseExited:
694         result.type = WebInputEvent::MouseLeave;
695         result.button = WebMouseEvent::ButtonNone;
696         break;
697     case NSLeftMouseDown:
698         result.type = WebInputEvent::MouseDown;
699         result.clickCount = [event clickCount];
700         result.button = WebMouseEvent::ButtonLeft;
701         break;
702     case NSOtherMouseDown:
703         result.type = WebInputEvent::MouseDown;
704         result.clickCount = [event clickCount];
705         result.button = WebMouseEvent::ButtonMiddle;
706         break;
707     case NSRightMouseDown:
708         result.type = WebInputEvent::MouseDown;
709         result.clickCount = [event clickCount];
710         result.button = WebMouseEvent::ButtonRight;
711         break;
712     case NSLeftMouseUp:
713         result.type = WebInputEvent::MouseUp;
714         result.clickCount = [event clickCount];
715         result.button = WebMouseEvent::ButtonLeft;
716         break;
717     case NSOtherMouseUp:
718         result.type = WebInputEvent::MouseUp;
719         result.clickCount = [event clickCount];
720         result.button = WebMouseEvent::ButtonMiddle;
721         break;
722     case NSRightMouseUp:
723         result.type = WebInputEvent::MouseUp;
724         result.clickCount = [event clickCount];
725         result.button = WebMouseEvent::ButtonRight;
726         break;
727     case NSMouseMoved:
728     case NSMouseEntered:
729         result.type = WebInputEvent::MouseMove;
730         break;
731     case NSLeftMouseDragged:
732         result.type = WebInputEvent::MouseMove;
733         result.button = WebMouseEvent::ButtonLeft;
734         break;
735     case NSOtherMouseDragged:
736         result.type = WebInputEvent::MouseMove;
737         result.button = WebMouseEvent::ButtonMiddle;
738         break;
739     case NSRightMouseDragged:
740         result.type = WebInputEvent::MouseMove;
741         result.button = WebMouseEvent::ButtonRight;
742         break;
743     default:
744         ASSERT_NOT_REACHED();
745     }
746
747     setWebEventLocationFromEventInView(&result, event, view);
748
749     result.modifiers = modifiersFromEvent(event);
750
751     result.timeStampSeconds = [event timestamp];
752
753     return result;
754 }
755
756 // WebMouseWheelEvent ---------------------------------------------------------
757
758 static WebMouseWheelEvent::Phase phaseForNSEventPhase(NSEventPhase eventPhase)
759 {
760     uint32_t phase = WebMouseWheelEvent::PhaseNone; 
761     if (eventPhase & NSEventPhaseBegan)
762         phase |= WebMouseWheelEvent::PhaseBegan;
763     if (eventPhase & NSEventPhaseStationary)
764         phase |= WebMouseWheelEvent::PhaseStationary;
765     if (eventPhase & NSEventPhaseChanged)
766         phase |= WebMouseWheelEvent::PhaseChanged;
767     if (eventPhase & NSEventPhaseEnded)
768         phase |= WebMouseWheelEvent::PhaseEnded;
769     if (eventPhase & NSEventPhaseCancelled)
770         phase |= WebMouseWheelEvent::PhaseCancelled;
771     return static_cast<WebMouseWheelEvent::Phase>(phase);
772 }
773
774 static WebMouseWheelEvent::Phase phaseForEvent(NSEvent *event)
775 {
776     if (![event respondsToSelector:@selector(phase)])
777         return WebMouseWheelEvent::PhaseNone;
778
779     NSEventPhase eventPhase = [event phase];
780     return phaseForNSEventPhase(eventPhase);
781 }
782
783 static WebMouseWheelEvent::Phase momentumPhaseForEvent(NSEvent *event)
784 {
785     if (![event respondsToSelector:@selector(momentumPhase)])
786         return WebMouseWheelEvent::PhaseNone;
787
788     NSEventPhase eventMomentumPhase = [event momentumPhase];
789     return phaseForNSEventPhase(eventMomentumPhase);
790 }
791
792 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(NSEvent* event, NSView* view)
793 {
794     WebMouseWheelEvent result;
795
796     result.type = WebInputEvent::MouseWheel;
797     result.button = WebMouseEvent::ButtonNone;
798
799     result.modifiers = modifiersFromEvent(event);
800
801     setWebEventLocationFromEventInView(&result, event, view);
802
803     // Of Mice and Men
804     // ---------------
805     //
806     // There are three types of scroll data available on a scroll wheel CGEvent.
807     // Apple's documentation ([1]) is rather vague in their differences, and not
808     // terribly helpful in deciding which to use. This is what's really going on.
809     //
810     // First, these events behave very differently depending on whether a standard
811     // wheel mouse is used (one that scrolls in discrete units) or a
812     // trackpad/Mighty Mouse is used (which both provide continuous scrolling).
813     // You must check to see which was used for the event by testing the
814     // kCGScrollWheelEventIsContinuous field.
815     //
816     // Second, these events refer to "axes". Axis 1 is the y-axis, and axis 2 is
817     // the x-axis.
818     //
819     // Third, there is a concept of mouse acceleration. Scrolling the same amount
820     // of physical distance will give you different results logically depending on
821     // whether you scrolled a little at a time or in one continuous motion. Some
822     // fields account for this while others do not.
823     //
824     // Fourth, for trackpads there is a concept of chunkiness. When scrolling
825     // continuously, events can be delivered in chunks. That is to say, lots of
826     // scroll events with delta 0 will be delivered, and every so often an event
827     // with a non-zero delta will be delivered, containing the accumulated deltas
828     // from all the intermediate moves. [2]
829     //
830     // For notchy wheel mice (kCGScrollWheelEventIsContinuous == 0)
831     // ------------------------------------------------------------
832     //
833     // kCGScrollWheelEventDeltaAxis*
834     //   This is the rawest of raw events. For each mouse notch you get a value of
835     //   +1/-1. This does not take acceleration into account and thus is less
836     //   useful for building UIs.
837     //
838     // kCGScrollWheelEventPointDeltaAxis*
839     //   This is smarter. In general, for each mouse notch you get a value of
840     //   +1/-1, but this _does_ take acceleration into account, so you will get
841     //   larger values on longer scrolls. This field would be ideal for building
842     //   UIs except for one nasty bug: when the shift key is pressed, this set of
843     //   fields fails to move the value into the axis2 field (the other two types
844     //   of data do). This wouldn't be so bad except for the fact that while the
845     //   number of axes is used in the creation of a CGScrollWheelEvent, there is
846     //   no way to get that information out of the event once created.
847     //
848     // kCGScrollWheelEventFixedPtDeltaAxis*
849     //   This is a fixed value, and for each mouse notch you get a value of
850     //   +0.1/-0.1 (but, like above, scaled appropriately for acceleration). This
851     //   value takes acceleration into account, and in fact is identical to the
852     //   results you get from -[NSEvent delta*]. (That is, if you linked on Tiger
853     //   or greater; see [2] for details.)
854     //
855     // A note about continuous devices
856     // -------------------------------
857     //
858     // There are two devices that provide continuous scrolling events (trackpads
859     // and Mighty Mouses) and they behave rather differently. The Mighty Mouse
860     // behaves a lot like a regular mouse. There is no chunking, and the
861     // FixedPtDelta values are the PointDelta values multiplied by 0.1. With the
862     // trackpad, though, there is chunking. While the FixedPtDelta values are
863     // reasonable (they occur about every fifth event but have values five times
864     // larger than usual) the Delta values are unreasonable. They don't appear to
865     // accumulate properly.
866     //
867     // For continuous devices (kCGScrollWheelEventIsContinuous != 0)
868     // -------------------------------------------------------------
869     //
870     // kCGScrollWheelEventDeltaAxis*
871     //   This provides values with no acceleration. With a trackpad, these values
872     //   are chunked but each non-zero value does not appear to be cumulative.
873     //   This seems to be a bug.
874     //
875     // kCGScrollWheelEventPointDeltaAxis*
876     //   This provides values with acceleration. With a trackpad, these values are
877     //   not chunked and are highly accurate.
878     //
879     // kCGScrollWheelEventFixedPtDeltaAxis*
880     //   This provides values with acceleration. With a trackpad, these values are
881     //   chunked but unlike Delta events are properly cumulative.
882     //
883     // Summary
884     // -------
885     //
886     // In general the best approach to take is: determine if the event is
887     // continuous. If it is not, then use the FixedPtDelta events (or just stick
888     // with Cocoa events). They provide both acceleration and proper horizontal
889     // scrolling. If the event is continuous, then doing pixel scrolling with the
890     // PointDelta is the way to go. In general, avoid the Delta events. They're
891     // the oldest (dating back to 10.4, before CGEvents were public) but they lack
892     // acceleration and precision, making them useful only in specific edge cases.
893     //
894     // References
895     // ----------
896     //
897     // [1] <http://developer.apple.com/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html>
898     // [2] <http://developer.apple.com/releasenotes/Cocoa/AppKitOlderNotes.html>
899     //     Scroll to the section headed "NSScrollWheel events".
900     //
901     // P.S. The "smooth scrolling" option in the system preferences is utterly
902     // unrelated to any of this.
903
904     CGEventRef cgEvent = [event CGEvent];
905     ASSERT(cgEvent);
906
907     // Wheel ticks are supposed to be raw, unaccelerated values, one per physical
908     // mouse wheel notch. The delta event is perfect for this (being a good
909     // "specific edge case" as mentioned above). Trackpads, unfortunately, do
910     // event chunking, and sending mousewheel events with 0 ticks causes some
911     // websites to malfunction. Therefore, for all continuous input devices we use
912     // the point delta data instead, since we cannot distinguish trackpad data
913     // from data from any other continuous device.
914
915     // Conversion between wheel delta amounts and number of pixels to scroll.
916     static const double scrollbarPixelsPerCocoaTick = 40.0;
917
918     if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) {
919         result.deltaX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
920         result.deltaY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
921         result.wheelTicksX = result.deltaX / scrollbarPixelsPerCocoaTick;
922         result.wheelTicksY = result.deltaY / scrollbarPixelsPerCocoaTick;
923         result.hasPreciseScrollingDeltas = true;
924     } else {
925         result.deltaX = [event deltaX] * scrollbarPixelsPerCocoaTick;
926         result.deltaY = [event deltaY] * scrollbarPixelsPerCocoaTick;
927         result.wheelTicksY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis1);
928         result.wheelTicksX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis2);
929     }
930
931     result.timeStampSeconds = [event timestamp];
932
933     result.phase              = phaseForEvent(event);
934     result.momentumPhase      = momentumPhaseForEvent(event);
935
936     return result;
937 }
938
939 WebGestureEvent WebInputEventFactory::gestureEvent(NSEvent *event, NSView *view)
940 {
941     WebGestureEvent result;
942
943     // Use a temporary WebMouseEvent to get the location.
944     WebMouseEvent temp;
945
946     setWebEventLocationFromEventInView(&temp, event, view);
947     result.x = temp.x;
948     result.y = temp.y;
949     result.globalX = temp.globalX;
950     result.globalY = temp.globalY;
951
952     result.type = gestureEventTypeForEvent(event);
953     result.modifiers = modifiersFromEvent(event);
954     result.timeStampSeconds = [event timestamp];
955
956     return result;
957 }
958
959 } // namespace WebKit