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.
4
5#import "components/remote_cocoa/app_shim/mouse_capture.h"
6
7#import <Cocoa/Cocoa.h>
8
9#import "base/mac/scoped_nsobject.h"
10#include "base/macros.h"
11#import "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
12#import "ui/base/test/cocoa_helper.h"
13#import "ui/events/test/cocoa_test_event_utils.h"
14
15// Simple test view that counts calls to -[NSView mouseDown:].
16@interface CocoaMouseCaptureTestView : NSView {
17 @private
18  int _mouseDownCount;
19}
20@property(readonly, nonatomic) int mouseDownCount;
21@end
22
23@implementation CocoaMouseCaptureTestView
24
25@synthesize mouseDownCount = _mouseDownCount;
26
27- (void)mouseDown:(NSEvent*)theEvent {
28  ++_mouseDownCount;
29}
30
31@end
32
33namespace remote_cocoa {
34namespace {
35
36// Simple capture delegate that just counts events forwarded.
37class TestCaptureDelegate : public CocoaMouseCaptureDelegate {
38 public:
39  explicit TestCaptureDelegate(NSWindow* window)
40      : event_count_(0), capture_lost_count_(0), window_(window) {}
41
42  void Acquire() { mouse_capture_ = std::make_unique<CocoaMouseCapture>(this); }
43  bool IsActive() { return mouse_capture_ && mouse_capture_->IsActive(); }
44  void SimulateDestroy() { mouse_capture_.reset(); }
45
46  int event_count() { return event_count_; }
47  int capture_lost_count() { return capture_lost_count_; }
48
49  // CocoaMouseCaptureDelegate:
50  void PostCapturedEvent(NSEvent* event) override { ++event_count_; }
51  void OnMouseCaptureLost() override { ++capture_lost_count_; }
52  NSWindow* GetWindow() const override { return window_; }
53
54 private:
55  std::unique_ptr<CocoaMouseCapture> mouse_capture_;
56  int event_count_;
57  int capture_lost_count_;
58  NSWindow* window_;
59
60  DISALLOW_COPY_AND_ASSIGN(TestCaptureDelegate);
61};
62
63}  // namespace
64
65using CocoaMouseCaptureTest = ui::CocoaTest;
66
67// Test that a new capture properly "steals" capture from an existing one.
68TEST_F(CocoaMouseCaptureTest, OnCaptureLost) {
69  TestCaptureDelegate capture(nil);
70
71  capture.Acquire();
72  EXPECT_TRUE(capture.IsActive());
73  {
74    TestCaptureDelegate capture2(nil);
75    EXPECT_EQ(0, capture.capture_lost_count());
76
77    // A second capture steals from the first.
78    capture2.Acquire();
79    EXPECT_TRUE(capture2.IsActive());
80    EXPECT_FALSE(capture.IsActive());
81    EXPECT_EQ(1, capture.capture_lost_count());
82    EXPECT_EQ(0, capture2.capture_lost_count());
83
84    // Simulate capture2 going out of scope. Inspect it.
85    capture2.SimulateDestroy();
86    EXPECT_FALSE(capture2.IsActive());
87    EXPECT_EQ(1, capture2.capture_lost_count());
88  }
89
90  // Re-acquiring is fine (not stealing).
91  EXPECT_FALSE(capture.IsActive());
92  capture.Acquire();
93  EXPECT_TRUE(capture.IsActive());
94
95  // Having no CocoaMouseCapture instance is fine.
96  capture.SimulateDestroy();
97  EXPECT_FALSE(capture.IsActive());
98  // Receives OnMouseCaptureLost again, since reacquired.
99  EXPECT_EQ(2, capture.capture_lost_count());
100}
101
102// Test event capture.
103TEST_F(CocoaMouseCaptureTest, CaptureEvents) {
104  base::scoped_nsobject<CocoaMouseCaptureTestView> view(
105      [[CocoaMouseCaptureTestView alloc] initWithFrame:NSZeroRect]);
106  [test_window() setContentView:view];
107  std::pair<NSEvent*, NSEvent*> click =
108      cocoa_test_event_utils::MouseClickInView(view, 1);
109
110  // First check that the view receives events normally.
111  EXPECT_EQ(0, [view mouseDownCount]);
112  [NSApp sendEvent:click.first];
113  EXPECT_EQ(1, [view mouseDownCount]);
114
115  {
116    TestCaptureDelegate capture(test_window());
117    capture.Acquire();
118
119    // Now check that the capture captures events.
120    EXPECT_EQ(0, capture.event_count());
121    [NSApp sendEvent:click.first];
122    EXPECT_EQ(1, [view mouseDownCount]);
123    EXPECT_EQ(1, capture.event_count());
124  }
125
126  // After the capture goes away, events should be received again.
127  [NSApp sendEvent:click.first];
128  EXPECT_EQ(2, [view mouseDownCount]);
129}
130
131}  // namespace remote_cocoa
132