Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: ui/events/cocoa/events_mac_unittest.mm

Issue 2209003002: Remove the swizzling of -[NSEvent window] and the middle button in EventsMacTest (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: more private Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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 #import <Cocoa/Cocoa.h> 5 #import <Cocoa/Cocoa.h>
6 #include <stdint.h> 6 #include <stdint.h>
7 7
8 #include <memory> 8 #include <memory>
9 9
10 #include "base/mac/scoped_cftyperef.h" 10 #include "base/mac/scoped_cftyperef.h"
11 #import "base/mac/scoped_objc_class_swizzler.h"
12 #include "base/mac/sdk_forward_declarations.h" 11 #include "base/mac/sdk_forward_declarations.h"
13 #include "base/macros.h" 12 #include "base/macros.h"
14 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/events/event_constants.h" 14 #include "ui/events/event_constants.h"
16 #include "ui/events/event_utils.h" 15 #include "ui/events/event_utils.h"
17 #import "ui/events/test/cocoa_test_event_utils.h" 16 #import "ui/events/test/cocoa_test_event_utils.h"
18 #include "ui/gfx/geometry/point.h" 17 #include "ui/gfx/geometry/point.h"
19 #import "ui/gfx/test/ui_cocoa_test_helper.h" 18 #import "ui/gfx/test/ui_cocoa_test_helper.h"
20 19
21 namespace {
22
23 NSWindow* g_test_window = nil;
24
25 } // namespace
26
27 // Mac APIs for creating test events are frustrating. Quartz APIs have, e.g.,
28 // CGEventCreateMouseEvent() which can't set a window or modifier flags.
29 // Cocoa APIs have +[NSEvent mouseEventWithType:..] which can't set
30 // buttonNumber or scroll deltas. To work around this, these tests use some
31 // Objective C magic to donate member functions to NSEvent temporarily.
32 @interface MiddleMouseButtonNumberDonor : NSObject
33 @end
34
35 @interface TestWindowDonor : NSObject
36 @end
37
38 @implementation MiddleMouseButtonNumberDonor
39 - (NSInteger)buttonNumber { return 2; }
40 @end
41
42 @implementation TestWindowDonor
43 - (NSWindow*)window { return g_test_window; }
44 @end
45
46 namespace ui { 20 namespace ui {
47 21
48 namespace { 22 namespace {
49 23
24 // Although CGEventFlags is just a typedef to int in 10.10 and earlier headers,
25 // the 10.11 header makes this a CF_ENUM, but doesn't give an option for "none".
26 const CGEventFlags kNoEventFlags = static_cast<CGEventFlags>(0);
27
50 class EventsMacTest : public CocoaTest { 28 class EventsMacTest : public CocoaTest {
51 public: 29 public:
52 EventsMacTest() {} 30 EventsMacTest() {}
53 31
54 gfx::Point Flip(gfx::Point window_location) { 32 gfx::Point Flip(gfx::Point window_location) {
55 NSRect window_frame = [test_window() frame]; 33 NSRect window_frame = [test_window() frame];
56 CGFloat content_height = 34 CGFloat content_height =
57 NSHeight([test_window() contentRectForFrameRect:window_frame]); 35 NSHeight([test_window() contentRectForFrameRect:window_frame]);
58 window_location.set_y(content_height - window_location.y()); 36 window_location.set_y(content_height - window_location.y());
59 return window_location; 37 return window_location;
60 } 38 }
61 39
62 void SwizzleMiddleMouseButton() { 40 NSEvent* TestMouseEvent(CGEventType type,
63 DCHECK(!swizzler_); 41 const gfx::Point& window_location,
64 swizzler_.reset(new base::mac::ScopedObjCClassSwizzler( 42 CGEventFlags event_flags) {
65 [NSEvent class], 43 // CGEventCreateMouseEvent() ignores the CGMouseButton parameter unless
66 [MiddleMouseButtonNumberDonor class], 44 // |type| is one of kCGEventOtherMouse{Up,Down,Dragged}. It can be an
67 @selector(buttonNumber))); 45 // integer up to 31. However, constants are only supplied up to 2. For now,
68 } 46 // just assume "other" means the third/center mouse button, and rely on
69 47 // Quartz ignoring it when the type is not "other".
70 void SwizzleTestWindow() { 48 CGMouseButton other_button = kCGMouseButtonCenter;
71 DCHECK(!g_test_window); 49 base::ScopedCFTypeRef<CGEventRef> mouse(CGEventCreateMouseEvent(
72 DCHECK(!swizzler_); 50 nullptr, type, TestWindowPointToScreen(window_location), other_button));
73 g_test_window = test_window(); 51 CGEventSetFlags(mouse, event_flags);
74 swizzler_.reset(new base::mac::ScopedObjCClassSwizzler( 52 return EventWithTestWindow(mouse);
75 [NSEvent class], [TestWindowDonor class], @selector(window)));
76 }
77
78 void ClearSwizzle() {
79 swizzler_.reset();
80 g_test_window = nil;
81 }
82
83 NSEvent* TestMouseEvent(NSEventType type,
84 const gfx::Point &window_location,
85 NSInteger modifier_flags) {
86 NSPoint point = NSPointFromCGPoint(Flip(window_location).ToCGPoint());
87 return [NSEvent mouseEventWithType:type
88 location:point
89 modifierFlags:modifier_flags
90 timestamp:0
91 windowNumber:[test_window() windowNumber]
92 context:nil
93 eventNumber:0
94 clickCount:0
95 pressure:1.0];
96 } 53 }
97 54
98 NSEvent* TestScrollEvent(const gfx::Point& window_location, 55 NSEvent* TestScrollEvent(const gfx::Point& window_location,
99 int32_t delta_x, 56 int32_t delta_x,
100 int32_t delta_y) { 57 int32_t delta_y) {
101 SwizzleTestWindow(); 58 base::ScopedCFTypeRef<CGEventRef> scroll(CGEventCreateScrollWheelEvent(
102 base::ScopedCFTypeRef<CGEventRef> scroll( 59 nullptr, kCGScrollEventUnitLine, 2, delta_y, delta_x));
103 CGEventCreateScrollWheelEvent(NULL, 60 CGEventSetLocation(scroll, TestWindowPointToScreen(window_location));
104 kCGScrollEventUnitLine, 61 return EventWithTestWindow(scroll);
105 2, 62 }
106 delta_y, 63
107 delta_x)); 64 private:
65 CGPoint TestWindowPointToScreen(const gfx::Point& window_location) {
108 // CGEvents are always in global display coordinates. These are like screen 66 // CGEvents are always in global display coordinates. These are like screen
109 // coordinates, but flipped. But first the point needs to be converted out 67 // coordinates, but flipped. But first the point needs to be converted out
110 // of window coordinates (which also requires flipping). 68 // of window coordinates (which also requires flipping).
111 NSPoint window_point = 69 NSPoint window_point =
112 NSPointFromCGPoint(Flip(window_location).ToCGPoint()); 70 NSPointFromCGPoint(Flip(window_location).ToCGPoint());
113 NSRect window_rect = NSMakeRect(window_point.x, window_point.y, 0, 0); 71 NSRect window_rect = NSMakeRect(window_point.x, window_point.y, 0, 0);
114 NSPoint screen_point = 72 NSPoint screen_point =
115 [test_window() convertRectToScreen:window_rect].origin; 73 [test_window() convertRectToScreen:window_rect].origin;
116 CGFloat primary_screen_height = 74 CGFloat primary_screen_height =
117 NSHeight([[[NSScreen screens] firstObject] frame]); 75 NSHeight([[[NSScreen screens] firstObject] frame]);
118 screen_point.y = primary_screen_height - screen_point.y; 76 screen_point.y = primary_screen_height - screen_point.y;
119 CGEventSetLocation(scroll, NSPointToCGPoint(screen_point)); 77 return NSPointToCGPoint(screen_point);
120 return [NSEvent eventWithCGEvent:scroll];
121 } 78 }
122 79
123 private: 80 NSEvent* EventWithTestWindow(CGEventRef event) {
124 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> swizzler_; 81 // These CGEventFields were made public in the 10.7 SDK, but don't help to
82 // populate the -[NSEvent window] pointer when creating an event with
83 // +[NSEvent eventWithCGEvent:]. Set that separately, using reflection.
84 CGEventSetIntegerValueField(event, kCGMouseEventWindowUnderMousePointer,
85 [test_window() windowNumber]);
86 CGEventSetIntegerValueField(
87 event, kCGMouseEventWindowUnderMousePointerThatCanHandleThisEvent,
88 [test_window() windowNumber]);
89 NSEvent* ns_event = [NSEvent eventWithCGEvent:event];
90 EXPECT_EQ(nil, [ns_event window]); // Verify assumptions.
91 [ns_event setValue:test_window() forKey:@"_window"];
92 EXPECT_EQ(test_window(), [ns_event window]);
93 return ns_event;
94 }
125 95
126 DISALLOW_COPY_AND_ASSIGN(EventsMacTest); 96 DISALLOW_COPY_AND_ASSIGN(EventsMacTest);
127 }; 97 };
128 98
129 } // namespace 99 } // namespace
130 100
131 TEST_F(EventsMacTest, EventFlagsFromNative) { 101 TEST_F(EventsMacTest, EventFlagsFromNative) {
132 // Left click. 102 // Left click.
133 NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0); 103 NSEvent* left = cocoa_test_event_utils::MouseEventWithType(NSLeftMouseUp, 0);
134 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left)); 104 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, EventFlagsFromNative(left));
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask); 155 NSLeftMouseUp, NSCommandKeyMask | NSAlternateKeyMask);
186 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN, 156 EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_COMMAND_DOWN | EF_ALT_DOWN,
187 EventFlagsFromNative(cmdalt)); 157 EventFlagsFromNative(cmdalt));
188 } 158 }
189 159
190 // Tests mouse button presses and mouse wheel events. 160 // Tests mouse button presses and mouse wheel events.
191 TEST_F(EventsMacTest, ButtonEvents) { 161 TEST_F(EventsMacTest, ButtonEvents) {
192 gfx::Point location(5, 10); 162 gfx::Point location(5, 10);
193 gfx::Vector2d offset; 163 gfx::Vector2d offset;
194 164
195 NSEvent* event = TestMouseEvent(NSLeftMouseDown, location, 0); 165 NSEvent* event =
166 TestMouseEvent(kCGEventLeftMouseDown, location, kNoEventFlags);
196 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); 167 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event));
197 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); 168 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event));
198 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 169 EXPECT_EQ(location, ui::EventLocationFromNative(event));
199 170
200 SwizzleMiddleMouseButton(); 171 event =
201 event = TestMouseEvent(NSOtherMouseDown, location, NSShiftKeyMask); 172 TestMouseEvent(kCGEventOtherMouseDown, location, kCGEventFlagMaskShift);
202 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); 173 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event));
203 EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN, 174 EXPECT_EQ(ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN,
204 ui::EventFlagsFromNative(event)); 175 ui::EventFlagsFromNative(event));
205 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 176 EXPECT_EQ(location, ui::EventLocationFromNative(event));
206 ClearSwizzle();
207 177
208 event = TestMouseEvent(NSRightMouseUp, location, 0); 178 event = TestMouseEvent(kCGEventRightMouseUp, location, kNoEventFlags);
209 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(event)); 179 EXPECT_EQ(ui::ET_MOUSE_RELEASED, ui::EventTypeFromNative(event));
210 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); 180 EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, ui::EventFlagsFromNative(event));
211 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 181 EXPECT_EQ(location, ui::EventLocationFromNative(event));
212 182
213 // Scroll up. 183 // Scroll up.
214 event = TestScrollEvent(location, 0, 1); 184 event = TestScrollEvent(location, 0, 1);
215 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); 185 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
216 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); 186 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
217 EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString()); 187 EXPECT_EQ(location.ToString(), ui::EventLocationFromNative(event).ToString());
218 offset = ui::GetMouseWheelOffset(event); 188 offset = ui::GetMouseWheelOffset(event);
219 EXPECT_GT(offset.y(), 0); 189 EXPECT_GT(offset.y(), 0);
220 EXPECT_EQ(0, offset.x()); 190 EXPECT_EQ(0, offset.x());
221 ClearSwizzle();
222 191
223 // Scroll down. 192 // Scroll down.
224 event = TestScrollEvent(location, 0, -1); 193 event = TestScrollEvent(location, 0, -1);
225 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); 194 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
226 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); 195 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
227 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 196 EXPECT_EQ(location, ui::EventLocationFromNative(event));
228 offset = ui::GetMouseWheelOffset(event); 197 offset = ui::GetMouseWheelOffset(event);
229 EXPECT_LT(offset.y(), 0); 198 EXPECT_LT(offset.y(), 0);
230 EXPECT_EQ(0, offset.x()); 199 EXPECT_EQ(0, offset.x());
231 ClearSwizzle();
232 200
233 // Scroll left. 201 // Scroll left.
234 event = TestScrollEvent(location, 1, 0); 202 event = TestScrollEvent(location, 1, 0);
235 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); 203 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
236 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); 204 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
237 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 205 EXPECT_EQ(location, ui::EventLocationFromNative(event));
238 offset = ui::GetMouseWheelOffset(event); 206 offset = ui::GetMouseWheelOffset(event);
239 EXPECT_EQ(0, offset.y()); 207 EXPECT_EQ(0, offset.y());
240 EXPECT_GT(offset.x(), 0); 208 EXPECT_GT(offset.x(), 0);
241 ClearSwizzle();
242 209
243 // Scroll right. 210 // Scroll right.
244 event = TestScrollEvent(location, -1, 0); 211 event = TestScrollEvent(location, -1, 0);
245 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event)); 212 EXPECT_EQ(ui::ET_MOUSEWHEEL, ui::EventTypeFromNative(event));
246 EXPECT_EQ(0, ui::EventFlagsFromNative(event)); 213 EXPECT_EQ(0, ui::EventFlagsFromNative(event));
247 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 214 EXPECT_EQ(location, ui::EventLocationFromNative(event));
248 offset = ui::GetMouseWheelOffset(event); 215 offset = ui::GetMouseWheelOffset(event);
249 EXPECT_EQ(0, offset.y()); 216 EXPECT_EQ(0, offset.y());
250 EXPECT_LT(offset.x(), 0); 217 EXPECT_LT(offset.x(), 0);
251 ClearSwizzle();
252 } 218 }
253 219
254 // Test correct location when the window has a native titlebar. 220 // Test correct location when the window has a native titlebar.
255 TEST_F(EventsMacTest, NativeTitlebarEventLocation) { 221 TEST_F(EventsMacTest, NativeTitlebarEventLocation) {
256 gfx::Point location(5, 10); 222 gfx::Point location(5, 10);
257 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask | 223 NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
258 NSMiniaturizableWindowMask | NSResizableWindowMask; 224 NSMiniaturizableWindowMask | NSResizableWindowMask;
259 225
260 // First check that the window provided by ui::CocoaTest is how we think. 226 // First check that the window provided by ui::CocoaTest is how we think.
261 DCHECK_EQ(NSBorderlessWindowMask, [test_window() styleMask]); 227 DCHECK_EQ(NSBorderlessWindowMask, [test_window() styleMask]);
262 [test_window() setStyleMask:style_mask]; 228 [test_window() setStyleMask:style_mask];
263 DCHECK_EQ(style_mask, [test_window() styleMask]); 229 DCHECK_EQ(style_mask, [test_window() styleMask]);
264 230
265 // EventLocationFromNative should behave the same as the ButtonEvents test. 231 // EventLocationFromNative should behave the same as the ButtonEvents test.
266 NSEvent* event = TestMouseEvent(NSLeftMouseDown, location, 0); 232 NSEvent* event =
233 TestMouseEvent(kCGEventLeftMouseDown, location, kNoEventFlags);
267 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event)); 234 EXPECT_EQ(ui::ET_MOUSE_PRESSED, ui::EventTypeFromNative(event));
268 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event)); 235 EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, ui::EventFlagsFromNative(event));
269 EXPECT_EQ(location, ui::EventLocationFromNative(event)); 236 EXPECT_EQ(location, ui::EventLocationFromNative(event));
270 237
271 // And be explicit, to ensure the test doesn't depend on some property of the 238 // And be explicit, to ensure the test doesn't depend on some property of the
272 // test harness. The change to the frame rect could be OS-specfic, so set it 239 // test harness. The change to the frame rect could be OS-specfic, so set it
273 // to a known value. 240 // to a known value.
274 const CGFloat kTestHeight = 400; 241 const CGFloat kTestHeight = 400;
275 NSRect content_rect = NSMakeRect(0, 0, 600, kTestHeight); 242 NSRect content_rect = NSMakeRect(0, 0, 600, kTestHeight);
276 NSRect frame_rect = [test_window() frameRectForContentRect:content_rect]; 243 NSRect frame_rect = [test_window() frameRectForContentRect:content_rect];
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0); 282 event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
316 EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(event)); 283 EXPECT_EQ(ui::ET_MOUSE_MOVED, ui::EventTypeFromNative(event));
317 284
318 event = cocoa_test_event_utils::EnterEvent(); 285 event = cocoa_test_event_utils::EnterEvent();
319 EXPECT_EQ(ui::ET_MOUSE_ENTERED, ui::EventTypeFromNative(event)); 286 EXPECT_EQ(ui::ET_MOUSE_ENTERED, ui::EventTypeFromNative(event));
320 event = cocoa_test_event_utils::ExitEvent(); 287 event = cocoa_test_event_utils::ExitEvent();
321 EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(event)); 288 EXPECT_EQ(ui::ET_MOUSE_EXITED, ui::EventTypeFromNative(event));
322 } 289 }
323 290
324 } // namespace ui 291 } // namespace ui
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698