Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / ui / views / widget / widget_interactive_uitest.cc
blobff00ed156793d60d6f5ee0c385617149c588cdc2
1 // Copyright (c) 2013 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 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/path_service.h"
9 #include "base/run_loop.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/base/ui_base_paths.h"
14 #include "ui/base/ui_base_switches.h"
15 #include "ui/events/event_processor.h"
16 #include "ui/events/test/event_generator.h"
17 #include "ui/gfx/native_widget_types.h"
18 #include "ui/gl/gl_surface.h"
19 #include "ui/views/controls/textfield/textfield.h"
20 #include "ui/views/controls/textfield/textfield_test_api.h"
21 #include "ui/views/focus/focus_manager.h"
22 #include "ui/views/test/focus_manager_test.h"
23 #include "ui/views/test/widget_test.h"
24 #include "ui/views/touchui/touch_selection_controller_impl.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/views/window/dialog_delegate.h"
27 #include "ui/wm/core/default_screen_position_client.h"
28 #include "ui/wm/public/activation_client.h"
30 #if defined(OS_WIN)
31 #include "ui/aura/window.h"
32 #include "ui/aura/window_tree_host.h"
33 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
34 #include "ui/views/win/hwnd_util.h"
35 #endif
37 namespace views {
38 namespace test {
40 namespace {
42 // A View that closes the Widget and exits the current message-loop when it
43 // receives a mouse-release event.
44 class ExitLoopOnRelease : public View {
45 public:
46 ExitLoopOnRelease() {}
47 ~ExitLoopOnRelease() override {}
49 private:
50 // Overridden from View:
51 void OnMouseReleased(const ui::MouseEvent& event) override {
52 GetWidget()->Close();
53 base::MessageLoop::current()->QuitNow();
56 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
59 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
60 class GestureCaptureView : public View {
61 public:
62 GestureCaptureView() {}
63 ~GestureCaptureView() override {}
65 private:
66 // Overridden from View:
67 void OnGestureEvent(ui::GestureEvent* event) override {
68 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
69 GetWidget()->SetCapture(this);
70 event->StopPropagation();
74 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
77 // A view that always processes all mouse events.
78 class MouseView : public View {
79 public:
80 MouseView()
81 : View(),
82 entered_(0),
83 exited_(0),
84 pressed_(0) {
86 ~MouseView() override {}
88 bool OnMousePressed(const ui::MouseEvent& event) override {
89 pressed_++;
90 return true;
93 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
95 void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
97 // Return the number of OnMouseEntered calls and reset the counter.
98 int EnteredCalls() {
99 int i = entered_;
100 entered_ = 0;
101 return i;
104 // Return the number of OnMouseExited calls and reset the counter.
105 int ExitedCalls() {
106 int i = exited_;
107 exited_ = 0;
108 return i;
111 int pressed() const { return pressed_; }
113 private:
114 int entered_;
115 int exited_;
117 int pressed_;
119 DISALLOW_COPY_AND_ASSIGN(MouseView);
122 // A View that shows a different widget, sets capture on that widget, and
123 // initiates a nested message-loop when it receives a mouse-press event.
124 class NestedLoopCaptureView : public View {
125 public:
126 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
127 ~NestedLoopCaptureView() override {}
129 private:
130 // Overridden from View:
131 bool OnMousePressed(const ui::MouseEvent& event) override {
132 // Start a nested loop.
133 widget_->Show();
134 widget_->SetCapture(widget_->GetContentsView());
135 EXPECT_TRUE(widget_->HasCapture());
137 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
138 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
140 base::RunLoop run_loop;
141 run_loop.Run();
142 return true;
145 Widget* widget_;
147 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
150 } // namespace
152 class WidgetTestInteractive : public WidgetTest {
153 public:
154 WidgetTestInteractive() {}
155 ~WidgetTestInteractive() override {}
157 void SetUp() override {
158 gfx::GLSurface::InitializeOneOffForTests();
159 ui::RegisterPathProvider();
160 base::FilePath ui_test_pak_path;
161 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
162 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
163 WidgetTest::SetUp();
166 protected:
167 static void ShowQuickMenuImmediately(
168 TouchSelectionControllerImpl* controller) {
169 DCHECK(controller);
170 if (controller->context_menu_timer_.IsRunning()) {
171 controller->context_menu_timer_.Stop();
172 // TODO(tapted): Enable this when porting ui/views/touchui to Mac.
173 #if !defined(OS_MACOSX)
174 controller->ContextMenuTimerFired();
175 #endif
179 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
180 DCHECK(controller);
181 return controller->context_menu_ && controller->context_menu_->visible();
185 #if defined(OS_WIN)
186 // Tests whether activation and focus change works correctly in Windows.
187 // We test the following:-
188 // 1. If the active aura window is correctly set when a top level widget is
189 // created.
190 // 2. If the active aura window in widget 1 created above, is set to NULL when
191 // another top level widget is created and focused.
192 // 3. On focusing the native platform window for widget 1, the active aura
193 // window for widget 1 should be set and that for widget 2 should reset.
194 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
195 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
196 // Create widget 1 and expect the active window to be its window.
197 View* contents_view1 = new View;
198 contents_view1->SetFocusable(true);
199 Widget widget1;
200 Widget::InitParams init_params =
201 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
202 init_params.bounds = gfx::Rect(0, 0, 200, 200);
203 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
204 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
205 widget1.Init(init_params);
206 widget1.SetContentsView(contents_view1);
207 widget1.Show();
208 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
209 contents_view1->RequestFocus();
211 EXPECT_TRUE(root_window1 != NULL);
212 aura::client::ActivationClient* activation_client1 =
213 aura::client::GetActivationClient(root_window1);
214 EXPECT_TRUE(activation_client1 != NULL);
215 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
217 // Create widget 2 and expect the active window to be its window.
218 View* contents_view2 = new View;
219 Widget widget2;
220 Widget::InitParams init_params2 =
221 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
222 init_params2.bounds = gfx::Rect(0, 0, 200, 200);
223 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
224 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
225 widget2.Init(init_params2);
226 widget2.SetContentsView(contents_view2);
227 widget2.Show();
228 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
229 contents_view2->RequestFocus();
230 ::SetActiveWindow(
231 root_window2->GetHost()->GetAcceleratedWidget());
233 aura::client::ActivationClient* activation_client2 =
234 aura::client::GetActivationClient(root_window2);
235 EXPECT_TRUE(activation_client2 != NULL);
236 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
237 EXPECT_EQ(activation_client1->GetActiveWindow(),
238 reinterpret_cast<aura::Window*>(NULL));
240 // Now set focus back to widget 1 and expect the active window to be its
241 // window.
242 contents_view1->RequestFocus();
243 ::SetActiveWindow(
244 root_window1->GetHost()->GetAcceleratedWidget());
245 EXPECT_EQ(activation_client2->GetActiveWindow(),
246 reinterpret_cast<aura::Window*>(NULL));
247 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
249 #endif // defined(OS_WIN)
251 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
252 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
253 View* container = new View;
254 toplevel->SetContentsView(container);
256 EXPECT_FALSE(toplevel->HasCapture());
257 toplevel->SetCapture(NULL);
258 EXPECT_TRUE(toplevel->HasCapture());
260 // By default, mouse release removes capture.
261 gfx::Point click_location(45, 15);
262 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
263 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
264 toplevel->OnMouseEvent(&release);
265 EXPECT_FALSE(toplevel->HasCapture());
267 // Now a mouse release shouldn't remove capture.
268 toplevel->set_auto_release_capture(false);
269 toplevel->SetCapture(NULL);
270 EXPECT_TRUE(toplevel->HasCapture());
271 toplevel->OnMouseEvent(&release);
272 EXPECT_TRUE(toplevel->HasCapture());
273 toplevel->ReleaseCapture();
274 EXPECT_FALSE(toplevel->HasCapture());
276 toplevel->Close();
277 RunPendingMessages();
280 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
281 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
282 View* container = new View;
283 toplevel->SetContentsView(container);
285 View* gesture = new GestureCaptureView;
286 gesture->SetBounds(0, 0, 30, 30);
287 container->AddChildView(gesture);
289 MouseView* mouse = new MouseView;
290 mouse->SetBounds(30, 0, 30, 30);
291 container->AddChildView(mouse);
293 toplevel->SetSize(gfx::Size(100, 100));
294 toplevel->Show();
296 // Start a gesture on |gesture|.
297 ui::GestureEvent tap_down(15,
300 base::TimeDelta(),
301 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
302 ui::GestureEvent end(15,
305 base::TimeDelta(),
306 ui::GestureEventDetails(ui::ET_GESTURE_END));
307 toplevel->OnGestureEvent(&tap_down);
309 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
310 // will not receive the event.
311 gfx::Point click_location(45, 15);
313 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
314 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
315 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
316 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
318 EXPECT_TRUE(toplevel->HasCapture());
320 toplevel->OnMouseEvent(&press);
321 toplevel->OnMouseEvent(&release);
322 EXPECT_EQ(0, mouse->pressed());
324 EXPECT_FALSE(toplevel->HasCapture());
326 // The end of the gesture should release the capture, and pressing on |mouse|
327 // should now reach |mouse|.
328 toplevel->OnGestureEvent(&end);
329 toplevel->OnMouseEvent(&press);
330 toplevel->OnMouseEvent(&release);
331 EXPECT_EQ(1, mouse->pressed());
333 toplevel->Close();
334 RunPendingMessages();
337 // Checks that if a mouse-press triggers a capture on a different widget (which
338 // consumes the mouse-release event), then the target of the press does not have
339 // capture.
340 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
341 // The test creates two widgets: |first| and |second|.
342 // The View in |first| makes |second| visible, sets capture on it, and starts
343 // a nested loop (like a menu does). The View in |second| terminates the
344 // nested loop and closes the widget.
345 // The test sends a mouse-press event to |first|, and posts a task to send a
346 // release event to |second|, to make sure that the release event is
347 // dispatched after the nested loop starts.
349 Widget* first = CreateTopLevelFramelessPlatformWidget();
350 Widget* second = CreateTopLevelFramelessPlatformWidget();
352 View* container = new NestedLoopCaptureView(second);
353 first->SetContentsView(container);
355 second->SetContentsView(new ExitLoopOnRelease());
357 first->SetSize(gfx::Size(100, 100));
358 first->Show();
360 gfx::Point location(20, 20);
361 base::MessageLoop::current()->PostTask(FROM_HERE,
362 base::Bind(&Widget::OnMouseEvent,
363 base::Unretained(second),
364 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
365 location,
366 location,
367 ui::EF_LEFT_MOUSE_BUTTON,
368 ui::EF_LEFT_MOUSE_BUTTON))));
369 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
370 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
371 first->OnMouseEvent(&press);
372 EXPECT_FALSE(first->HasCapture());
373 first->Close();
374 RunPendingMessages();
377 // Tests some grab/ungrab events.
378 // TODO(estade): can this be enabled now that this is an interactive ui test?
379 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
380 Widget* toplevel = CreateTopLevelPlatformWidget();
381 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
382 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
384 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
386 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
387 View* view = new MouseView();
388 view->SetBounds(0, 0, 300, 300);
389 child1->GetRootView()->AddChildView(view);
391 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
392 view = new MouseView();
393 view->SetBounds(0, 0, 200, 200);
394 child2->GetRootView()->AddChildView(view);
396 toplevel->Show();
397 RunPendingMessages();
399 // Click on child1
400 gfx::Point p1(45, 45);
401 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
402 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
403 toplevel->OnMouseEvent(&pressed);
405 EXPECT_TRUE(toplevel->HasCapture());
406 EXPECT_TRUE(child1->HasCapture());
407 EXPECT_FALSE(child2->HasCapture());
409 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
410 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
411 toplevel->OnMouseEvent(&released);
413 EXPECT_FALSE(toplevel->HasCapture());
414 EXPECT_FALSE(child1->HasCapture());
415 EXPECT_FALSE(child2->HasCapture());
417 RunPendingMessages();
419 // Click on child2
420 gfx::Point p2(315, 45);
421 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
422 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
423 toplevel->OnMouseEvent(&pressed2);
424 EXPECT_TRUE(pressed2.handled());
425 EXPECT_TRUE(toplevel->HasCapture());
426 EXPECT_TRUE(child2->HasCapture());
427 EXPECT_FALSE(child1->HasCapture());
429 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
430 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
431 toplevel->OnMouseEvent(&released2);
432 EXPECT_FALSE(toplevel->HasCapture());
433 EXPECT_FALSE(child1->HasCapture());
434 EXPECT_FALSE(child2->HasCapture());
436 toplevel->CloseNow();
439 // Tests mouse move outside of the window into the "resize controller" and back
440 // will still generate an OnMouseEntered and OnMouseExited event..
441 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
442 Widget* toplevel = CreateTopLevelPlatformWidget();
444 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
446 MouseView* view = new MouseView();
447 view->SetBounds(90, 90, 10, 10);
448 toplevel->GetRootView()->AddChildView(view);
450 toplevel->Show();
451 RunPendingMessages();
453 // Move to an outside position.
454 gfx::Point p1(200, 200);
455 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE,
456 ui::EF_NONE);
457 toplevel->OnMouseEvent(&moved_out);
458 EXPECT_EQ(0, view->EnteredCalls());
459 EXPECT_EQ(0, view->ExitedCalls());
461 // Move onto the active view.
462 gfx::Point p2(95, 95);
463 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE,
464 ui::EF_NONE);
465 toplevel->OnMouseEvent(&moved_over);
466 EXPECT_EQ(1, view->EnteredCalls());
467 EXPECT_EQ(0, view->ExitedCalls());
469 // Move onto the outer resizing border.
470 gfx::Point p3(102, 95);
471 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE,
472 ui::EF_NONE);
473 toplevel->OnMouseEvent(&moved_resizer);
474 EXPECT_EQ(0, view->EnteredCalls());
475 EXPECT_EQ(1, view->ExitedCalls());
477 // Move onto the view again.
478 toplevel->OnMouseEvent(&moved_over);
479 EXPECT_EQ(1, view->EnteredCalls());
480 EXPECT_EQ(0, view->ExitedCalls());
482 RunPendingMessages();
484 toplevel->CloseNow();
487 // Test view focus restoration when a widget is deactivated and re-activated.
488 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
489 Widget* widget1 = CreateTopLevelPlatformWidget();
490 View* view1 = new View;
491 view1->SetFocusable(true);
492 widget1->GetContentsView()->AddChildView(view1);
494 Widget* widget2 = CreateTopLevelPlatformWidget();
495 View* view2a = new View;
496 View* view2b = new View;
497 view2a->SetFocusable(true);
498 view2b->SetFocusable(true);
499 widget2->GetContentsView()->AddChildView(view2a);
500 widget2->GetContentsView()->AddChildView(view2b);
502 widget1->Show();
503 EXPECT_TRUE(widget1->IsActive());
504 view1->RequestFocus();
505 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
507 widget2->Show();
508 EXPECT_TRUE(widget2->IsActive());
509 EXPECT_FALSE(widget1->IsActive());
510 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
511 view2a->RequestFocus();
512 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
513 view2b->RequestFocus();
514 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
516 widget1->Activate();
517 EXPECT_TRUE(widget1->IsActive());
518 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
519 EXPECT_FALSE(widget2->IsActive());
520 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
522 widget2->Activate();
523 EXPECT_TRUE(widget2->IsActive());
524 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
525 EXPECT_FALSE(widget1->IsActive());
526 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
528 widget1->CloseNow();
529 widget2->CloseNow();
532 #if defined(OS_WIN)
534 // Test view focus retention when a widget's HWND is disabled and re-enabled.
535 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
536 Widget* widget = CreateTopLevelFramelessPlatformWidget();
537 widget->SetContentsView(new View);
538 for (size_t i = 0; i < 2; ++i) {
539 widget->GetContentsView()->AddChildView(new View);
540 widget->GetContentsView()->child_at(i)->SetFocusable(true);
543 widget->Show();
544 const HWND hwnd = HWNDForWidget(widget);
545 EXPECT_TRUE(::IsWindow(hwnd));
546 EXPECT_TRUE(::IsWindowEnabled(hwnd));
547 EXPECT_EQ(hwnd, ::GetActiveWindow());
549 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
550 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
551 View* view = widget->GetContentsView()->child_at(i);
553 view->RequestFocus();
554 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
555 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
556 EXPECT_FALSE(::IsWindowEnabled(hwnd));
558 // Oddly, disabling the HWND leaves it active with the focus unchanged.
559 EXPECT_EQ(hwnd, ::GetActiveWindow());
560 EXPECT_TRUE(widget->IsActive());
561 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
563 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
564 EXPECT_TRUE(::IsWindowEnabled(hwnd));
565 EXPECT_EQ(hwnd, ::GetActiveWindow());
566 EXPECT_TRUE(widget->IsActive());
567 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
570 widget->CloseNow();
573 // This class subclasses the Widget class to listen for activation change
574 // notifications and provides accessors to return information as to whether
575 // the widget is active. We need this to ensure that users of the widget
576 // class activate the widget only when the underlying window becomes really
577 // active. Previously we would activate the widget in the WM_NCACTIVATE
578 // message which is incorrect because APIs like FlashWindowEx flash the
579 // window caption by sending fake WM_NCACTIVATE messages.
580 class WidgetActivationTest : public Widget {
581 public:
582 WidgetActivationTest()
583 : active_(false) {}
585 virtual ~WidgetActivationTest() {}
587 virtual void OnNativeWidgetActivationChanged(bool active) override {
588 active_ = active;
591 bool active() const { return active_; }
593 private:
594 bool active_;
596 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
599 // Tests whether the widget only becomes active when the underlying window
600 // is really active.
601 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
602 WidgetActivationTest widget1;
603 Widget::InitParams init_params =
604 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
605 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
606 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
607 init_params.bounds = gfx::Rect(0, 0, 200, 200);
608 widget1.Init(init_params);
609 widget1.Show();
610 EXPECT_EQ(true, widget1.active());
612 WidgetActivationTest widget2;
613 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
614 widget2.Init(init_params);
615 widget2.Show();
616 EXPECT_EQ(true, widget2.active());
617 EXPECT_EQ(false, widget1.active());
619 HWND win32_native_window1 = HWNDForWidget(&widget1);
620 EXPECT_TRUE(::IsWindow(win32_native_window1));
622 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
623 EXPECT_EQ(false, widget1.active());
624 EXPECT_EQ(true, widget2.active());
626 ::SetActiveWindow(win32_native_window1);
627 EXPECT_EQ(true, widget1.active());
628 EXPECT_EQ(false, widget2.active());
630 #endif // defined(OS_WIN)
632 #if !defined(OS_CHROMEOS)
633 // Provides functionality to create a window modal dialog.
634 class ModalDialogDelegate : public DialogDelegateView {
635 public:
636 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
637 ~ModalDialogDelegate() override {}
639 // WidgetDelegate overrides.
640 ui::ModalType GetModalType() const override { return type_; }
642 private:
643 ui::ModalType type_;
645 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
648 // Tests whether the focused window is set correctly when a modal window is
649 // created and destroyed. When it is destroyed it should focus the owner window.
650 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
651 TestWidgetFocusChangeListener focus_listener;
652 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
653 const std::vector<NativeViewPair>& focus_changes =
654 focus_listener.focus_changes();
656 // Create a top level widget.
657 Widget top_level_widget;
658 Widget::InitParams init_params =
659 CreateParams(Widget::InitParams::TYPE_WINDOW);
660 init_params.show_state = ui::SHOW_STATE_NORMAL;
661 gfx::Rect initial_bounds(0, 0, 500, 500);
662 init_params.bounds = initial_bounds;
663 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
664 init_params.native_widget =
665 new PlatformDesktopNativeWidget(&top_level_widget);
666 top_level_widget.Init(init_params);
667 top_level_widget.Show();
669 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
670 EXPECT_EQ(1u, focus_changes.size());
671 EXPECT_EQ(NativeViewPair(NULL, top_level_native_view), focus_changes[0]);
673 // Create a modal dialog.
674 // This instance will be destroyed when the dialog is destroyed.
675 ModalDialogDelegate* dialog_delegate =
676 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
678 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
679 dialog_delegate, NULL, top_level_widget.GetNativeView());
680 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
681 modal_dialog_widget->Show();
683 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
684 EXPECT_EQ(3u, focus_changes.size());
685 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
686 focus_changes[1]);
687 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
688 focus_changes[2]);
690 modal_dialog_widget->CloseNow();
692 EXPECT_EQ(5u, focus_changes.size());
693 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
694 focus_changes[3]);
695 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
696 focus_changes[4]);
698 top_level_widget.CloseNow();
699 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
702 // Test that when opening a system-modal window, capture is released.
703 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) {
704 TestWidgetFocusChangeListener focus_listener;
705 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
707 // Create a top level widget.
708 Widget top_level_widget;
709 Widget::InitParams init_params =
710 CreateParams(Widget::InitParams::TYPE_WINDOW);
711 init_params.show_state = ui::SHOW_STATE_NORMAL;
712 gfx::Rect initial_bounds(0, 0, 500, 500);
713 init_params.bounds = initial_bounds;
714 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
715 init_params.native_widget =
716 new PlatformDesktopNativeWidget(&top_level_widget);
717 top_level_widget.Init(init_params);
718 top_level_widget.Show();
720 EXPECT_EQ(top_level_widget.GetNativeView(),
721 focus_listener.focus_changes().back().second);;
723 EXPECT_FALSE(top_level_widget.HasCapture());
724 top_level_widget.SetCapture(NULL);
725 EXPECT_TRUE(top_level_widget.HasCapture());
727 // Create a modal dialog.
728 ModalDialogDelegate* dialog_delegate =
729 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
731 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
732 dialog_delegate, NULL, top_level_widget.GetNativeView());
733 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
734 modal_dialog_widget->Show();
736 EXPECT_FALSE(top_level_widget.HasCapture());
738 modal_dialog_widget->CloseNow();
739 top_level_widget.CloseNow();
740 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
743 #endif // !defined(OS_CHROMEOS)
745 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
746 Widget widget;
747 Widget::InitParams init_params =
748 CreateParams(Widget::InitParams::TYPE_WINDOW);
749 init_params.bounds = gfx::Rect(0, 0, 200, 200);
750 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
751 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
752 #if !defined(OS_CHROMEOS)
753 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
754 #endif // !defined(OS_CHROMEOS)
755 widget.Init(init_params);
757 widget.Show();
758 EXPECT_FALSE(widget.IsActive());
761 // Test that touch selection quick menu is not activated when opened.
762 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
763 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing);
764 #if defined(OS_WIN)
765 views_delegate().set_use_desktop_native_widgets(true);
766 #endif // !defined(OS_WIN)
768 wm::DefaultScreenPositionClient screen_position_client;
769 Widget widget;
770 Widget::InitParams init_params =
771 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
772 init_params.bounds = gfx::Rect(0, 0, 200, 200);
773 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
774 widget.Init(init_params);
776 Textfield* textfield = new Textfield;
777 textfield->SetBounds(0, 0, 200, 20);
778 textfield->SetText(base::ASCIIToUTF16("some text"));
779 widget.GetRootView()->AddChildView(textfield);
781 widget.Show();
782 textfield->RequestFocus();
783 textfield->SelectAll(true);
784 TextfieldTestApi textfield_test_api(textfield);
785 #if defined(USE_AURA)
786 aura::client::SetScreenPositionClient(widget.GetNativeView()->GetRootWindow(),
787 &screen_position_client);
788 #endif // !defined(USE_AURA)
790 RunPendingMessages();
792 ui::test::EventGenerator generator(widget.GetNativeWindow());
793 generator.GestureTapAt(gfx::Point(10, 10));
794 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
795 textfield_test_api.touch_selection_controller()));
797 EXPECT_TRUE(textfield->HasFocus());
798 EXPECT_TRUE(widget.IsActive());
799 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>(
800 textfield_test_api.touch_selection_controller())));
801 aura::client::SetScreenPositionClient(widget.GetNativeView()->GetRootWindow(),
802 nullptr);
805 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
806 #if defined(OS_WIN)
807 views_delegate().set_use_desktop_native_widgets(true);
808 #endif // !defined(OS_WIN)
810 // Create first widget and view, activate the widget, and focus the view.
811 Widget widget1;
812 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
813 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
814 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
815 widget1.Init(params1);
817 View* view1 = new View();
818 view1->SetFocusable(true);
819 widget1.GetRootView()->AddChildView(view1);
821 widget1.Activate();
822 EXPECT_TRUE(widget1.IsActive());
824 FocusManager* focus_manager1 = widget1.GetFocusManager();
825 ASSERT_TRUE(focus_manager1);
826 focus_manager1->SetFocusedView(view1);
827 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
829 // Create second widget and view, activate the widget, and focus the view.
830 Widget widget2;
831 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
832 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
833 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
834 widget2.Init(params2);
836 View* view2 = new View();
837 view2->SetFocusable(true);
838 widget2.GetRootView()->AddChildView(view2);
840 widget2.Activate();
841 EXPECT_TRUE(widget2.IsActive());
842 EXPECT_FALSE(widget1.IsActive());
844 FocusManager* focus_manager2 = widget2.GetFocusManager();
845 ASSERT_TRUE(focus_manager2);
846 focus_manager2->SetFocusedView(view2);
847 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
849 // Disable the first view and make sure it loses focus, but its widget is not
850 // activated.
851 view1->SetEnabled(false);
852 EXPECT_NE(view1, focus_manager1->GetFocusedView());
853 EXPECT_FALSE(widget1.IsActive());
854 EXPECT_TRUE(widget2.IsActive());
857 namespace {
859 // Used to veirfy OnMouseCaptureLost() has been invoked.
860 class CaptureLostTrackingWidget : public Widget {
861 public:
862 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
863 ~CaptureLostTrackingWidget() override {}
865 bool GetAndClearGotCaptureLost() {
866 bool value = got_capture_lost_;
867 got_capture_lost_ = false;
868 return value;
871 // Widget:
872 void OnMouseCaptureLost() override {
873 got_capture_lost_ = true;
874 Widget::OnMouseCaptureLost();
877 private:
878 bool got_capture_lost_;
880 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
883 } // namespace
885 class WidgetCaptureTest : public ViewsTestBase {
886 public:
887 WidgetCaptureTest() {
890 ~WidgetCaptureTest() override {}
892 void SetUp() override {
893 gfx::GLSurface::InitializeOneOffForTests();
894 ui::RegisterPathProvider();
895 base::FilePath ui_test_pak_path;
896 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
897 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
898 ViewsTestBase::SetUp();
901 // Verifies Widget::SetCapture() results in updating native capture along with
902 // invoking the right Widget function.
903 void TestCapture(bool use_desktop_native_widget) {
904 CaptureLostTrackingWidget widget1;
905 Widget::InitParams params1 =
906 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
907 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
908 &widget1);
909 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
910 widget1.Init(params1);
911 widget1.Show();
913 CaptureLostTrackingWidget widget2;
914 Widget::InitParams params2 =
915 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
916 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
917 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
918 &widget2);
919 widget2.Init(params2);
920 widget2.Show();
922 // Set capture to widget2 and verity it gets it.
923 widget2.SetCapture(widget2.GetRootView());
924 EXPECT_FALSE(widget1.HasCapture());
925 EXPECT_TRUE(widget2.HasCapture());
926 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
927 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
929 // Set capture to widget1 and verify it gets it.
930 widget1.SetCapture(widget1.GetRootView());
931 EXPECT_TRUE(widget1.HasCapture());
932 EXPECT_FALSE(widget2.HasCapture());
933 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
934 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
936 // Release and verify no one has it.
937 widget1.ReleaseCapture();
938 EXPECT_FALSE(widget1.HasCapture());
939 EXPECT_FALSE(widget2.HasCapture());
940 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
941 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
944 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
945 Widget* widget) {
946 #if !defined(OS_CHROMEOS)
947 if (create_desktop_native_widget)
948 return new PlatformDesktopNativeWidget(widget);
949 #endif
950 return NULL;
953 private:
954 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
957 // See description in TestCapture().
958 TEST_F(WidgetCaptureTest, Capture) {
959 TestCapture(false);
962 #if !defined(OS_CHROMEOS)
963 // See description in TestCapture(). Creates DesktopNativeWidget.
964 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
965 TestCapture(true);
967 #endif
969 // Test that no state is set if capture fails.
970 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
971 Widget widget;
972 Widget::InitParams params =
973 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
974 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
975 params.bounds = gfx::Rect(400, 400);
976 widget.Init(params);
978 MouseView* mouse_view1 = new MouseView;
979 MouseView* mouse_view2 = new MouseView;
980 View* contents_view = new View;
981 contents_view->AddChildView(mouse_view1);
982 contents_view->AddChildView(mouse_view2);
983 widget.SetContentsView(contents_view);
985 mouse_view1->SetBounds(0, 0, 200, 400);
986 mouse_view2->SetBounds(200, 0, 200, 400);
988 // Setting capture should fail because |widget| is not visible.
989 widget.SetCapture(mouse_view1);
990 EXPECT_FALSE(widget.HasCapture());
992 widget.Show();
993 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
994 generator.set_current_location(gfx::Point(300, 10));
995 generator.PressLeftButton();
997 EXPECT_FALSE(mouse_view1->pressed());
998 EXPECT_TRUE(mouse_view2->pressed());
1001 #if !defined(OS_CHROMEOS) && !defined(OS_WIN)
1002 // Test that a synthetic mouse exit is sent to the widget which was handling
1003 // mouse events when a different widget grabs capture.
1004 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1005 TEST_F(WidgetCaptureTest, MouseExitOnCaptureGrab) {
1006 Widget widget1;
1007 Widget::InitParams params1 =
1008 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1009 params1.native_widget = CreateNativeWidget(true, &widget1);
1010 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1011 widget1.Init(params1);
1012 MouseView* mouse_view1 = new MouseView;
1013 widget1.SetContentsView(mouse_view1);
1014 widget1.Show();
1015 widget1.SetBounds(gfx::Rect(300, 300));
1017 Widget widget2;
1018 Widget::InitParams params2 =
1019 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1020 params2.native_widget = CreateNativeWidget(true, &widget2);
1021 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1022 widget2.Init(params2);
1023 widget2.Show();
1024 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1026 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1027 generator.set_current_location(gfx::Point(100, 100));
1028 generator.MoveMouseBy(0, 0);
1030 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1031 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1033 widget2.SetCapture(NULL);
1034 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1035 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1036 // in addition to the one generated by Chrome.
1037 EXPECT_LT(0, mouse_view1->ExitedCalls());
1039 #endif // !defined(OS_CHROMEOS)
1041 namespace {
1043 // Widget observer which grabs capture when the widget is activated.
1044 class CaptureOnActivationObserver : public WidgetObserver {
1045 public:
1046 CaptureOnActivationObserver() {
1048 ~CaptureOnActivationObserver() override {}
1050 // WidgetObserver:
1051 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1052 if (active)
1053 widget->SetCapture(NULL);
1056 private:
1057 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1060 } // namespace
1062 // Test that setting capture on widget activation of a non-toplevel widget
1063 // (e.g. a bubble on Linux) succeeds.
1064 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1065 Widget toplevel;
1066 Widget::InitParams toplevel_params =
1067 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1068 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel);
1069 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1070 toplevel.Init(toplevel_params);
1071 toplevel.Show();
1073 Widget* child = new Widget;
1074 Widget::InitParams child_params =
1075 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1076 child_params.parent = toplevel.GetNativeView();
1077 child_params.context = toplevel.GetNativeWindow();
1078 child->Init(child_params);
1080 CaptureOnActivationObserver observer;
1081 child->AddObserver(&observer);
1082 child->Show();
1084 EXPECT_TRUE(child->HasCapture());
1088 #if defined(OS_WIN)
1089 namespace {
1091 // Used to verify OnMouseEvent() has been invoked.
1092 class MouseEventTrackingWidget : public Widget {
1093 public:
1094 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1095 virtual ~MouseEventTrackingWidget() {}
1097 bool GetAndClearGotMouseEvent() {
1098 bool value = got_mouse_event_;
1099 got_mouse_event_ = false;
1100 return value;
1103 // Widget:
1104 virtual void OnMouseEvent(ui::MouseEvent* event) override {
1105 got_mouse_event_ = true;
1106 Widget::OnMouseEvent(event);
1109 private:
1110 bool got_mouse_event_;
1112 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1115 } // namespace
1117 // Verifies if a mouse event is received on a widget that doesn't have capture
1118 // on Windows that it is correctly processed by the widget that doesn't have
1119 // capture. This behavior is not desired on OSes other than Windows.
1120 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1121 MouseEventTrackingWidget widget1;
1122 Widget::InitParams params1 =
1123 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1124 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1125 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1126 widget1.Init(params1);
1127 widget1.Show();
1129 MouseEventTrackingWidget widget2;
1130 Widget::InitParams params2 =
1131 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1132 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1133 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1134 widget2.Init(params2);
1135 widget2.Show();
1137 // Set capture to widget2 and verity it gets it.
1138 widget2.SetCapture(widget2.GetRootView());
1139 EXPECT_FALSE(widget1.HasCapture());
1140 EXPECT_TRUE(widget2.HasCapture());
1142 widget1.GetAndClearGotMouseEvent();
1143 widget2.GetAndClearGotMouseEvent();
1144 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1145 // |widget2| has capture, |widget1| should still get the event.
1146 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1147 ui::EF_NONE, ui::EF_NONE);
1148 ui::EventDispatchDetails details = widget1.GetNativeWindow()->
1149 GetHost()->event_processor()->OnEventFromSource(&mouse_event);
1150 ASSERT_FALSE(details.dispatcher_destroyed);
1151 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1152 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1154 #endif // defined(OS_WIN)
1156 } // namespace test
1157 } // namespace views