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

Side by Side Diff: ui/base/test/ui_controls_mac.mm

Issue 1747803003: MacViews: Implement Tab Dragging (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove CGEvent-generating code from ui_controls_mac.mm Created 4 years, 6 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
OLDNEW
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 <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/callback.h" 11 #include "base/callback.h"
12 #import "base/mac/foundation_util.h"
13 #import "base/mac/scoped_objc_class_swizzler.h"
12 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop.h"
13 #include "ui/base/cocoa/cocoa_base_utils.h" 15 #include "ui/base/cocoa/cocoa_base_utils.h"
14 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" 16 #include "ui/events/keycodes/keyboard_code_conversion_mac.h"
15 #import "ui/events/test/cocoa_test_event_utils.h" 17 #import "ui/events/test/cocoa_test_event_utils.h"
18 #include "ui/gfx/geometry/point.h"
19 #import "ui/gfx/mac/coordinate_conversion.h"
16 20
17 // Implementation details: We use [NSApplication sendEvent:] instead 21 // Implementation details: We use [NSApplication sendEvent:] instead
18 // of [NSApplication postEvent:atStart:] so that the event gets sent 22 // of [NSApplication postEvent:atStart:] so that the event gets sent
19 // immediately. This lets us run the post-event task right 23 // immediately. This lets us run the post-event task right
20 // immediately as well. Unfortunately I cannot subclass NSEvent (it's 24 // immediately as well. Unfortunately I cannot subclass NSEvent (it's
21 // probably a class cluster) to allow other easy answers. For 25 // 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 26 // example, if I could subclass NSEvent, I could run the Task in it's
23 // dealloc routine (which necessarily happens after the event is 27 // dealloc routine (which necessarily happens after the event is
24 // dispatched). Unlike Linux, Mac does not have message loop 28 // dispatched). Unlike Linux, Mac does not have message loop
25 // observer/notification. Unlike windows, I cannot post non-events 29 // observer/notification. Unlike windows, I cannot post non-events
(...skipping 19 matching lines...) Expand all
45 49
46 using cocoa_test_event_utils::SynthesizeKeyEvent; 50 using cocoa_test_event_utils::SynthesizeKeyEvent;
47 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; 51 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup;
48 52
49 namespace { 53 namespace {
50 54
51 // Stores the current mouse location on the screen. So that we can use it 55 // Stores the current mouse location on the screen. So that we can use it
52 // when firing keyboard and mouse click events. 56 // when firing keyboard and mouse click events.
53 NSPoint g_mouse_location = { 0, 0 }; 57 NSPoint g_mouse_location = { 0, 0 };
54 58
59 // Stores the current pressed mouse buttons. Indexed by
60 // ui_controls::MouseButton.
61 bool g_mouse_button_down[3] = {false, false, false};
62
55 bool g_ui_controls_enabled = false; 63 bool g_ui_controls_enabled = false;
56 64
57 // Creates the proper sequence of autoreleased key events for a key down + up. 65 // Creates the proper sequence of autoreleased key events for a key down + up.
58 void SynthesizeKeyEventsSequence(NSWindow* window, 66 void SynthesizeKeyEventsSequence(NSWindow* window,
59 ui::KeyboardCode keycode, 67 ui::KeyboardCode keycode,
60 bool control, 68 bool control,
61 bool shift, 69 bool shift,
62 bool alt, 70 bool alt,
63 bool command, 71 bool command,
64 std::vector<NSEvent*>* events) { 72 std::vector<NSEvent*>* events) {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 flags &= ~NSControlKeyMask; 126 flags &= ~NSControlKeyMask;
119 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags); 127 event = SynthesizeKeyEvent(window, false, ui::VKEY_CONTROL, flags);
120 DCHECK(event); 128 DCHECK(event);
121 events->push_back(event); 129 events->push_back(event);
122 } 130 }
123 } 131 }
124 132
125 // A helper function to watch for the event queue. The specific task will be 133 // A helper function to watch for the event queue. The specific task will be
126 // fired when there is no more event in the queue. 134 // fired when there is no more event in the queue.
127 void EventQueueWatcher(const base::Closure& task) { 135 void EventQueueWatcher(const base::Closure& task) {
136 DCHECK_EQ(dispatch_get_current_queue(), dispatch_get_main_queue())
tapted 2016/06/01 11:29:55 This DCHECK can probably go since we're not using
themblsha 2016/06/03 17:42:08 Done.
137 << "It should be run on the UI thread, as otherwise it will always "
138 "report there are no pending events";
128 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 139 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
129 untilDate:nil 140 untilDate:nil
130 inMode:NSDefaultRunLoopMode 141 inMode:NSDefaultRunLoopMode
131 dequeue:NO]; 142 dequeue:NO];
132 // If there is still event in the queue, then we need to check again. 143 // If there is still event in the queue, then we need to check again.
133 if (event) { 144 if (event) {
134 base::MessageLoop::current()->PostTask( 145 base::MessageLoop::current()->PostTask(
135 FROM_HERE, 146 FROM_HERE,
136 base::Bind(&EventQueueWatcher, task)); 147 base::Bind(&EventQueueWatcher, task));
137 } else { 148 } else {
(...skipping 28 matching lines...) Expand all
166 return window; 177 return window;
167 } 178 }
168 179
169 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test 180 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test
170 // uses those, it will need to handle that itself. 181 // uses those, it will need to handle that itself.
171 return nil; 182 return nil;
172 } 183 }
173 184
174 } // namespace 185 } // namespace
175 186
187 // Donates testing implementations of NSEvent methods.
188 @interface FakeNSEventTestingDonor : NSObject
189 @end
190
191 @implementation FakeNSEventTestingDonor
192 + (NSPoint)mouseLocation {
193 return g_mouse_location;
194 }
195
196 + (NSUInteger)pressedMouseButtons {
197 NSUInteger result = 0;
198 const int buttons[3] = {
199 ui_controls::LEFT, ui_controls::RIGHT, ui_controls::MIDDLE};
200 for (unsigned int i = 0; i < arraysize(buttons); ++i) {
tapted 2016/06/01 11:29:55 nit: unsigned int -> size_t (or just `int`, but th
themblsha 2016/06/03 17:42:08 Done.
201 if (g_mouse_button_down[buttons[i]])
202 result |= (1 << i);
203 }
204 return result;
205 }
206 @end
207
208 // Donates testing implementations of NSWindow methods.
209 @interface FakeNSWindowTestingDonor : NSObject
210 @end
211
212 @implementation FakeNSWindowTestingDonor
213 - (NSPoint)mouseLocationOutsideOfEventStream {
tapted 2016/06/01 11:29:55 Is anything being tested that requires this? (remo
themblsha 2016/06/03 17:42:08 It's widely used in Chromium, and without this swi
tapted 2016/06/06 07:12:36 `git grep mouseLocationOutsideOfEventStream` shows
themblsha 2016/06/06 17:20:27 Done.
214 NSWindow* window = base::mac::ObjCCastStrict<NSWindow>(self);
215 return ui::ConvertPointFromWindowToScreen(window, g_mouse_location);
216 }
217 @end
218
219 namespace {
tapted 2016/06/01 11:29:55 nit: blank line after
themblsha 2016/06/03 17:42:08 Done.
220 class NSEventSwizzler {
tapted 2016/06/01 11:29:55 needs a comment
themblsha 2016/06/03 17:42:08 Done.
221 public:
222 static void Install() {
223 static NSEventSwizzler* swizzler = nullptr;
tapted 2016/06/01 11:29:55 I'm unsure about this -- the swizzler is "Scoped"
themblsha 2016/06/03 17:42:08 In tests the entire browser state gets reconstruct
tapted 2016/06/06 07:12:36 Not always -- there is `--single-process-tests` wh
themblsha 2016/06/06 17:20:27 If it's only installed in EnableUIControls() and n
224 if (!swizzler) {
225 swizzler = new NSEventSwizzler();
226 }
227 }
228
229 protected:
230 NSEventSwizzler()
231 : mouse_location_swizzler_(new base::mac::ScopedObjCClassSwizzler(
232 [NSEvent class],
233 [FakeNSEventTestingDonor class],
234 @selector(mouseLocation))),
235 pressed_mouse_buttons_swizzler_(new base::mac::ScopedObjCClassSwizzler(
236 [NSEvent class],
237 [FakeNSEventTestingDonor class],
238 @selector(pressedMouseButtons))),
239 mouse_location_outside_of_event_stream_swizzler_(
240 new base::mac::ScopedObjCClassSwizzler(
241 [NSWindow class],
242 [FakeNSWindowTestingDonor class],
243 @selector(mouseLocationOutsideOfEventStream))) {}
244
245 private:
246 std::unique_ptr<base::mac::ScopedObjCClassSwizzler> mouse_location_swizzler_;
247 std::unique_ptr<base::mac::ScopedObjCClassSwizzler>
248 pressed_mouse_buttons_swizzler_;
249 std::unique_ptr<base::mac::ScopedObjCClassSwizzler>
250 mouse_location_outside_of_event_stream_swizzler_;
251 };
tapted 2016/06/01 11:29:55 nit: DISALLOW_COPY_AND_ASSIGN(..)
themblsha 2016/06/03 17:42:08 Done.
252 } // namespace
tapted 2016/06/01 11:29:55 nit: blank line before
themblsha 2016/06/03 17:42:08 Done.
253
176 namespace ui_controls { 254 namespace ui_controls {
177 255
178 void EnableUIControls() { 256 void EnableUIControls() {
179 g_ui_controls_enabled = true; 257 g_ui_controls_enabled = true;
180 } 258 }
181 259
182 bool IsUIControlsEnabled() { 260 bool IsUIControlsEnabled() {
183 return g_ui_controls_enabled; 261 return g_ui_controls_enabled;
184 } 262 }
185 263
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 CHECK(g_ui_controls_enabled); 310 CHECK(g_ui_controls_enabled);
233 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); 311 return SendMouseMoveNotifyWhenDone(x, y, base::Closure());
234 } 312 }
235 313
236 // Input position is in screen coordinates. However, NSMouseMoved 314 // Input position is in screen coordinates. However, NSMouseMoved
237 // events require them window-relative, so we adjust. We *DO* flip 315 // events require them window-relative, so we adjust. We *DO* flip
238 // the coordinate space, so input events can be the same for all 316 // the coordinate space, so input events can be the same for all
239 // platforms. E.g. (0,0) is upper-left. 317 // platforms. E.g. (0,0) is upper-left.
240 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { 318 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) {
241 CHECK(g_ui_controls_enabled); 319 CHECK(g_ui_controls_enabled);
242 CGFloat screenHeight = 320 g_mouse_location = gfx::ScreenPointToNSPoint(gfx::Point(x, y)); // flip!
243 [[[NSScreen screens] firstObject] frame].size.height; 321 NSEventSwizzler::Install();
244 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip!
245 322
246 NSWindow* window = WindowAtCurrentMouseLocation(); 323 NSWindow* window = WindowAtCurrentMouseLocation();
247 324
248 NSPoint pointInWindow = g_mouse_location; 325 NSPoint pointInWindow = g_mouse_location;
249 if (window) 326 if (window)
250 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); 327 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow);
251 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); 328 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup();
252 329
330 NSEventType event_type = NSMouseMoved;
331 if (g_mouse_button_down[LEFT]) {
332 event_type = NSLeftMouseDragged;
333 } else if (g_mouse_button_down[RIGHT]) {
334 event_type = NSRightMouseDragged;
335 } else if (g_mouse_button_down[MIDDLE]) {
336 event_type = NSOtherMouseDragged;
337 }
338
253 NSEvent* event = 339 NSEvent* event =
254 [NSEvent mouseEventWithType:NSMouseMoved 340 [NSEvent mouseEventWithType:event_type
255 location:pointInWindow 341 location:pointInWindow
256 modifierFlags:0 342 modifierFlags:0
257 timestamp:timestamp 343 timestamp:timestamp
258 windowNumber:[window windowNumber] 344 windowNumber:[window windowNumber]
259 context:nil 345 context:nil
260 eventNumber:0 346 eventNumber:0
261 clickCount:0 347 clickCount:(event_type == NSMouseMoved ? 0 : 1)
tapted 2016/06/01 11:29:55 outer () parens not required, same below
themblsha 2016/06/03 17:42:08 Done. Although there are parens in SendMouseEvents
262 pressure:0.0]; 348 pressure:(event_type == NSMouseMoved ? 0.0 : 1.0)];
263 [[NSApplication sharedApplication] postEvent:event atStart:NO]; 349 [[NSApplication sharedApplication] postEvent:event atStart:NO];
264 350
265 if (!task.is_null()) { 351 if (!task.is_null()) {
266 base::MessageLoop::current()->PostTask( 352 base::MessageLoop::current()->PostTask(
267 FROM_HERE, base::Bind(&EventQueueWatcher, task)); 353 FROM_HERE, base::Bind(&EventQueueWatcher, task));
268 } 354 }
269 355
270 return true; 356 return true;
271 } 357 }
272 358
273 bool SendMouseEvents(MouseButton type, int state) { 359 bool SendMouseEvents(MouseButton type, int state) {
274 CHECK(g_ui_controls_enabled); 360 CHECK(g_ui_controls_enabled);
275 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); 361 return SendMouseEventsNotifyWhenDone(type, state, base::Closure());
276 } 362 }
277 363
278 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, 364 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state,
279 const base::Closure& task) { 365 const base::Closure& task) {
280 CHECK(g_ui_controls_enabled); 366 CHECK(g_ui_controls_enabled);
281 // On windows it appears state can be (UP|DOWN). It is unclear if 367 // On windows it appears state can be (UP|DOWN). It is unclear if
282 // that'll happen here but prepare for it just in case. 368 // that'll happen here but prepare for it just in case.
283 if (state == (UP|DOWN)) { 369 if (state == (UP|DOWN)) {
284 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && 370 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) &&
285 SendMouseEventsNotifyWhenDone(type, UP, task)); 371 SendMouseEventsNotifyWhenDone(type, UP, task));
286 } 372 }
287 NSEventType etype = NSLeftMouseDown; 373 NSEventType event_type = NSLeftMouseDown;
288 if (type == LEFT) { 374 if (type == LEFT) {
289 if (state == UP) { 375 if (state == UP) {
290 etype = NSLeftMouseUp; 376 event_type = NSLeftMouseUp;
291 } else { 377 } else {
292 etype = NSLeftMouseDown; 378 event_type = NSLeftMouseDown;
293 } 379 }
294 } else if (type == MIDDLE) { 380 } else if (type == MIDDLE) {
295 if (state == UP) { 381 if (state == UP) {
296 etype = NSOtherMouseUp; 382 event_type = NSOtherMouseUp;
297 } else { 383 } else {
298 etype = NSOtherMouseDown; 384 event_type = NSOtherMouseDown;
299 } 385 }
300 } else if (type == RIGHT) { 386 } else if (type == RIGHT) {
301 if (state == UP) { 387 if (state == UP) {
302 etype = NSRightMouseUp; 388 event_type = NSRightMouseUp;
303 } else { 389 } else {
304 etype = NSRightMouseDown; 390 event_type = NSRightMouseDown;
305 } 391 }
306 } else { 392 } else {
393 NOTREACHED();
307 return false; 394 return false;
308 } 395 }
396 g_mouse_button_down[type] = state == DOWN;
397
309 NSWindow* window = WindowAtCurrentMouseLocation(); 398 NSWindow* window = WindowAtCurrentMouseLocation();
310 NSPoint pointInWindow = g_mouse_location; 399 NSPoint pointInWindow = g_mouse_location;
311 if (window) 400 if (window)
312 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow); 401 pointInWindow = ui::ConvertPointFromScreenToWindow(window, pointInWindow);
313 402
403 NSEventSwizzler::Install();
404
314 NSEvent* event = 405 NSEvent* event =
315 [NSEvent mouseEventWithType:etype 406 [NSEvent mouseEventWithType:event_type
316 location:pointInWindow 407 location:pointInWindow
317 modifierFlags:0 408 modifierFlags:0
318 timestamp:TimeIntervalSinceSystemStartup() 409 timestamp:TimeIntervalSinceSystemStartup()
319 windowNumber:[window windowNumber] 410 windowNumber:[window windowNumber]
320 context:nil 411 context:nil
321 eventNumber:0 412 eventNumber:0
322 clickCount:1 413 clickCount:1
323 pressure:(state == DOWN ? 1.0 : 0.0 )]; 414 pressure:(state == DOWN ? 1.0 : 0.0 )];
324 [[NSApplication sharedApplication] postEvent:event atStart:NO]; 415 [[NSApplication sharedApplication] postEvent:event atStart:NO];
325 416
(...skipping 13 matching lines...) Expand all
339 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { 430 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) {
340 base::MessageLoop::current()->PostTask( 431 base::MessageLoop::current()->PostTask(
341 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); 432 FROM_HERE, base::Bind(&EventQueueWatcher, closure));
342 } 433 }
343 434
344 bool IsFullKeyboardAccessEnabled() { 435 bool IsFullKeyboardAccessEnabled() {
345 return [NSApp isFullKeyboardAccessEnabled]; 436 return [NSApp isFullKeyboardAccessEnabled];
346 } 437 }
347 438
348 } // namespace ui_controls 439 } // namespace ui_controls
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698