Chromium Code Reviews| 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 <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 #include "base/mac/scoped_nsautorelease_pool.h" | |
| 13 #include "base/mac/scoped_nsobject.h" | |
| 14 #include "base/mac/scoped_objc_class_swizzler.h" | |
| 12 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 16 #include "base/thread_task_runner_handle.h" | |
| 13 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" | 17 #include "ui/events/keycodes/keyboard_code_conversion_mac.h" |
| 14 #import "ui/events/test/cocoa_test_event_utils.h" | 18 #import "ui/events/test/cocoa_test_event_utils.h" |
| 19 #include "ui/gfx/mac/coordinate_conversion.h" | |
| 20 #include "ui/gfx/screen.h" | |
| 15 | 21 |
| 16 // Implementation details: We use [NSApplication sendEvent:] instead | 22 // Implementation details: We use [NSApplication sendEvent:] instead |
| 17 // of [NSApplication postEvent:atStart:] so that the event gets sent | 23 // of [NSApplication postEvent:atStart:] so that the event gets sent |
| 18 // immediately. This lets us run the post-event task right | 24 // immediately. This lets us run the post-event task right |
| 19 // immediately as well. Unfortunately I cannot subclass NSEvent (it's | 25 // immediately as well. Unfortunately I cannot subclass NSEvent (it's |
| 20 // probably a class cluster) to allow other easy answers. For | 26 // probably a class cluster) to allow other easy answers. For |
| 21 // example, if I could subclass NSEvent, I could run the Task in it's | 27 // example, if I could subclass NSEvent, I could run the Task in it's |
| 22 // dealloc routine (which necessarily happens after the event is | 28 // dealloc routine (which necessarily happens after the event is |
| 23 // dispatched). Unlike Linux, Mac does not have message loop | 29 // dispatched). Unlike Linux, Mac does not have message loop |
| 24 // observer/notification. Unlike windows, I cannot post non-events | 30 // observer/notification. Unlike windows, I cannot post non-events |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 43 // be used, so that we don't need to poll the event queue time to time. | 49 // be used, so that we don't need to poll the event queue time to time. |
| 44 | 50 |
| 45 using cocoa_test_event_utils::SynthesizeKeyEvent; | 51 using cocoa_test_event_utils::SynthesizeKeyEvent; |
| 46 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; | 52 using cocoa_test_event_utils::TimeIntervalSinceSystemStartup; |
| 47 | 53 |
| 48 namespace { | 54 namespace { |
| 49 | 55 |
| 50 // Stores the current mouse location on the screen. So that we can use it | 56 // Stores the current mouse location on the screen. So that we can use it |
| 51 // when firing keyboard and mouse click events. | 57 // when firing keyboard and mouse click events. |
| 52 NSPoint g_mouse_location = { 0, 0 }; | 58 NSPoint g_mouse_location = { 0, 0 }; |
| 59 bool g_mouse_button_down[3] = { false, false, false }; | |
|
tapted
2016/03/01 08:11:58
comment (mention that it's indexed by ui_controls:
themblsha
2016/03/09 17:40:22
Done.
| |
| 53 | 60 |
| 54 bool g_ui_controls_enabled = false; | 61 bool g_ui_controls_enabled = false; |
| 55 | 62 |
| 56 // Creates the proper sequence of autoreleased key events for a key down + up. | 63 // Creates the proper sequence of autoreleased key events for a key down + up. |
| 57 void SynthesizeKeyEventsSequence(NSWindow* window, | 64 void SynthesizeKeyEventsSequence(NSWindow* window, |
| 58 ui::KeyboardCode keycode, | 65 ui::KeyboardCode keycode, |
| 59 bool control, | 66 bool control, |
| 60 bool shift, | 67 bool shift, |
| 61 bool alt, | 68 bool alt, |
| 62 bool command, | 69 bool command, |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 return window; | 172 return window; |
| 166 } | 173 } |
| 167 | 174 |
| 168 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test | 175 // Note that -[NSApplication orderedWindows] won't include NSPanels. If a test |
| 169 // uses those, it will need to handle that itself. | 176 // uses those, it will need to handle that itself. |
| 170 return nil; | 177 return nil; |
| 171 } | 178 } |
| 172 | 179 |
| 173 } // namespace | 180 } // namespace |
| 174 | 181 |
| 182 namespace { | |
| 183 int kEventDeletedTaskRunnerKey; | |
|
tapted
2016/03/01 08:11:58
can be merged with the namespace above. needs a co
themblsha
2016/03/09 17:40:22
Done.
| |
| 184 } // namespace | |
| 185 | |
| 186 @interface EventDeletedTaskRunner : NSObject { | |
|
tapted
2016/03/01 08:11:58
What's the reason why EventQueueWatcher couldn't s
themblsha
2016/03/01 18:41:53
I'm spawning these on a separate thread, as the ma
| |
| 187 @private | |
| 188 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
| 189 base::Closure finish_closure_; | |
| 190 } | |
| 191 - (id)initWithClosure:(const base::Closure&)task; | |
| 192 @end | |
| 193 | |
| 194 @implementation EventDeletedTaskRunner | |
| 195 - (id)initWithClosure:(const base::Closure&)task { | |
| 196 if (self = [super init]) { | |
| 197 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
| 198 finish_closure_ = task; | |
| 199 } | |
| 200 return self; | |
| 201 } | |
| 202 | |
| 203 - (void)dealloc { | |
| 204 task_runner_->PostTask(FROM_HERE, finish_closure_); | |
| 205 [super dealloc]; | |
| 206 } | |
| 207 @end | |
| 208 | |
| 209 // Donates testing implementations of NSEvent methods. | |
| 210 @interface FakeNSEventTestingDonor : NSObject | |
| 211 @end | |
| 212 | |
| 213 @implementation FakeNSEventTestingDonor | |
| 214 + (NSPoint)mouseLocation { | |
| 215 return g_mouse_location; | |
| 216 } | |
| 217 | |
| 218 + (NSUInteger)pressedMouseButtons { | |
| 219 NSUInteger result = 0; | |
| 220 const int buttons[3] = { | |
| 221 ui_controls::LEFT, ui_controls::RIGHT, ui_controls::MIDDLE}; | |
| 222 for (unsigned int i = 0; i < arraysize(buttons); ++i) { | |
| 223 if (g_mouse_button_down[buttons[i]]) | |
| 224 result |= (1 << i); | |
| 225 } | |
| 226 return result; | |
| 227 } | |
| 228 @end | |
| 229 | |
| 230 namespace { | |
| 231 class ScopedNSEventSwizzler { | |
| 232 public: | |
| 233 static void Install() { | |
| 234 static ScopedNSEventSwizzler* swizzler = nullptr; | |
| 235 if (!swizzler) { | |
| 236 swizzler = new ScopedNSEventSwizzler(); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 protected: | |
| 241 ScopedNSEventSwizzler() | |
| 242 : mouse_location_swizzler_(new base::mac::ScopedObjCClassSwizzler( | |
| 243 [NSEvent class], | |
| 244 [FakeNSEventTestingDonor class], | |
| 245 @selector(mouseLocation))), | |
| 246 pressed_mouse_buttons_swizzler_(new base::mac::ScopedObjCClassSwizzler( | |
| 247 [NSEvent class], | |
| 248 [FakeNSEventTestingDonor class], | |
| 249 @selector(pressedMouseButtons))) { | |
| 250 } | |
| 251 | |
| 252 private: | |
| 253 scoped_ptr<base::mac::ScopedObjCClassSwizzler> mouse_location_swizzler_; | |
| 254 scoped_ptr<base::mac::ScopedObjCClassSwizzler> | |
| 255 pressed_mouse_buttons_swizzler_; | |
| 256 }; | |
| 257 } // namespace | |
| 258 | |
| 175 namespace ui_controls { | 259 namespace ui_controls { |
| 176 | 260 |
| 177 void EnableUIControls() { | 261 void EnableUIControls() { |
| 178 g_ui_controls_enabled = true; | 262 g_ui_controls_enabled = true; |
| 179 } | 263 } |
| 180 | 264 |
| 181 bool SendKeyPress(gfx::NativeWindow window, | 265 bool SendKeyPress(gfx::NativeWindow window, |
| 182 ui::KeyboardCode key, | 266 ui::KeyboardCode key, |
| 183 bool control, | 267 bool control, |
| 184 bool shift, | 268 bool shift, |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 195 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, | 279 bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, |
| 196 ui::KeyboardCode key, | 280 ui::KeyboardCode key, |
| 197 bool control, | 281 bool control, |
| 198 bool shift, | 282 bool shift, |
| 199 bool alt, | 283 bool alt, |
| 200 bool command, | 284 bool command, |
| 201 const base::Closure& task) { | 285 const base::Closure& task) { |
| 202 CHECK(g_ui_controls_enabled); | 286 CHECK(g_ui_controls_enabled); |
| 203 DCHECK(base::MessageLoopForUI::IsCurrent()); | 287 DCHECK(base::MessageLoopForUI::IsCurrent()); |
| 204 | 288 |
| 289 // we want to destroy the autoreleased event ASAP | |
|
tapted
2016/03/01 08:11:58
Comments should all be complete sentences. Capital
themblsha
2016/03/09 17:40:22
Done.
| |
| 290 base::mac::ScopedNSAutoreleasePool pool; | |
| 291 | |
| 205 std::vector<NSEvent*> events; | 292 std::vector<NSEvent*> events; |
| 206 SynthesizeKeyEventsSequence( | 293 SynthesizeKeyEventsSequence( |
| 207 window, key, control, shift, alt, command, &events); | 294 window, key, control, shift, alt, command, &events); |
| 208 | 295 |
| 296 if (!task.is_null()) { | |
| 297 base::scoped_nsobject<EventDeletedTaskRunner> task_runner = | |
| 298 [[EventDeletedTaskRunner alloc] initWithClosure:task]; | |
| 299 | |
| 300 // event will be deleted when the last event is deleted | |
| 301 for (std::vector<NSEvent*>::iterator iter = events.begin(); | |
|
tapted
2016/03/01 08:11:58
for (NSEvent* event : events)
| |
| 302 iter != events.end(); ++iter) | |
| 303 objc_setAssociatedObject(*iter, &kEventDeletedTaskRunnerKey, | |
| 304 task_runner.get(), OBJC_ASSOCIATION_RETAIN); | |
| 305 } | |
| 306 | |
| 209 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes | 307 // TODO(suzhe): Using [NSApplication postEvent:atStart:] here causes |
| 210 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 | 308 // BrowserKeyEventsTest.CommandKeyEvents to fail. See http://crbug.com/49270 |
| 211 // But using [NSApplication sendEvent:] should be safe for keyboard events, | 309 // But using [NSApplication sendEvent:] should be safe for keyboard events, |
| 212 // because until now, no code wants to retrieve the next event when handling | 310 // because until now, no code wants to retrieve the next event when handling |
| 213 // a keyboard event. | 311 // a keyboard event. |
| 214 for (std::vector<NSEvent*>::iterator iter = events.begin(); | 312 for (std::vector<NSEvent*>::iterator iter = events.begin(); |
| 215 iter != events.end(); ++iter) | 313 iter != events.end(); ++iter) |
| 216 [[NSApplication sharedApplication] sendEvent:*iter]; | 314 [[NSApplication sharedApplication] sendEvent:*iter]; |
| 217 | 315 |
| 218 if (!task.is_null()) { | |
| 219 base::MessageLoop::current()->PostTask( | |
| 220 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | |
| 221 } | |
| 222 | |
| 223 return true; | 316 return true; |
| 224 } | 317 } |
| 225 | 318 |
| 226 bool SendMouseMove(long x, long y) { | 319 bool SendMouseMove(long x, long y) { |
| 227 CHECK(g_ui_controls_enabled); | 320 CHECK(g_ui_controls_enabled); |
| 228 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); | 321 return SendMouseMoveNotifyWhenDone(x, y, base::Closure()); |
| 229 } | 322 } |
| 230 | 323 |
| 231 // Input position is in screen coordinates. However, NSMouseMoved | 324 // Input position is in screen coordinates. However, NSMouseMoved |
| 232 // events require them window-relative, so we adjust. We *DO* flip | 325 // events require them window-relative, so we adjust. We *DO* flip |
| 233 // the coordinate space, so input events can be the same for all | 326 // the coordinate space, so input events can be the same for all |
| 234 // platforms. E.g. (0,0) is upper-left. | 327 // platforms. E.g. (0,0) is upper-left. |
| 235 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { | 328 bool SendMouseMoveNotifyWhenDone(long x, long y, const base::Closure& task) { |
| 236 CHECK(g_ui_controls_enabled); | 329 CHECK(g_ui_controls_enabled); |
| 237 CGFloat screenHeight = | 330 g_mouse_location = gfx::ScreenPointToNSPoint(gfx::Point(x, y)); // flip! |
| 238 [[[NSScreen screens] firstObject] frame].size.height; | 331 ScopedNSEventSwizzler::Install(); |
| 239 g_mouse_location = NSMakePoint(x, screenHeight - y); // flip! | |
| 240 | 332 |
| 241 NSWindow* window = WindowAtCurrentMouseLocation(); | 333 NSWindow* window = WindowAtCurrentMouseLocation(); |
| 242 | 334 |
| 243 NSPoint pointInWindow = g_mouse_location; | 335 NSPoint pointInWindow = g_mouse_location; |
| 244 if (window) | 336 if (window) |
| 245 pointInWindow = [window convertScreenToBase:pointInWindow]; | 337 pointInWindow = [window convertScreenToBase:pointInWindow]; |
| 246 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); | 338 NSTimeInterval timestamp = TimeIntervalSinceSystemStartup(); |
| 247 | 339 |
| 248 NSEvent* event = | 340 NSEventType etype = NSMouseMoved; |
|
tapted
2016/03/01 08:11:58
etype -> event_type
themblsha
2016/03/09 17:40:22
Done.
| |
| 249 [NSEvent mouseEventWithType:NSMouseMoved | 341 if (g_mouse_button_down[LEFT]) { |
| 250 location:pointInWindow | 342 etype = NSLeftMouseDragged; |
| 251 modifierFlags:0 | 343 } else if (g_mouse_button_down[RIGHT]) { |
| 252 timestamp:timestamp | 344 etype = NSRightMouseDragged; |
| 253 windowNumber:[window windowNumber] | 345 } else if (g_mouse_button_down[MIDDLE]) { |
| 254 context:nil | 346 etype = NSOtherMouseDragged; |
| 255 eventNumber:0 | 347 } |
| 256 clickCount:0 | 348 |
| 257 pressure:0.0]; | 349 // we want to destroy the autoreleased event ASAP |
| 258 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | 350 base::mac::ScopedNSAutoreleasePool pool; |
| 351 | |
| 352 NSEvent* event = [NSEvent mouseEventWithType:etype | |
| 353 location:pointInWindow | |
| 354 modifierFlags:0 | |
| 355 timestamp:timestamp | |
| 356 windowNumber:[window windowNumber] | |
| 357 context:nil | |
| 358 eventNumber:0 | |
| 359 clickCount:0 | |
| 360 pressure:0.0]; | |
| 259 | 361 |
| 260 if (!task.is_null()) { | 362 if (!task.is_null()) { |
| 261 base::MessageLoop::current()->PostTask( | 363 base::scoped_nsobject<EventDeletedTaskRunner> task_runner = |
| 262 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 364 [[EventDeletedTaskRunner alloc] initWithClosure:task]; |
| 365 objc_setAssociatedObject(event, &kEventDeletedTaskRunnerKey, | |
| 366 task_runner.get(), OBJC_ASSOCIATION_RETAIN); | |
| 263 } | 367 } |
| 264 | 368 |
| 369 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
| 370 | |
| 265 return true; | 371 return true; |
| 266 } | 372 } |
| 267 | 373 |
| 268 bool SendMouseEvents(MouseButton type, int state) { | 374 bool SendMouseEvents(MouseButton type, int state) { |
| 269 CHECK(g_ui_controls_enabled); | 375 CHECK(g_ui_controls_enabled); |
| 270 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); | 376 return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); |
| 271 } | 377 } |
| 272 | 378 |
| 273 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, | 379 bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, |
| 274 const base::Closure& task) { | 380 const base::Closure& task) { |
| 275 CHECK(g_ui_controls_enabled); | 381 CHECK(g_ui_controls_enabled); |
| 276 // On windows it appears state can be (UP|DOWN). It is unclear if | 382 // On windows it appears state can be (UP|DOWN). It is unclear if |
| 277 // that'll happen here but prepare for it just in case. | 383 // that'll happen here but prepare for it just in case. |
| 278 if (state == (UP|DOWN)) { | 384 if (state == (UP|DOWN)) { |
| 279 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && | 385 return (SendMouseEventsNotifyWhenDone(type, DOWN, base::Closure()) && |
| 280 SendMouseEventsNotifyWhenDone(type, UP, task)); | 386 SendMouseEventsNotifyWhenDone(type, UP, task)); |
| 281 } | 387 } |
| 282 NSEventType etype = NSLeftMouseDown; | 388 NSEventType etype = NSLeftMouseDown; |
| 283 if (type == LEFT) { | 389 if (type == LEFT) { |
| 284 if (state == UP) { | 390 if (state == UP) { |
| 285 etype = NSLeftMouseUp; | 391 etype = NSLeftMouseUp; |
| 392 g_mouse_button_down[LEFT] = false; | |
| 286 } else { | 393 } else { |
| 287 etype = NSLeftMouseDown; | 394 etype = NSLeftMouseDown; |
| 395 g_mouse_button_down[LEFT] = true; | |
| 288 } | 396 } |
| 289 } else if (type == MIDDLE) { | 397 } else if (type == MIDDLE) { |
| 290 if (state == UP) { | 398 if (state == UP) { |
| 291 etype = NSOtherMouseUp; | 399 etype = NSOtherMouseUp; |
| 400 g_mouse_button_down[MIDDLE] = false; | |
| 292 } else { | 401 } else { |
| 293 etype = NSOtherMouseDown; | 402 etype = NSOtherMouseDown; |
| 403 g_mouse_button_down[MIDDLE] = true; | |
| 294 } | 404 } |
| 295 } else if (type == RIGHT) { | 405 } else if (type == RIGHT) { |
| 296 if (state == UP) { | 406 if (state == UP) { |
| 297 etype = NSRightMouseUp; | 407 etype = NSRightMouseUp; |
| 408 g_mouse_button_down[RIGHT] = false; | |
| 298 } else { | 409 } else { |
| 299 etype = NSRightMouseDown; | 410 etype = NSRightMouseDown; |
| 411 g_mouse_button_down[RIGHT] = true; | |
|
tapted
2016/03/01 08:11:58
these can all be collapsed into
g_mouse_button_do
themblsha
2016/03/09 17:40:22
Done.
| |
| 300 } | 412 } |
| 301 } else { | 413 } else { |
| 302 return false; | 414 return false; |
| 303 } | 415 } |
| 304 NSWindow* window = WindowAtCurrentMouseLocation(); | 416 NSWindow* window = WindowAtCurrentMouseLocation(); |
| 305 NSPoint pointInWindow = g_mouse_location; | 417 NSPoint pointInWindow = g_mouse_location; |
| 306 if (window) | 418 if (window) |
| 307 pointInWindow = [window convertScreenToBase:pointInWindow]; | 419 pointInWindow = [window convertScreenToBase:pointInWindow]; |
| 308 | 420 |
| 421 ScopedNSEventSwizzler::Install(); | |
| 422 | |
| 423 // we want to destroy the autoreleased event ASAP | |
| 424 base::mac::ScopedNSAutoreleasePool pool; | |
| 425 | |
| 309 NSEvent* event = | 426 NSEvent* event = |
| 310 [NSEvent mouseEventWithType:etype | 427 [NSEvent mouseEventWithType:etype |
| 311 location:pointInWindow | 428 location:pointInWindow |
| 312 modifierFlags:0 | 429 modifierFlags:0 |
| 313 timestamp:TimeIntervalSinceSystemStartup() | 430 timestamp:TimeIntervalSinceSystemStartup() |
| 314 windowNumber:[window windowNumber] | 431 windowNumber:[window windowNumber] |
| 315 context:nil | 432 context:nil |
| 316 eventNumber:0 | 433 eventNumber:0 |
| 317 clickCount:1 | 434 clickCount:1 |
| 318 pressure:(state == DOWN ? 1.0 : 0.0 )]; | 435 pressure:(state == DOWN ? 1.0 : 0.0 )]; |
| 319 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
| 320 | 436 |
| 321 if (!task.is_null()) { | 437 if (!task.is_null()) { |
| 322 base::MessageLoop::current()->PostTask( | 438 base::scoped_nsobject<EventDeletedTaskRunner> task_runner = |
| 323 FROM_HERE, base::Bind(&EventQueueWatcher, task)); | 439 [[EventDeletedTaskRunner alloc] initWithClosure:task]; |
| 440 objc_setAssociatedObject(event, &kEventDeletedTaskRunnerKey, | |
| 441 task_runner.get(), OBJC_ASSOCIATION_RETAIN); | |
| 324 } | 442 } |
| 325 | 443 |
| 444 [[NSApplication sharedApplication] postEvent:event atStart:NO]; | |
| 445 | |
| 326 return true; | 446 return true; |
| 327 } | 447 } |
| 328 | 448 |
| 329 bool SendMouseClick(MouseButton type) { | 449 bool SendMouseClick(MouseButton type) { |
| 330 CHECK(g_ui_controls_enabled); | 450 CHECK(g_ui_controls_enabled); |
| 331 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); | 451 return SendMouseEventsNotifyWhenDone(type, UP|DOWN, base::Closure()); |
| 332 } | 452 } |
| 333 | 453 |
| 334 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { | 454 void RunClosureAfterAllPendingUIEvents(const base::Closure& closure) { |
| 335 base::MessageLoop::current()->PostTask( | 455 base::MessageLoop::current()->PostTask( |
| 336 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); | 456 FROM_HERE, base::Bind(&EventQueueWatcher, closure)); |
| 337 } | 457 } |
| 338 | 458 |
| 339 bool IsFullKeyboardAccessEnabled() { | 459 bool IsFullKeyboardAccessEnabled() { |
| 340 return [NSApp isFullKeyboardAccessEnabled]; | 460 return [NSApp isFullKeyboardAccessEnabled]; |
| 341 } | 461 } |
| 342 | 462 |
| 343 } // namespace ui_controls | 463 } // namespace ui_controls |
| OLD | NEW |