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 #include "ui/events/event_utils.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/mac/scoped_cftyperef.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/events/event_constants.h"
13 #import "ui/events/test/cocoa_test_event_utils.h"
14 #include "ui/gfx/point.h"
15 #import "ui/gfx/test/ui_cocoa_test_helper.h"
19 NSWindow* g_test_window = nil;
23 // Mac APIs for creating test events are frustrating. Quartz APIs have, e.g.,
24 // CGEventCreateMouseEvent() which can't set a window or modifier flags.
25 // Cocoa APIs have +[NSEvent mouseEventWithType:..] which can't set
26 // buttonNumber or scroll deltas. To work around this, these tests use some
27 // Objective C magic to donate member functions to NSEvent temporarily.
28 @interface MiddleMouseButtonNumberDonor : NSObject
31 @interface TestWindowDonor : NSObject
34 @implementation MiddleMouseButtonNumberDonor
35 - (NSUInteger)buttonNumber { return 2; }
38 @implementation TestWindowDonor
39 - (NSWindow*)window { return g_test_window; }
46 class EventsMacTest : public CocoaTest {
50 gfx::Point Flip(gfx::Point window_location) {
51 window_location.set_y(
52 NSHeight([test_window() frame]) - window_location.y());
53 return window_location;
56 void SwizzleMiddleMouseButton() {
58 swizzler_.reset(new ScopedClassSwizzler(
60 [MiddleMouseButtonNumberDonor class],
61 @selector(buttonNumber)));
64 void SwizzleTestWindow() {
65 DCHECK(!g_test_window);
67 g_test_window = test_window();
68 swizzler_.reset(new ScopedClassSwizzler(
70 [TestWindowDonor class],
79 NSEvent* TestMouseEvent(NSEventType type,
80 const gfx::Point &window_location,
81 NSInteger modifier_flags) {
82 NSPoint point = NSPointFromCGPoint(Flip(window_location).ToCGPoint());
83 return [NSEvent mouseEventWithType:type
85 modifierFlags:modifier_flags
87 windowNumber:[test_window() windowNumber]
94 NSEvent* TestScrollEvent(const gfx::Point& window_location,
98 base::ScopedCFTypeRef<CGEventRef> scroll(
99 CGEventCreateScrollWheelEvent(NULL,
100 kCGScrollEventUnitLine,
104 // CGEvents are always in global display coordinates. These are like screen
105 // coordinates, but flipped. But first the point needs to be converted out
106 // of window coordinates (which also requires flipping).
107 NSPoint window_point =
108 NSPointFromCGPoint(Flip(window_location).ToCGPoint());
109 NSPoint screen_point = [test_window() convertBaseToScreen:window_point];
110 CGFloat primary_screen_height =
111 NSHeight([[[NSScreen screens] objectAtIndex:0] frame]);
112 screen_point.y = primary_screen_height - screen_point.y;
113 CGEventSetLocation(scroll, NSPointToCGPoint(screen_point));
114 return [NSEvent eventWithCGEvent:scroll];
118 scoped_ptr<ScopedClassSwizzler> swizzler_;
120 DISALLOW_COPY_AND_ASSIGN(EventsMacTest);
125 TEST_F(EventsMacTest, EventFlagsFromNative) {
127 NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0);
128 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left));
131 NSEvent* right = cocoa_test_event_utils::MouseEventWithType(NSRightMouseUp,
133 EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, EventFlagsFromNative(right));
136 NSEvent* middle = cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp,
138 EXPECT_EQ(EF_MIDDLE_MOUSE_BUTTON, EventFlagsFromNative(middle));
141 NSEvent* caps = cocoa_test_event_utils::MouseEventWithType(
142 NSLeftMouseUp, NSAlphaShiftKeyMask);
143 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_CAPS_LOCK_DOWN,
144 EventFlagsFromNative(caps));
147 NSEvent* shift = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp,
149 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_SHIFT_DOWN, EventFlagsFromNative(shift));
152 NSEvent* ctrl = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp,
154 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_CONTROL_DOWN, EventFlagsFromNative(ctrl));
157 NSEvent* alt = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp,
159 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_ALT_DOWN, EventFlagsFromNative(alt));
162 NSEvent* cmd = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp,
164 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN, EventFlagsFromNative(cmd));
166 // Shift + Ctrl + Left
167 NSEvent* shiftctrl = cocoa_test_event_utils::MouseEventWithType(
168 NSLeftMouseUp, NSShiftKeyMask | NSControlKeyMask);
169 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_SHIFT_DOWN | EF_CONTROL_DOWN,
170 EventFlagsFromNative(shiftctrl));
173 NSEvent* cmdalt = cocoa_test_event_utils::MouseEventWithType(
174 NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask);
175 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN,
176 EventFlagsFromNative(cmdalt));
179 // Tests mouse button presses and mouse wheel events.
180 TEST_F(EventsMacTest, ButtonEvents) {
181 gfx::Point location(5, 10);
182 gfx::Vector2d offset;
184 NSEvent* event = TestMouseEvent(NSLeftMouseDown, location, 0);
185 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event));
186 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event));
187 EXPECT_EQ(location, ui::EventLocationFromNative(event));
189 SwizzleMiddleMouseButton();
190 event = TestMouseEvent(NSOtherMouseDown, location, NSShiftKeyMask);
191 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event));
192 EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN,
193 ui::EventFlagsFromNative(event));
194 EXPECT_EQ(location, ui::EventLocationFromNative(event));
197 event = TestMouseEvent(NSRightMouseUp, location, 0);
198 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(event));
199 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(event));
200 EXPECT_EQ(location, ui::EventLocationFromNative(event));
203 event = TestScrollEvent(location, 0, 1);
204 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
205 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
206 EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString());
207 offset = ui::GetMouseWheelOffset(event);
208 EXPECT_GT(offset.y(), 0);
209 EXPECT_EQ(0, offset.x());
213 event = TestScrollEvent(location, 0, -1);
214 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
215 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
216 EXPECT_EQ(location, ui::EventLocationFromNative(event));
217 offset = ui::GetMouseWheelOffset(event);
218 EXPECT_LT(offset.y(), 0);
219 EXPECT_EQ(0, offset.x());
223 event = TestScrollEvent(location, 1, 0);
224 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
225 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
226 EXPECT_EQ(location, ui::EventLocationFromNative(event));
227 offset = ui::GetMouseWheelOffset(event);
228 EXPECT_EQ(0, offset.y());
229 EXPECT_GT(offset.x(), 0);
233 event = TestScrollEvent(location, -1, 0);
234 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
235 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
236 EXPECT_EQ(location, ui::EventLocationFromNative(event));
237 offset = ui::GetMouseWheelOffset(event);
238 EXPECT_EQ(0, offset.y());
239 EXPECT_LT(offset.x(), 0);