Fix "value possibly truncated" warnings on MSVC, net/disk_cache edition.
[chromium-blink-merge.git] / ui / views / test / event_generator_delegate_mac.mm
blobd156aa5d4af42f9381c6e21c5329ec1a1861814c
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 #import <Cocoa/Cocoa.h>
7 #import "base/mac/scoped_objc_class_swizzler.h"
8 #include "base/memory/singleton.h"
9 #include "ui/events/event_processor.h"
10 #include "ui/events/event_target.h"
11 #include "ui/events/event_target_iterator.h"
12 #include "ui/events/event_targeter.h"
13 #include "ui/events/test/event_generator.h"
14 #include "ui/gfx/mac/coordinate_conversion.h"
16 namespace {
18 // Singleton to provide state for swizzled Objective C methods.
19 ui::test::EventGenerator* g_active_generator = NULL;
21 }  // namespace
23 @interface NSEventDonor : NSObject
24 @end
26 @implementation NSEventDonor
28 // Donate +[NSEvent pressedMouseButtons] by retrieving the flags from the
29 // active generator.
30 + (NSUInteger)pressedMouseButtons {
31   int flags = g_active_generator->flags();
32   NSUInteger bitmask = 0;
33   if (flags & ui::EF_LEFT_MOUSE_BUTTON)
34     bitmask |= 1;
35   if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
36     bitmask |= 1 << 1;
37   if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
38     bitmask |= 1 << 2;
39   return bitmask;
42 @end
44 namespace {
46 NSPoint ConvertRootPointToTarget(NSWindow* target,
47                                  const gfx::Point& point_in_root) {
48   // Normally this would do [NSWindow convertScreenToBase:]. However, Cocoa can
49   // reposition the window on screen and make things flaky. Initially, just
50   // assume that the contentRect of |target| is at the top-left corner of the
51   // screen.
52   NSRect content_rect = [target contentRectForFrameRect:[target frame]];
53   return NSMakePoint(point_in_root.x(),
54                      NSHeight(content_rect) - point_in_root.y());
57 // Inverse of ui::EventFlagsFromModifiers().
58 NSUInteger EventFlagsToModifiers(int flags) {
59   NSUInteger modifiers = 0;
60   modifiers |= (flags & ui::EF_CAPS_LOCK_DOWN) ? NSAlphaShiftKeyMask : 0;
61   modifiers |= (flags & ui::EF_SHIFT_DOWN) ? NSShiftKeyMask : 0;
62   modifiers |= (flags & ui::EF_CONTROL_DOWN) ? NSControlKeyMask : 0;
63   modifiers |= (flags & ui::EF_ALT_DOWN) ? NSAlternateKeyMask : 0;
64   modifiers |= (flags & ui::EF_COMMAND_DOWN) ? NSCommandKeyMask : 0;
65   // ui::EF_*_MOUSE_BUTTON not handled here.
66   // NSFunctionKeyMask, NSNumericPadKeyMask and NSHelpKeyMask not mapped.
67   return modifiers;
70 // Picks the corresponding mouse event type for the buttons set in |flags|.
71 NSEventType PickMouseEventType(int flags,
72                                NSEventType left,
73                                NSEventType right,
74                                NSEventType other) {
75   if (flags & ui::EF_LEFT_MOUSE_BUTTON)
76     return left;
77   if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
78     return right;
79   return other;
82 // Inverse of ui::EventTypeFromNative(). If non-null |modifiers| will be set
83 // using the inverse of ui::EventFlagsFromNSEventWithModifiers().
84 NSEventType EventTypeToNative(ui::EventType ui_event_type,
85                               int flags,
86                               NSUInteger* modifiers) {
87   if (modifiers)
88     *modifiers = EventFlagsToModifiers(flags);
89   switch (ui_event_type) {
90     case ui::ET_UNKNOWN:
91       return 0;
92     case ui::ET_KEY_PRESSED:
93       return NSKeyDown;
94     case ui::ET_KEY_RELEASED:
95       return NSKeyUp;
96     case ui::ET_MOUSE_PRESSED:
97       return PickMouseEventType(flags,
98                                 NSLeftMouseDown,
99                                 NSRightMouseDown,
100                                 NSOtherMouseDown);
101     case ui::ET_MOUSE_RELEASED:
102       return PickMouseEventType(flags,
103                                 NSLeftMouseUp,
104                                 NSRightMouseUp,
105                                 NSOtherMouseUp);
106     case ui::ET_MOUSE_DRAGGED:
107       return PickMouseEventType(flags,
108                                 NSLeftMouseDragged,
109                                 NSRightMouseDragged,
110                                 NSOtherMouseDragged);
111     case ui::ET_MOUSE_MOVED:
112       return NSMouseMoved;
113     case ui::ET_MOUSEWHEEL:
114       return NSScrollWheel;
115     case ui::ET_MOUSE_ENTERED:
116       return NSMouseEntered;
117     case ui::ET_MOUSE_EXITED:
118       return NSMouseExited;
119     case ui::ET_SCROLL_FLING_START:
120       return NSEventTypeSwipe;
121     default:
122       NOTREACHED();
123       return 0;
124   }
127 // Emulate the dispatching that would be performed by -[NSWindow sendEvent:].
128 // sendEvent is a black box which (among other things) will try to peek at the
129 // event queue and can block indefinitely.
130 void EmulateSendEvent(NSWindow* window, NSEvent* event) {
131   NSResponder* responder = [window firstResponder];
132   switch ([event type]) {
133     case NSKeyDown:
134       [responder keyDown:event];
135       return;
136     case NSKeyUp:
137       [responder keyUp:event];
138       return;
139   }
141   // For mouse events, NSWindow will use -[NSView hitTest:] for the initial
142   // mouseDown, and then keep track of the NSView returned. The toolkit-views
143   // RootView does this too. So, for tests, assume tracking will be done there,
144   // and the NSWindow's contentView is wrapping a views::internal::RootView.
145   responder = [window contentView];
146   switch ([event type]) {
147     case NSLeftMouseDown:
148       [responder mouseDown:event];
149       break;
150     case NSRightMouseDown:
151       [responder rightMouseDown:event];
152       break;
153     case NSOtherMouseDown:
154       [responder otherMouseDown:event];
155       break;
156     case NSLeftMouseUp:
157       [responder mouseUp:event];
158       break;
159     case NSRightMouseUp:
160       [responder rightMouseUp:event];
161       break;
162     case NSOtherMouseUp:
163       [responder otherMouseUp:event];
164       break;
165     case NSLeftMouseDragged:
166       [responder mouseDragged:event];
167       break;
168     case NSRightMouseDragged:
169       [responder rightMouseDragged:event];
170       break;
171     case NSOtherMouseDragged:
172       [responder otherMouseDragged:event];
173       break;
174     case NSMouseMoved:
175       // Assumes [NSWindow acceptsMouseMovedEvents] would return YES, and that
176       // NSTrackingAreas have been appropriately installed on |responder|.
177       [responder mouseMoved:event];
178       break;
179     case NSScrollWheel:
180       [responder scrollWheel:event];
181       break;
182     case NSMouseEntered:
183     case NSMouseExited:
184       // With the assumptions in NSMouseMoved, it doesn't make sense for the
185       // generator to handle entered/exited separately. It's the responsibility
186       // of views::internal::RootView to convert the moved events into entered
187       // and exited events for the individual views.
188       NOTREACHED();
189       break;
190     case NSEventTypeSwipe:
191       // NSEventTypeSwipe events can't be generated using public interfaces on
192       // NSEvent, so this will need to be handled at a higher level.
193       NOTREACHED();
194       break;
195     default:
196       NOTREACHED();
197   }
200 void DispatchMouseEventInWindow(NSWindow* window,
201                                 ui::EventType event_type,
202                                 const gfx::Point& point_in_root,
203                                 int flags) {
204   NSUInteger click_count = 0;
205   if (event_type == ui::ET_MOUSE_PRESSED ||
206       event_type == ui::ET_MOUSE_RELEASED) {
207     if (flags & ui::EF_IS_TRIPLE_CLICK)
208       click_count = 3;
209     else if (flags & ui::EF_IS_DOUBLE_CLICK)
210       click_count = 2;
211     else
212       click_count = 1;
213   }
214   NSPoint point = ConvertRootPointToTarget(window, point_in_root);
215   NSUInteger modifiers = 0;
216   NSEventType type = EventTypeToNative(event_type, flags, &modifiers);
217   NSEvent* event = [NSEvent mouseEventWithType:type
218                                       location:point
219                                  modifierFlags:modifiers
220                                      timestamp:0
221                                   windowNumber:[window windowNumber]
222                                        context:nil
223                                    eventNumber:0
224                                     clickCount:click_count
225                                       pressure:1.0];
227   // Typically events go through NSApplication. For tests, dispatch the event
228   // directly to make things more predicatble.
229   EmulateSendEvent(window, event);
232 // Implementation of ui::test::EventGeneratorDelegate for Mac. Everything
233 // defined inline is just a stub. Interesting overrides are defined below the
234 // class.
235 class EventGeneratorDelegateMac : public ui::EventTarget,
236                                   public ui::EventSource,
237                                   public ui::EventProcessor,
238                                   public ui::EventTargeter,
239                                   public ui::test::EventGeneratorDelegate {
240  public:
241   static EventGeneratorDelegateMac* GetInstance() {
242     return Singleton<EventGeneratorDelegateMac>::get();
243   }
245   // Overridden from ui::EventTarget:
246   virtual bool CanAcceptEvent(const ui::Event& event) override { return true; }
247   virtual ui::EventTarget* GetParentTarget()  override { return NULL; }
248   virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override;
249   virtual ui::EventTargeter* GetEventTargeter() override {
250     return this;
251   }
253   // Overridden from ui::EventHandler (via ui::EventTarget):
254   virtual void OnMouseEvent(ui::MouseEvent* event) override;
256   // Overridden from ui::EventSource:
257   virtual ui::EventProcessor* GetEventProcessor() override { return this; }
259   // Overridden from ui::EventProcessor:
260   virtual ui::EventTarget* GetRootTarget() override { return this; }
262   // Overridden from ui::EventDispatcherDelegate (via ui::EventProcessor):
263   virtual bool CanDispatchToTarget(EventTarget* target) override {
264     return true;
265   }
267   // Overridden from ui::test::EventGeneratorDelegate:
268   virtual void SetContext(ui::test::EventGenerator* owner,
269                           gfx::NativeWindow root_window,
270                           gfx::NativeWindow window) override;
271   virtual ui::EventTarget* GetTargetAt(const gfx::Point& location) override {
272     return this;
273   }
274   virtual ui::EventSource* GetEventSource(ui::EventTarget* target) override {
275     return this;
276   }
277   virtual gfx::Point CenterOfTarget(
278       const ui::EventTarget* target) const override;
279   virtual gfx::Point CenterOfWindow(gfx::NativeWindow window) const override;
281   virtual void ConvertPointFromTarget(const ui::EventTarget* target,
282                                       gfx::Point* point) const override {}
283   virtual void ConvertPointToTarget(const ui::EventTarget* target,
284                                     gfx::Point* point) const override {}
285   virtual void ConvertPointFromHost(const ui::EventTarget* hosted_target,
286                                     gfx::Point* point) const override {}
288  private:
289   friend struct DefaultSingletonTraits<EventGeneratorDelegateMac>;
291   EventGeneratorDelegateMac();
292   virtual ~EventGeneratorDelegateMac();
294   ui::test::EventGenerator* owner_;
295   NSWindow* window_;
296   scoped_ptr<base::mac::ScopedObjCClassSwizzler> swizzle_pressed_;
298   DISALLOW_COPY_AND_ASSIGN(EventGeneratorDelegateMac);
301 EventGeneratorDelegateMac::EventGeneratorDelegateMac()
302     : owner_(NULL),
303       window_(NULL) {
304   DCHECK(!ui::test::EventGenerator::default_delegate);
305   ui::test::EventGenerator::default_delegate = this;
308 EventGeneratorDelegateMac::~EventGeneratorDelegateMac() {
309   DCHECK_EQ(this, ui::test::EventGenerator::default_delegate);
310   ui::test::EventGenerator::default_delegate = NULL;
313 scoped_ptr<ui::EventTargetIterator>
314 EventGeneratorDelegateMac::GetChildIterator() const {
315   // Return NULL to dispatch all events to the result of GetRootTarget().
316   return scoped_ptr<ui::EventTargetIterator>();
319 void EventGeneratorDelegateMac::OnMouseEvent(ui::MouseEvent* event) {
320   // For mouse drag events, ensure the swizzled methods return the right flags.
321   base::AutoReset<ui::test::EventGenerator*> reset(&g_active_generator, owner_);
322   DispatchMouseEventInWindow(window_,
323                              event->type(),
324                              event->location(),
325                              event->changed_button_flags());
328 void EventGeneratorDelegateMac::SetContext(ui::test::EventGenerator* owner,
329                                            gfx::NativeWindow root_window,
330                                            gfx::NativeWindow window) {
331   swizzle_pressed_.reset();
332   owner_ = owner;
333   window_ = window;
334   if (owner_) {
335     swizzle_pressed_.reset(new base::mac::ScopedObjCClassSwizzler(
336         [NSEvent class],
337         [NSEventDonor class],
338         @selector(pressedMouseButtons)));
339   }
342 gfx::Point EventGeneratorDelegateMac::CenterOfTarget(
343     const ui::EventTarget* target) const {
344   DCHECK_EQ(target, this);
345   return CenterOfWindow(window_);
348 gfx::Point EventGeneratorDelegateMac::CenterOfWindow(
349     gfx::NativeWindow window) const {
350   DCHECK_EQ(window, window_);
351   return gfx::ScreenRectFromNSRect([window frame]).CenterPoint();
354 }  // namespace
356 namespace views {
357 namespace test {
359 void InitializeMacEventGeneratorDelegate() {
360   EventGeneratorDelegateMac::GetInstance();
363 }  // namespace test
364 }  // namespace views