Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / events / test / cocoa_test_event_utils.mm
blob870d9303af56caa4e86076d6712f0fd8e1597ede
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
6 #include <mach/mach_time.h>
8 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
9 #include "ui/events/test/cocoa_test_event_utils.h"
11 namespace cocoa_test_event_utils {
13 namespace {
15 // From
16 // http://stackoverflow.com/questions/1597383/cgeventtimestamp-to-nsdate
17 // Which credits Apple sample code for this routine.
18 uint64_t UpTimeInNanoseconds(void) {
19   uint64_t time;
20   uint64_t timeNano;
21   static mach_timebase_info_data_t sTimebaseInfo;
23   time = mach_absolute_time();
25   // Convert to nanoseconds.
27   // If this is the first time we've run, get the timebase.
28   // We can use denom == 0 to indicate that sTimebaseInfo is
29   // uninitialised because it makes no sense to have a zero
30   // denominator is a fraction.
31   if (sTimebaseInfo.denom == 0) {
32     (void) mach_timebase_info(&sTimebaseInfo);
33   }
35   // This could overflow; for testing needs we probably don't care.
36   timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;
37   return timeNano;
40 }  // namespace
42 NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type,
43                            NSUInteger modifiers) {
44   if (type == NSOtherMouseUp) {
45     // To synthesize middle clicks we need to create a CGEvent with the
46     // "center" button flags so that our resulting NSEvent will have the
47     // appropriate buttonNumber field. NSEvent provides no way to create a
48     // mouse event with a buttonNumber directly.
49     CGPoint location = { point.x, point.y };
50     CGEventRef cg_event = CGEventCreateMouseEvent(NULL, kCGEventOtherMouseUp,
51                                                   location,
52                                                   kCGMouseButtonCenter);
53     // Also specify the modifiers for the middle click case. This makes this
54     // test resilient to external modifiers being pressed.
55     CGEventSetFlags(cg_event, modifiers);
56     NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
57     CFRelease(cg_event);
58     return event;
59   }
60   return [NSEvent mouseEventWithType:type
61                             location:point
62                        modifierFlags:modifiers
63                            timestamp:0
64                         windowNumber:0
65                              context:nil
66                          eventNumber:0
67                           clickCount:1
68                             pressure:1.0];
71 NSEvent* MouseEventWithType(NSEventType type, NSUInteger modifiers) {
72   return MouseEventAtPoint(NSZeroPoint, type, modifiers);
75 NSEvent* MouseEventAtPointInWindow(NSPoint point,
76                                    NSEventType type,
77                                    NSWindow* window,
78                                    NSUInteger clickCount) {
79   return [NSEvent mouseEventWithType:type
80                             location:point
81                        modifierFlags:0
82                            timestamp:0
83                         windowNumber:[window windowNumber]
84                              context:nil
85                          eventNumber:0
86                           clickCount:clickCount
87                             pressure:1.0];
90 NSEvent* RightMouseDownAtPointInWindow(NSPoint point, NSWindow* window) {
91   return MouseEventAtPointInWindow(point, NSRightMouseDown, window, 1);
94 NSEvent* RightMouseDownAtPoint(NSPoint point) {
95   return RightMouseDownAtPointInWindow(point, nil);
98 NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window) {
99   return MouseEventAtPointInWindow(point, NSLeftMouseDown, window, 1);
102 NSEvent* LeftMouseDownAtPoint(NSPoint point) {
103   return LeftMouseDownAtPointInWindow(point, nil);
106 std::pair<NSEvent*,NSEvent*> MouseClickInView(NSView* view,
107                                               NSUInteger clickCount) {
108   const NSRect bounds = [view convertRect:[view bounds] toView:nil];
109   const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
110   NSEvent* down = MouseEventAtPointInWindow(mid_point, NSLeftMouseDown,
111                                             [view window], clickCount);
112   NSEvent* up = MouseEventAtPointInWindow(mid_point, NSLeftMouseUp,
113                                           [view window], clickCount);
114   return std::make_pair(down, up);
117 std::pair<NSEvent*, NSEvent*> RightMouseClickInView(NSView* view,
118                                                     NSUInteger clickCount) {
119   const NSRect bounds = [view convertRect:[view bounds] toView:nil];
120   const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
121   NSEvent* down = MouseEventAtPointInWindow(mid_point, NSRightMouseDown,
122                                             [view window], clickCount);
123   NSEvent* up = MouseEventAtPointInWindow(mid_point, NSRightMouseUp,
124                                           [view window], clickCount);
125   return std::make_pair(down, up);
128 NSEvent* KeyEventWithCharacter(unichar c) {
129   return KeyEventWithKeyCode(0, c, NSKeyDown, 0);
132 NSEvent* KeyEventWithType(NSEventType event_type, NSUInteger modifiers) {
133   return KeyEventWithKeyCode(0x78, 'x', event_type, modifiers);
136 NSEvent* KeyEventWithKeyCode(unsigned short key_code,
137                              unichar c,
138                              NSEventType event_type,
139                              NSUInteger modifiers) {
140   NSString* chars = [NSString stringWithCharacters:&c length:1];
141   return [NSEvent keyEventWithType:event_type
142                           location:NSZeroPoint
143                      modifierFlags:modifiers
144                          timestamp:0
145                       windowNumber:0
146                            context:nil
147                         characters:chars
148        charactersIgnoringModifiers:chars
149                          isARepeat:NO
150                            keyCode:key_code];
153 NSEvent* EnterExitEventWithType(NSEventType event_type) {
154   return [NSEvent enterExitEventWithType:event_type
155                                 location:NSZeroPoint
156                            modifierFlags:0
157                                timestamp:0
158                             windowNumber:0
159                                  context:nil
160                              eventNumber:0
161                           trackingNumber:0
162                                 userData:NULL];
165 NSEvent* OtherEventWithType(NSEventType event_type) {
166   return [NSEvent otherEventWithType:event_type
167                             location:NSZeroPoint
168                        modifierFlags:0
169                            timestamp:0
170                         windowNumber:0
171                              context:nil
172                              subtype:0
173                                data1:0
174                                data2:0];
177 NSTimeInterval TimeIntervalSinceSystemStartup() {
178   return UpTimeInNanoseconds() / 1000000000.0;
181 NSEvent* SynthesizeKeyEvent(NSWindow* window,
182                             bool keyDown,
183                             ui::KeyboardCode keycode,
184                             NSUInteger flags) {
185   // If caps lock is set for an alpha keycode, treat it as if shift was pressed.
186   // Note on Mac (unlike other platforms) shift while caps is down does not go
187   // back to lowercase.
188   if (keycode >= ui::VKEY_A && keycode <= ui::VKEY_Z &&
189       (flags & NSAlphaShiftKeyMask))
190     flags |= NSShiftKeyMask;
192   // Clear caps regardless -- MacKeyCodeForWindowsKeyCode doesn't implement
193   // logic to support it.
194   flags &= ~NSAlphaShiftKeyMask;
196   unichar character;
197   unichar shifted_character;
198   int macKeycode = ui::MacKeyCodeForWindowsKeyCode(
199       keycode, flags, &shifted_character, &character);
201   if (macKeycode < 0)
202     return nil;
204   // Note that, in line with AppKit's documentation (and tracing "real" events),
205   // -[NSEvent charactersIngoringModifiers]" are "the characters generated by
206   // the receiving key event as if no modifier key (except for Shift)".
207   // So |charactersIgnoringModifiers| uses |shifted_character|.
208   NSString* charactersIgnoringModifiers =
209       [[[NSString alloc] initWithCharacters:&shifted_character
210                                      length:1] autorelease];
211   NSString* characters;
212   // The following were determined empirically on OSX 10.9.
213   if (flags & NSControlKeyMask) {
214     // If Ctrl is pressed, Cocoa always puts an empty string into |characters|.
215     characters = [NSString string];
216   } else if (flags & NSCommandKeyMask) {
217     // If Cmd is pressed, Cocoa puts a lowercase character into |characters|,
218     // regardless of Shift. If, however, Alt is also pressed then shift *is*
219     // preserved, but re-mappings for Alt are not implemented. Although we still
220     // need to support Alt for things like Alt+Left/Right which don't care.
221     characters =
222         [[[NSString alloc] initWithCharacters:&character length:1] autorelease];
223   } else {
224     // If just Shift or nothing is pressed, |characters| will match
225     // |charactersIgnoringModifiers|. Alt puts a special character into
226     // |characters| (not |charactersIgnoringModifiers|), but they're not mapped
227     // here.
228     characters = charactersIgnoringModifiers;
229   }
231   NSEventType type = (keyDown ? NSKeyDown : NSKeyUp);
233   // Modifier keys generate NSFlagsChanged event rather than
234   // NSKeyDown/NSKeyUp events.
235   if (keycode == ui::VKEY_CONTROL || keycode == ui::VKEY_SHIFT ||
236       keycode == ui::VKEY_MENU || keycode == ui::VKEY_COMMAND)
237     type = NSFlagsChanged;
239   // For events other than mouse moved, [event locationInWindow] is
240   // UNDEFINED if the event is not NSMouseMoved.  Thus, the (0,0)
241   // location should be fine.
242   NSEvent* event = [NSEvent keyEventWithType:type
243                                     location:NSZeroPoint
244                                modifierFlags:flags
245                                    timestamp:TimeIntervalSinceSystemStartup()
246                                 windowNumber:[window windowNumber]
247                                      context:nil
248                                   characters:characters
249                  charactersIgnoringModifiers:charactersIgnoringModifiers
250                                    isARepeat:NO
251                                      keyCode:(unsigned short)macKeycode];
253   return event;
256 }  // namespace cocoa_test_event_utils