| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/test/ui_controls.h" | 5 #include "ui/base/test/ui_controls.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #include <mach/mach_time.h> | |
| 9 #include <vector> | 8 #include <vector> |
| 10 | 9 |
| 11 #include "base/bind.h" | 10 #include "base/bind.h" |
| 12 #include "base/callback.h" | 11 #include "base/callback.h" |
| 13 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
| 14 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" | 13 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" |
| 15 | 14 #import "ui/events/test/cocoa_test_event_utils.h" |
| 16 | 15 |
| 17 // Implementation details: We use [NSApplication sendEvent:] instead | 16 // Implementation details: We use [NSApplication sendEvent:] instead |
| 18 // of [NSApplication postEvent:atStart:] so that the event gets sent | 17 // of [NSApplication postEvent:atStart:] so that the event gets sent |
| 19 // immediately. This lets us run the post-event task right | 18 // immediately. This lets us run the post-event task right |
| 20 // immediately as well. Unfortunately I cannot subclass NSEvent (it's | 19 // immediately as well. Unfortunately I cannot subclass NSEvent (it's |
| 21 // probably a class cluster) to allow other easy answers. For | 20 // probably a class cluster) to allow other easy answers. For |
| 22 // example, if I could subclass NSEvent, I could run the Task in it's | 21 // example, if I could subclass NSEvent, I could run the Task in it's |
| 23 // dealloc routine (which necessarily happens after the event is | 22 // dealloc routine (which necessarily happens after the event is |
| 24 // dispatched). Unlike Linux, Mac does not have message loop | 23 // dispatched). Unlike Linux, Mac does not have message loop |
| 25 // observer/notification. Unlike windows, I cannot post non-events | 24 // observer/notification. Unlike windows, I cannot post non-events |
| (...skipping 10 matching lines...) Expand all Loading... |
| 36 // processed, we setup a task to watch for the event queue time to time and | 35 // processed, we setup a task to watch for the event queue time to time and |
| 37 // notify the caller as soon as there is no event in the queue. | 36 // notify the caller as soon as there is no event in the queue. |
| 38 // | 37 // |
| 39 // TODO(suzhe): | 38 // TODO(suzhe): |
| 40 // 1. Investigate why using [NSApplication postEvent:atStart:] for keyboard | 39 // 1. Investigate why using [NSApplication postEvent:atStart:] for keyboard |
| 41 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. | 40 // events causes BrowserKeyEventsTest.CommandKeyEvents to fail. |
| 42 // See http://crbug.com/49270 | 41 // See http://crbug.com/49270 |
| 43 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may | 42 // 2. On OSX 10.6, [NSEvent addLocalMonitorForEventsMatchingMask:handler:] may |
| 44 // be used, so that we don't need to poll the event queue time to time. | 43 // be used, so that we don't need to poll the event queue time to time. |
| 45 | 44 |
| 45 using cocoa_test_event_utils::SynthesizeKeyEvent; |
| 46 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; |
| 47 |
| 46 namespace { | 48 namespace { |
| 47 | 49 |
| 48 // Stores the current mouse location on the screen. So that we can use it | 50 // Stores the current mouse location on the screen. So that we can use it |
| 49 // when firing keyboard and mouse click events. | 51 // when firing keyboard and mouse click events. |
| 50 NSPoint g_mouse_location = { 0, 0 }; | 52 NSPoint g_mouse_location = { 0, 0 }; |
| 51 | 53 |
| 52 bool g_ui_controls_enabled = false; | 54 bool g_ui_controls_enabled = false; |
| 53 | 55 |
| 54 // From | |
| 55 // http://stackoverflow.com/questions/1597383/cgeventtimestamp-to-nsdate | |
| 56 // Which credits Apple sample code for this routine. | |
| 57 uint64_t UpTimeInNanoseconds(void) { | |
| 58 uint64_t time; | |
| 59 uint64_t timeNano; | |
| 60 static mach_timebase_info_data_t sTimebaseInfo; | |
| 61 | |
| 62 time = mach_absolute_time(); | |
| 63 | |
| 64 // Convert to nanoseconds. | |
| 65 | |
| 66 // If this is the first time we've run, get the timebase. | |
| 67 // We can use denom == 0 to indicate that sTimebaseInfo is | |
| 68 // uninitialised because it makes no sense to have a zero | |
| 69 // denominator is a fraction. | |
| 70 if (sTimebaseInfo.denom == 0) { | |
| 71 (void) mach_timebase_info(&sTimebaseInfo); | |
| 72 } | |
| 73 | |
| 74 // This could overflow; for testing needs we probably don't care. | |
| 75 timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom; | |
| 76 return timeNano; | |
| 77 } | |
| 78 | |
| 79 NSTimeInterval TimeIntervalSinceSystemStartup() { | |
| 80 return UpTimeInNanoseconds() / 1000000000.0; | |
| 81 } | |
| 82 | |
| 83 // Creates and returns an autoreleased key event. | |
| 84 NSEvent* SynthesizeKeyEvent(NSWindow* window, | |
| 85 bool keyDown, | |
| 86 ui::KeyboardCode keycode, | |
| 87 NSUInteger flags) { | |
| 88 unichar character; | |
| 89 unichar shifted_character; | |
| 90 int macKeycode = ui::MacKeyCodeForWindowsKeyCode( | |
| 91 keycode, flags, &shifted_character, &character); | |
| 92 | |
| 93 if (macKeycode < 0) | |
| 94 return nil; | |
| 95 | |
| 96 // Note that, in line with AppKit's documentation (and tracing "real" events), | |
| 97 // -[NSEvent charactersIngoringModifiers]" are "the characters generated by | |
| 98 // the receiving key event as if no modifier key (except for Shift)". | |
| 99 // So |charactersIgnoringModifiers| uses |shifted_character|. | |
| 100 NSString* charactersIgnoringModifiers = | |
| 101 [[[NSString alloc] initWithCharacters:&shifted_character | |
| 102 length:1] autorelease]; | |
| 103 NSString* characters; | |
| 104 // The following were determined empirically on OSX 10.9. | |
| 105 if (flags & NSControlKeyMask) { | |
| 106 // If Ctrl is pressed, Cocoa always puts an empty string into |characters|. | |
| 107 characters = [NSString string]; | |
| 108 } else if (flags & NSCommandKeyMask) { | |
| 109 // If Cmd is pressed, Cocoa puts a lowercase character into |characters|, | |
| 110 // regardless of Shift. If, however, Alt is also pressed then shift *is* | |
| 111 // preserved, but re-mappings for Alt are not implemented. Although we still | |
| 112 // need to support Alt for things like Alt+Left/Right which don't care. | |
| 113 characters = | |
| 114 [[[NSString alloc] initWithCharacters:&character length:1] autorelease]; | |
| 115 } else { | |
| 116 // If just Shift or nothing is pressed, |characters| will match | |
| 117 // |charactersIgnoringModifiers|. Alt puts a special character into | |
| 118 // |characters| (not |charactersIgnoringModifiers|), but they're not mapped | |
| 119 // here. | |
| 120 characters = charactersIgnoringModifiers; | |
| 121 } | |
| 122 | |
| 123 NSEventType type = (keyDown ? NSKeyDown : NSKeyUp); | |
| 124 | |
| 125 // Modifier keys generate NSFlagsChanged event rather than | |
| 126 // NSKeyDown/NSKeyUp events. | |
| 127 if (keycode == ui::VKEY_CONTROL || keycode == ui::VKEY_SHIFT || | |
| 128 keycode == ui::VKEY_MENU || keycode == ui::VKEY_COMMAND) | |
| 129 type = NSFlagsChanged; | |
| 130 | |
| 131 // For events other than mouse moved, [event locationInWindow] is | |
| 132 // UNDEFINED if the event is not NSMouseMoved. Thus, the (0,0) | |
| 133 // location should be fine. | |
| 134 NSEvent* event = [NSEvent keyEventWithType:type | |
| 135 location:NSZeroPoint | |
| 136 modifierFlags:flags | |
| 137 timestamp:TimeIntervalSinceSystemStartup() | |
| 138 windowNumber:[window windowNumber] | |
| 139 context:nil | |
| 140 characters:characters | |
| 141 charactersIgnoringModifiers:charactersIgnoringModifiers | |
| 142 isARepeat:NO | |
| 143 keyCode:(unsigned short)macKeycode]; | |
| 144 | |
| 145 return event; | |
| 146 } | |
| 147 | |
| 148 // Creates the proper sequence of autoreleased key events for a key down + up. | 56 // Creates the proper sequence of autoreleased key events for a key down + up. |
| 149 void SynthesizeKeyEventsSequence(NSWindow* window, | 57 void SynthesizeKeyEventsSequence(NSWindow* window, |
| 150 ui::KeyboardCode keycode, | 58 ui::KeyboardCode keycode, |
| 151 bool control, | 59 bool control, |
| 152 bool shift, | 60 bool shift, |
| 153 bool alt, | 61 bool alt, |
| 154 bool command, | 62 bool command, |
| 155 std::vector<NSEvent*>* events) { | 63 std::vector<NSEvent*>* events) { |
| 156 NSEvent* event = nil; | 64 NSEvent* event = nil; |
| 157 NSUInteger flags = 0; | 65 NSUInteger flags = 0; |
| (...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { | 334 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { |
| 427 base::MessageLoop::current()->PostTask( | 335 base::MessageLoop::current()->PostTask( |
| 428 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); | 336 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); |
| 429 } | 337 } |
| 430 | 338 |
| 431 bool IsFullKeyboardAccessEnabled() { | 339 bool IsFullKeyboardAccessEnabled() { |
| 432 return [NSApp isFullKeyboardAccessEnabled]; | 340 return [NSApp isFullKeyboardAccessEnabled]; |
| 433 } | 341 } |
| 434 | 342 |
| 435 } // namespace ui_controls | 343 } // namespace ui_controls |
| OLD | NEW |