OLD | NEW |
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 "ui/views/cocoa/bridged_native_widget.h" | 5 #import "ui/views/cocoa/bridged_native_widget.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 | 8 |
9 #import "base/mac/mac_util.h" | 9 #import "base/mac/mac_util.h" |
10 #import "base/mac/sdk_forward_declarations.h" | 10 #import "base/mac/sdk_forward_declarations.h" |
11 #import "ui/base/test/nswindow_fullscreen_notification_waiter.h" | 11 #import "ui/base/test/nswindow_fullscreen_notification_waiter.h" |
| 12 #include "ui/base/hit_test.h" |
| 13 #import "ui/base/test/windowed_nsnotification_observer.h" |
| 14 #import "ui/events/test/cocoa_test_event_utils.h" |
12 #include "ui/views/test/widget_test.h" | 15 #include "ui/views/test/widget_test.h" |
| 16 #include "ui/views/window/native_frame_view.h" |
13 | 17 |
14 namespace views { | 18 namespace views { |
| 19 namespace test { |
15 | 20 |
16 class BridgedNativeWidgetUITest : public test::WidgetTest { | 21 class BridgedNativeWidgetUITest : public test::WidgetTest { |
17 public: | 22 public: |
18 BridgedNativeWidgetUITest() {} | 23 BridgedNativeWidgetUITest() {} |
19 | 24 |
20 // testing::Test: | 25 // testing::Test: |
21 void SetUp() override { | 26 void SetUp() override { |
22 WidgetTest::SetUp(); | 27 WidgetTest::SetUp(); |
23 Widget::InitParams init_params = | 28 Widget::InitParams init_params = |
24 CreateParams(Widget::InitParams::TYPE_WINDOW); | 29 CreateParams(Widget::InitParams::TYPE_WINDOW); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 widget_->SetFullscreen(false); | 142 widget_->SetFullscreen(false); |
138 EXPECT_FALSE(widget_->IsFullscreen()); | 143 EXPECT_FALSE(widget_->IsFullscreen()); |
139 EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds()); | 144 EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds()); |
140 | 145 |
141 [waiter waitForEnterCount:1 exitCount:1]; | 146 [waiter waitForEnterCount:1 exitCount:1]; |
142 EXPECT_EQ(1, [waiter enterCount]); | 147 EXPECT_EQ(1, [waiter enterCount]); |
143 EXPECT_EQ(1, [waiter exitCount]); | 148 EXPECT_EQ(1, [waiter exitCount]); |
144 EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds()); | 149 EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds()); |
145 } | 150 } |
146 | 151 |
| 152 namespace { |
| 153 |
| 154 // This is used to wait for reposted events to be seen. We can't just use |
| 155 // RunPendingMessages() because CGEventPost might not be synchronous. |
| 156 class HitTestBridgedNativeWidget : public BridgedNativeWidget { |
| 157 public: |
| 158 explicit HitTestBridgedNativeWidget(NativeWidgetMac* widget) |
| 159 : BridgedNativeWidget(widget) {} |
| 160 |
| 161 // BridgedNativeWidget: |
| 162 bool ShouldRepostPendingLeftMouseDown(NSPoint location_in_window) override { |
| 163 bool draggable_before = [ns_view() mouseDownCanMoveWindow]; |
| 164 bool should_repost = BridgedNativeWidget::ShouldRepostPendingLeftMouseDown( |
| 165 location_in_window); |
| 166 bool draggable_after = [ns_view() mouseDownCanMoveWindow]; |
| 167 |
| 168 if (run_loop_ && draggable_before && !draggable_after) |
| 169 run_loop_->Quit(); |
| 170 |
| 171 return should_repost; |
| 172 } |
| 173 |
| 174 void WaitForIsDraggableChange() { |
| 175 base::RunLoop run_loop; |
| 176 run_loop_ = &run_loop; |
| 177 run_loop.Run(); |
| 178 run_loop_ = nullptr; |
| 179 } |
| 180 |
| 181 private: |
| 182 base::RunLoop* run_loop_; |
| 183 |
| 184 DISALLOW_COPY_AND_ASSIGN(HitTestBridgedNativeWidget); |
| 185 }; |
| 186 |
| 187 // This is used to return a customized result to NonClientHitTest. |
| 188 class HitTestNonClientFrameView : public NativeFrameView { |
| 189 public: |
| 190 explicit HitTestNonClientFrameView(Widget* widget) |
| 191 : NativeFrameView(widget), hit_test_result_(HTNOWHERE) {} |
| 192 |
| 193 // NonClientFrameView overrides: |
| 194 int NonClientHitTest(const gfx::Point& point) override { |
| 195 return hit_test_result_; |
| 196 } |
| 197 |
| 198 void set_hit_test_result(int component) { hit_test_result_ = component; } |
| 199 |
| 200 private: |
| 201 int hit_test_result_; |
| 202 |
| 203 DISALLOW_COPY_AND_ASSIGN(HitTestNonClientFrameView); |
| 204 }; |
| 205 |
| 206 void WaitForEvent(NSUInteger mask) { |
| 207 // Pointer because the handler block captures local variables by copying. |
| 208 base::RunLoop run_loop; |
| 209 base::RunLoop* run_loop_ref = &run_loop; |
| 210 id monitor = [NSEvent |
| 211 addLocalMonitorForEventsMatchingMask:mask |
| 212 handler:^NSEvent*(NSEvent* ns_event) { |
| 213 run_loop_ref->Quit(); |
| 214 return ns_event; |
| 215 }]; |
| 216 run_loop.Run(); |
| 217 [NSEvent removeMonitor:monitor]; |
| 218 } |
| 219 |
| 220 } // namespace |
| 221 |
| 222 // This is used to inject test versions of NativeFrameView and |
| 223 // BridgedNativeWidget. |
| 224 class HitTestNativeWidgetMac : public NativeWidgetMac { |
| 225 public: |
| 226 HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate, |
| 227 NativeFrameView* native_frame_view) |
| 228 : NativeWidgetMac(delegate), native_frame_view_(native_frame_view) { |
| 229 NativeWidgetMac::bridge_.reset(new HitTestBridgedNativeWidget(this)); |
| 230 } |
| 231 |
| 232 HitTestBridgedNativeWidget* bridge() { |
| 233 return static_cast<HitTestBridgedNativeWidget*>( |
| 234 NativeWidgetMac::bridge_.get()); |
| 235 } |
| 236 |
| 237 // internal::NativeWidgetPrivate: |
| 238 NonClientFrameView* CreateNonClientFrameView() override { |
| 239 return native_frame_view_; |
| 240 } |
| 241 |
| 242 private: |
| 243 // Owned by Widget. |
| 244 NativeFrameView* native_frame_view_; |
| 245 |
| 246 DISALLOW_COPY_AND_ASSIGN(HitTestNativeWidgetMac); |
| 247 }; |
| 248 |
| 249 TEST_F(BridgedNativeWidgetUITest, HitTest) { |
| 250 Widget widget; |
| 251 HitTestNonClientFrameView* frame_view = |
| 252 new HitTestNonClientFrameView(&widget); |
| 253 test::HitTestNativeWidgetMac* native_widget = |
| 254 new test::HitTestNativeWidgetMac(&widget, frame_view); |
| 255 Widget::InitParams init_params = |
| 256 CreateParams(Widget::InitParams::TYPE_WINDOW); |
| 257 init_params.native_widget = native_widget; |
| 258 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 259 init_params.bounds = gfx::Rect(100, 200, 400, 300); |
| 260 widget.Init(init_params); |
| 261 widget.Show(); |
| 262 |
| 263 // Dragging the window should work. |
| 264 frame_view->set_hit_test_result(HTCAPTION); |
| 265 { |
| 266 EXPECT_EQ(100, [widget.GetNativeWindow() frame].origin.x); |
| 267 |
| 268 NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( |
| 269 NSMakePoint(10, 10), widget.GetNativeWindow()); |
| 270 CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); |
| 271 native_widget->bridge()->WaitForIsDraggableChange(); |
| 272 |
| 273 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( |
| 274 [[WindowedNSNotificationObserver alloc] |
| 275 initForNotification:NSWindowDidMoveNotification]); |
| 276 NSEvent* mouse_drag = cocoa_test_event_utils::MouseEventAtPointInWindow( |
| 277 NSMakePoint(110, 110), NSLeftMouseDragged, widget.GetNativeWindow(), 0); |
| 278 CGEventPost(kCGSessionEventTap, [mouse_drag CGEvent]); |
| 279 [ns_observer wait]; |
| 280 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 281 |
| 282 NSEvent* mouse_up = cocoa_test_event_utils::MouseEventAtPointInWindow( |
| 283 NSMakePoint(10, 10), NSLeftMouseUp, widget.GetNativeWindow(), 0); |
| 284 CGEventPost(kCGSessionEventTap, [mouse_up CGEvent]); |
| 285 WaitForEvent(NSLeftMouseUpMask); |
| 286 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 287 } |
| 288 |
| 289 // Mouse-downs on the window controls should not be intercepted. |
| 290 { |
| 291 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 292 |
| 293 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( |
| 294 [[WindowedNSNotificationObserver alloc] |
| 295 initForNotification:NSWindowDidMiniaturizeNotification]); |
| 296 |
| 297 // Position this on the minimize button. |
| 298 NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( |
| 299 NSMakePoint(30, 290), widget.GetNativeWindow()); |
| 300 CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); |
| 301 |
| 302 NSEvent* mouse_up = cocoa_test_event_utils::MouseEventAtPointInWindow( |
| 303 NSMakePoint(30, 290), NSLeftMouseUp, widget.GetNativeWindow(), 0); |
| 304 EXPECT_FALSE([widget.GetNativeWindow() isMiniaturized]); |
| 305 CGEventPost(kCGSessionEventTap, [mouse_up CGEvent]); |
| 306 [ns_observer wait]; |
| 307 EXPECT_TRUE([widget.GetNativeWindow() isMiniaturized]); |
| 308 [widget.GetNativeWindow() deminiaturize:nil]; |
| 309 |
| 310 // Position unchanged. |
| 311 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 312 } |
| 313 |
| 314 // Non-draggable areas should do nothing. |
| 315 frame_view->set_hit_test_result(HTCLIENT); |
| 316 { |
| 317 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 318 |
| 319 NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( |
| 320 NSMakePoint(10, 10), widget.GetNativeWindow()); |
| 321 CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); |
| 322 WaitForEvent(NSLeftMouseDownMask); |
| 323 |
| 324 NSEvent* mouse_drag = cocoa_test_event_utils::MouseEventAtPointInWindow( |
| 325 NSMakePoint(110, 110), NSLeftMouseDragged, widget.GetNativeWindow(), 0); |
| 326 CGEventPost(kCGSessionEventTap, [mouse_drag CGEvent]); |
| 327 WaitForEvent(NSLeftMouseDraggedMask); |
| 328 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 329 |
| 330 NSEvent* mouse_up = cocoa_test_event_utils::MouseEventAtPointInWindow( |
| 331 NSMakePoint(110, 110), NSLeftMouseUp, widget.GetNativeWindow(), 0); |
| 332 CGEventPost(kCGSessionEventTap, [mouse_up CGEvent]); |
| 333 WaitForEvent(NSLeftMouseUpMask); |
| 334 EXPECT_EQ(200, [widget.GetNativeWindow() frame].origin.x); |
| 335 } |
| 336 } |
| 337 |
| 338 } // namespace test |
147 } // namespace views | 339 } // namespace views |
OLD | NEW |