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"
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/public/activation_client.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_tree_host.h"
32 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
33 #include "ui/views/win/hwnd_util.h"
41 // A View that closes the Widget and exits the current message-loop when it
42 // receives a mouse-release event.
43 class ExitLoopOnRelease : public View {
45 ExitLoopOnRelease() {}
46 ~ExitLoopOnRelease() override {}
49 // Overridden from View:
50 void OnMouseReleased(const ui::MouseEvent& event) override {
52 base::MessageLoop::current()->QuitNow();
55 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
58 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
59 class GestureCaptureView : public View {
61 GestureCaptureView() {}
62 ~GestureCaptureView() override {}
65 // Overridden from View:
66 void OnGestureEvent(ui::GestureEvent* event) override {
67 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
68 GetWidget()->SetCapture(this);
69 event->StopPropagation();
73 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
76 // A view that always processes all mouse events.
77 class MouseView : public View {
85 ~MouseView() override {}
87 bool OnMousePressed(const ui::MouseEvent& event) override {
92 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
94 void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
96 // Return the number of OnMouseEntered calls and reset the counter.
103 // Return the number of OnMouseExited calls and reset the counter.
110 int pressed() const { return pressed_; }
118 DISALLOW_COPY_AND_ASSIGN(MouseView);
121 // A View that shows a different widget, sets capture on that widget, and
122 // initiates a nested message-loop when it receives a mouse-press event.
123 class NestedLoopCaptureView : public View {
125 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
126 ~NestedLoopCaptureView() override {}
129 // Overridden from View:
130 bool OnMousePressed(const ui::MouseEvent& event) override {
131 // Start a nested loop.
133 widget_->SetCapture(widget_->GetContentsView());
134 EXPECT_TRUE(widget_->HasCapture());
136 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
137 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
139 base::RunLoop run_loop;
146 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
151 class WidgetTestInteractive : public WidgetTest {
153 WidgetTestInteractive() {}
154 ~WidgetTestInteractive() override {}
156 void SetUp() override {
157 gfx::GLSurface::InitializeOneOffForTests();
158 ui::RegisterPathProvider();
159 base::FilePath ui_test_pak_path;
160 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
161 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
166 static void ShowQuickMenuImmediately(
167 TouchSelectionControllerImpl* controller) {
169 if (controller->context_menu_timer_.IsRunning()) {
170 controller->context_menu_timer_.Stop();
171 // TODO(tapted): Enable this when porting ui/views/touchui to Mac.
172 #if !defined(OS_MACOSX)
173 controller->ContextMenuTimerFired();
178 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
180 return controller->context_menu_ && controller->context_menu_->visible();
185 // Tests whether activation and focus change works correctly in Windows.
186 // We test the following:-
187 // 1. If the active aura window is correctly set when a top level widget is
189 // 2. If the active aura window in widget 1 created above, is set to NULL when
190 // another top level widget is created and focused.
191 // 3. On focusing the native platform window for widget 1, the active aura
192 // window for widget 1 should be set and that for widget 2 should reset.
193 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
194 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
195 // Create widget 1 and expect the active window to be its window.
196 View* contents_view1 = new View;
197 contents_view1->SetFocusable(true);
199 Widget::InitParams init_params =
200 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
201 init_params.bounds = gfx::Rect(0, 0, 200, 200);
202 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
203 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
204 widget1.Init(init_params);
205 widget1.SetContentsView(contents_view1);
207 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
208 contents_view1->RequestFocus();
210 EXPECT_TRUE(root_window1 != NULL);
211 aura::client::ActivationClient* activation_client1 =
212 aura::client::GetActivationClient(root_window1);
213 EXPECT_TRUE(activation_client1 != NULL);
214 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
216 // Create widget 2 and expect the active window to be its window.
217 View* contents_view2 = new View;
219 Widget::InitParams init_params2 =
220 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
221 init_params2.bounds = gfx::Rect(0, 0, 200, 200);
222 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
223 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
224 widget2.Init(init_params2);
225 widget2.SetContentsView(contents_view2);
227 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
228 contents_view2->RequestFocus();
230 root_window2->GetHost()->GetAcceleratedWidget());
232 aura::client::ActivationClient* activation_client2 =
233 aura::client::GetActivationClient(root_window2);
234 EXPECT_TRUE(activation_client2 != NULL);
235 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
236 EXPECT_EQ(activation_client1->GetActiveWindow(),
237 reinterpret_cast<aura::Window*>(NULL));
239 // Now set focus back to widget 1 and expect the active window to be its
241 contents_view1->RequestFocus();
243 root_window1->GetHost()->GetAcceleratedWidget());
244 EXPECT_EQ(activation_client2->GetActiveWindow(),
245 reinterpret_cast<aura::Window*>(NULL));
246 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
248 #endif // defined(OS_WIN)
250 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
251 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
252 View* container = new View;
253 toplevel->SetContentsView(container);
255 EXPECT_FALSE(toplevel->HasCapture());
256 toplevel->SetCapture(NULL);
257 EXPECT_TRUE(toplevel->HasCapture());
259 // By default, mouse release removes capture.
260 gfx::Point click_location(45, 15);
261 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
262 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
263 toplevel->OnMouseEvent(&release);
264 EXPECT_FALSE(toplevel->HasCapture());
266 // Now a mouse release shouldn't remove capture.
267 toplevel->set_auto_release_capture(false);
268 toplevel->SetCapture(NULL);
269 EXPECT_TRUE(toplevel->HasCapture());
270 toplevel->OnMouseEvent(&release);
271 EXPECT_TRUE(toplevel->HasCapture());
272 toplevel->ReleaseCapture();
273 EXPECT_FALSE(toplevel->HasCapture());
276 RunPendingMessages();
279 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
280 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
281 View* container = new View;
282 toplevel->SetContentsView(container);
284 View* gesture = new GestureCaptureView;
285 gesture->SetBounds(0, 0, 30, 30);
286 container->AddChildView(gesture);
288 MouseView* mouse = new MouseView;
289 mouse->SetBounds(30, 0, 30, 30);
290 container->AddChildView(mouse);
292 toplevel->SetSize(gfx::Size(100, 100));
295 // Start a gesture on |gesture|.
296 ui::GestureEvent tap_down(15,
300 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
301 ui::GestureEvent end(15,
305 ui::GestureEventDetails(ui::ET_GESTURE_END));
306 toplevel->OnGestureEvent(&tap_down);
308 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
309 // will not receive the event.
310 gfx::Point click_location(45, 15);
312 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
313 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
314 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
315 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
317 EXPECT_TRUE(toplevel->HasCapture());
319 toplevel->OnMouseEvent(&press);
320 toplevel->OnMouseEvent(&release);
321 EXPECT_EQ(0, mouse->pressed());
323 EXPECT_FALSE(toplevel->HasCapture());
325 // The end of the gesture should release the capture, and pressing on |mouse|
326 // should now reach |mouse|.
327 toplevel->OnGestureEvent(&end);
328 toplevel->OnMouseEvent(&press);
329 toplevel->OnMouseEvent(&release);
330 EXPECT_EQ(1, mouse->pressed());
333 RunPendingMessages();
336 // Checks that if a mouse-press triggers a capture on a different widget (which
337 // consumes the mouse-release event), then the target of the press does not have
339 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
340 // The test creates two widgets: |first| and |second|.
341 // The View in |first| makes |second| visible, sets capture on it, and starts
342 // a nested loop (like a menu does). The View in |second| terminates the
343 // nested loop and closes the widget.
344 // The test sends a mouse-press event to |first|, and posts a task to send a
345 // release event to |second|, to make sure that the release event is
346 // dispatched after the nested loop starts.
348 Widget* first = CreateTopLevelFramelessPlatformWidget();
349 Widget* second = CreateTopLevelFramelessPlatformWidget();
351 View* container = new NestedLoopCaptureView(second);
352 first->SetContentsView(container);
354 second->SetContentsView(new ExitLoopOnRelease());
356 first->SetSize(gfx::Size(100, 100));
359 gfx::Point location(20, 20);
360 base::MessageLoop::current()->PostTask(FROM_HERE,
361 base::Bind(&Widget::OnMouseEvent,
362 base::Unretained(second),
363 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
366 ui::EF_LEFT_MOUSE_BUTTON,
367 ui::EF_LEFT_MOUSE_BUTTON))));
368 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
369 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
370 first->OnMouseEvent(&press);
371 EXPECT_FALSE(first->HasCapture());
373 RunPendingMessages();
376 // Tests some grab/ungrab events.
377 // TODO(estade): can this be enabled now that this is an interactive ui test?
378 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
379 Widget* toplevel = CreateTopLevelPlatformWidget();
380 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
381 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
383 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
385 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
386 View* view = new MouseView();
387 view->SetBounds(0, 0, 300, 300);
388 child1->GetRootView()->AddChildView(view);
390 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
391 view = new MouseView();
392 view->SetBounds(0, 0, 200, 200);
393 child2->GetRootView()->AddChildView(view);
396 RunPendingMessages();
399 gfx::Point p1(45, 45);
400 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
401 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
402 toplevel->OnMouseEvent(&pressed);
404 EXPECT_TRUE(toplevel->HasCapture());
405 EXPECT_TRUE(child1->HasCapture());
406 EXPECT_FALSE(child2->HasCapture());
408 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
409 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
410 toplevel->OnMouseEvent(&released);
412 EXPECT_FALSE(toplevel->HasCapture());
413 EXPECT_FALSE(child1->HasCapture());
414 EXPECT_FALSE(child2->HasCapture());
416 RunPendingMessages();
419 gfx::Point p2(315, 45);
420 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
421 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
422 toplevel->OnMouseEvent(&pressed2);
423 EXPECT_TRUE(pressed2.handled());
424 EXPECT_TRUE(toplevel->HasCapture());
425 EXPECT_TRUE(child2->HasCapture());
426 EXPECT_FALSE(child1->HasCapture());
428 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
429 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
430 toplevel->OnMouseEvent(&released2);
431 EXPECT_FALSE(toplevel->HasCapture());
432 EXPECT_FALSE(child1->HasCapture());
433 EXPECT_FALSE(child2->HasCapture());
435 toplevel->CloseNow();
438 // Tests mouse move outside of the window into the "resize controller" and back
439 // will still generate an OnMouseEntered and OnMouseExited event..
440 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
441 Widget* toplevel = CreateTopLevelPlatformWidget();
443 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
445 MouseView* view = new MouseView();
446 view->SetBounds(90, 90, 10, 10);
447 toplevel->GetRootView()->AddChildView(view);
450 RunPendingMessages();
452 // Move to an outside position.
453 gfx::Point p1(200, 200);
454 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE,
456 toplevel->OnMouseEvent(&moved_out);
457 EXPECT_EQ(0, view->EnteredCalls());
458 EXPECT_EQ(0, view->ExitedCalls());
460 // Move onto the active view.
461 gfx::Point p2(95, 95);
462 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE,
464 toplevel->OnMouseEvent(&moved_over);
465 EXPECT_EQ(1, view->EnteredCalls());
466 EXPECT_EQ(0, view->ExitedCalls());
468 // Move onto the outer resizing border.
469 gfx::Point p3(102, 95);
470 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE,
472 toplevel->OnMouseEvent(&moved_resizer);
473 EXPECT_EQ(0, view->EnteredCalls());
474 EXPECT_EQ(1, view->ExitedCalls());
476 // Move onto the view again.
477 toplevel->OnMouseEvent(&moved_over);
478 EXPECT_EQ(1, view->EnteredCalls());
479 EXPECT_EQ(0, view->ExitedCalls());
481 RunPendingMessages();
483 toplevel->CloseNow();
486 // Test view focus restoration when a widget is deactivated and re-activated.
487 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
488 Widget* widget1 = CreateTopLevelPlatformWidget();
489 View* view1 = new View;
490 view1->SetFocusable(true);
491 widget1->GetContentsView()->AddChildView(view1);
493 Widget* widget2 = CreateTopLevelPlatformWidget();
494 View* view2a = new View;
495 View* view2b = new View;
496 view2a->SetFocusable(true);
497 view2b->SetFocusable(true);
498 widget2->GetContentsView()->AddChildView(view2a);
499 widget2->GetContentsView()->AddChildView(view2b);
502 EXPECT_TRUE(widget1->IsActive());
503 view1->RequestFocus();
504 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
507 EXPECT_TRUE(widget2->IsActive());
508 EXPECT_FALSE(widget1->IsActive());
509 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
510 view2a->RequestFocus();
511 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
512 view2b->RequestFocus();
513 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
516 EXPECT_TRUE(widget1->IsActive());
517 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
518 EXPECT_FALSE(widget2->IsActive());
519 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
522 EXPECT_TRUE(widget2->IsActive());
523 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
524 EXPECT_FALSE(widget1->IsActive());
525 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
533 // Test view focus retention when a widget's HWND is disabled and re-enabled.
534 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
535 Widget* widget = CreateTopLevelFramelessPlatformWidget();
536 widget->SetContentsView(new View);
537 for (size_t i = 0; i < 2; ++i) {
538 widget->GetContentsView()->AddChildView(new View);
539 widget->GetContentsView()->child_at(i)->SetFocusable(true);
543 const HWND hwnd = HWNDForWidget(widget);
544 EXPECT_TRUE(::IsWindow(hwnd));
545 EXPECT_TRUE(::IsWindowEnabled(hwnd));
546 EXPECT_EQ(hwnd, ::GetActiveWindow());
548 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
549 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
550 View* view = widget->GetContentsView()->child_at(i);
552 view->RequestFocus();
553 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
554 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
555 EXPECT_FALSE(::IsWindowEnabled(hwnd));
557 // Oddly, disabling the HWND leaves it active with the focus unchanged.
558 EXPECT_EQ(hwnd, ::GetActiveWindow());
559 EXPECT_TRUE(widget->IsActive());
560 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
562 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
563 EXPECT_TRUE(::IsWindowEnabled(hwnd));
564 EXPECT_EQ(hwnd, ::GetActiveWindow());
565 EXPECT_TRUE(widget->IsActive());
566 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
572 // This class subclasses the Widget class to listen for activation change
573 // notifications and provides accessors to return information as to whether
574 // the widget is active. We need this to ensure that users of the widget
575 // class activate the widget only when the underlying window becomes really
576 // active. Previously we would activate the widget in the WM_NCACTIVATE
577 // message which is incorrect because APIs like FlashWindowEx flash the
578 // window caption by sending fake WM_NCACTIVATE messages.
579 class WidgetActivationTest : public Widget {
581 WidgetActivationTest()
584 virtual ~WidgetActivationTest() {}
586 virtual void OnNativeWidgetActivationChanged(bool active) override {
590 bool active() const { return active_; }
595 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
598 // Tests whether the widget only becomes active when the underlying window
600 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
601 WidgetActivationTest widget1;
602 Widget::InitParams init_params =
603 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
604 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
605 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
606 init_params.bounds = gfx::Rect(0, 0, 200, 200);
607 widget1.Init(init_params);
609 EXPECT_EQ(true, widget1.active());
611 WidgetActivationTest widget2;
612 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
613 widget2.Init(init_params);
615 EXPECT_EQ(true, widget2.active());
616 EXPECT_EQ(false, widget1.active());
618 HWND win32_native_window1 = HWNDForWidget(&widget1);
619 EXPECT_TRUE(::IsWindow(win32_native_window1));
621 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
622 EXPECT_EQ(false, widget1.active());
623 EXPECT_EQ(true, widget2.active());
625 ::SetActiveWindow(win32_native_window1);
626 EXPECT_EQ(true, widget1.active());
627 EXPECT_EQ(false, widget2.active());
629 #endif // defined(OS_WIN)
631 #if !defined(OS_CHROMEOS)
632 // Provides functionality to create a window modal dialog.
633 class ModalDialogDelegate : public DialogDelegateView {
635 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
636 ~ModalDialogDelegate() override {}
638 // WidgetDelegate overrides.
639 ui::ModalType GetModalType() const override { return type_; }
644 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
647 // Tests whether the focused window is set correctly when a modal window is
648 // created and destroyed. When it is destroyed it should focus the owner window.
649 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
650 TestWidgetFocusChangeListener focus_listener;
651 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
652 const std::vector<NativeViewPair>& focus_changes =
653 focus_listener.focus_changes();
655 // Create a top level widget.
656 Widget top_level_widget;
657 Widget::InitParams init_params =
658 CreateParams(Widget::InitParams::TYPE_WINDOW);
659 init_params.show_state = ui::SHOW_STATE_NORMAL;
660 gfx::Rect initial_bounds(0, 0, 500, 500);
661 init_params.bounds = initial_bounds;
662 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
663 init_params.native_widget =
664 new PlatformDesktopNativeWidget(&top_level_widget);
665 top_level_widget.Init(init_params);
666 top_level_widget.Show();
668 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
669 EXPECT_EQ(1u, focus_changes.size());
670 EXPECT_EQ(NativeViewPair(NULL, top_level_native_view), focus_changes[0]);
672 // Create a modal dialog.
673 // This instance will be destroyed when the dialog is destroyed.
674 ModalDialogDelegate* dialog_delegate =
675 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
677 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
678 dialog_delegate, NULL, top_level_widget.GetNativeView());
679 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
680 modal_dialog_widget->Show();
682 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
683 EXPECT_EQ(3u, focus_changes.size());
684 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
686 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
689 modal_dialog_widget->CloseNow();
691 EXPECT_EQ(5u, focus_changes.size());
692 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
694 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
697 top_level_widget.CloseNow();
698 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
701 // Test that when opening a system-modal window, capture is released.
702 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) {
703 TestWidgetFocusChangeListener focus_listener;
704 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
706 // Create a top level widget.
707 Widget top_level_widget;
708 Widget::InitParams init_params =
709 CreateParams(Widget::InitParams::TYPE_WINDOW);
710 init_params.show_state = ui::SHOW_STATE_NORMAL;
711 gfx::Rect initial_bounds(0, 0, 500, 500);
712 init_params.bounds = initial_bounds;
713 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
714 init_params.native_widget =
715 new PlatformDesktopNativeWidget(&top_level_widget);
716 top_level_widget.Init(init_params);
717 top_level_widget.Show();
719 EXPECT_EQ(top_level_widget.GetNativeView(),
720 focus_listener.focus_changes().back().second);;
722 EXPECT_FALSE(top_level_widget.HasCapture());
723 top_level_widget.SetCapture(NULL);
724 EXPECT_TRUE(top_level_widget.HasCapture());
726 // Create a modal dialog.
727 ModalDialogDelegate* dialog_delegate =
728 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
730 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
731 dialog_delegate, NULL, top_level_widget.GetNativeView());
732 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
733 modal_dialog_widget->Show();
735 EXPECT_FALSE(top_level_widget.HasCapture());
737 modal_dialog_widget->CloseNow();
738 top_level_widget.CloseNow();
739 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
742 #endif // !defined(OS_CHROMEOS)
744 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
746 Widget::InitParams init_params =
747 CreateParams(Widget::InitParams::TYPE_WINDOW);
748 init_params.bounds = gfx::Rect(0, 0, 200, 200);
749 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
750 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
751 #if !defined(OS_CHROMEOS)
752 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
753 #endif // !defined(OS_CHROMEOS)
754 widget.Init(init_params);
757 EXPECT_FALSE(widget.IsActive());
760 // Test that touch selection quick menu is not activated when opened.
761 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
762 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing);
764 views_delegate().set_use_desktop_native_widgets(true);
765 #endif // !defined(OS_WIN)
768 Widget::InitParams init_params =
769 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
770 init_params.bounds = gfx::Rect(0, 0, 200, 200);
771 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
772 widget.Init(init_params);
774 Textfield* textfield = new Textfield;
775 textfield->SetBounds(0, 0, 200, 20);
776 textfield->SetText(base::ASCIIToUTF16("some text"));
777 widget.GetRootView()->AddChildView(textfield);
780 textfield->RequestFocus();
781 textfield->SelectAll(true);
782 TextfieldTestApi textfield_test_api(textfield);
784 RunPendingMessages();
786 ui::test::EventGenerator generator(widget.GetNativeWindow());
787 generator.GestureTapAt(gfx::Point(10, 10));
788 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
789 textfield_test_api.touch_selection_controller()));
791 EXPECT_TRUE(textfield->HasFocus());
792 EXPECT_TRUE(widget.IsActive());
793 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>(
794 textfield_test_api.touch_selection_controller())));
797 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
799 views_delegate().set_use_desktop_native_widgets(true);
800 #endif // !defined(OS_WIN)
802 // Create first widget and view, activate the widget, and focus the view.
804 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
805 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
806 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
807 widget1.Init(params1);
809 View* view1 = new View();
810 view1->SetFocusable(true);
811 widget1.GetRootView()->AddChildView(view1);
814 EXPECT_TRUE(widget1.IsActive());
816 FocusManager* focus_manager1 = widget1.GetFocusManager();
817 ASSERT_TRUE(focus_manager1);
818 focus_manager1->SetFocusedView(view1);
819 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
821 // Create second widget and view, activate the widget, and focus the view.
823 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
824 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
825 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
826 widget2.Init(params2);
828 View* view2 = new View();
829 view2->SetFocusable(true);
830 widget2.GetRootView()->AddChildView(view2);
833 EXPECT_TRUE(widget2.IsActive());
834 EXPECT_FALSE(widget1.IsActive());
836 FocusManager* focus_manager2 = widget2.GetFocusManager();
837 ASSERT_TRUE(focus_manager2);
838 focus_manager2->SetFocusedView(view2);
839 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
841 // Disable the first view and make sure it loses focus, but its widget is not
843 view1->SetEnabled(false);
844 EXPECT_NE(view1, focus_manager1->GetFocusedView());
845 EXPECT_FALSE(widget1.IsActive());
846 EXPECT_TRUE(widget2.IsActive());
851 // Used to veirfy OnMouseCaptureLost() has been invoked.
852 class CaptureLostTrackingWidget : public Widget {
854 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
855 ~CaptureLostTrackingWidget() override {}
857 bool GetAndClearGotCaptureLost() {
858 bool value = got_capture_lost_;
859 got_capture_lost_ = false;
864 void OnMouseCaptureLost() override {
865 got_capture_lost_ = true;
866 Widget::OnMouseCaptureLost();
870 bool got_capture_lost_;
872 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
877 class WidgetCaptureTest : public ViewsTestBase {
879 WidgetCaptureTest() {
882 ~WidgetCaptureTest() override {}
884 void SetUp() override {
885 gfx::GLSurface::InitializeOneOffForTests();
886 ui::RegisterPathProvider();
887 base::FilePath ui_test_pak_path;
888 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
889 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
890 ViewsTestBase::SetUp();
893 // Verifies Widget::SetCapture() results in updating native capture along with
894 // invoking the right Widget function.
895 void TestCapture(bool use_desktop_native_widget) {
896 CaptureLostTrackingWidget widget1;
897 Widget::InitParams params1 =
898 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
899 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
901 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
902 widget1.Init(params1);
905 CaptureLostTrackingWidget widget2;
906 Widget::InitParams params2 =
907 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
908 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
909 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
911 widget2.Init(params2);
914 // Set capture to widget2 and verity it gets it.
915 widget2.SetCapture(widget2.GetRootView());
916 EXPECT_FALSE(widget1.HasCapture());
917 EXPECT_TRUE(widget2.HasCapture());
918 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
919 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
921 // Set capture to widget1 and verify it gets it.
922 widget1.SetCapture(widget1.GetRootView());
923 EXPECT_TRUE(widget1.HasCapture());
924 EXPECT_FALSE(widget2.HasCapture());
925 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
926 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
928 // Release and verify no one has it.
929 widget1.ReleaseCapture();
930 EXPECT_FALSE(widget1.HasCapture());
931 EXPECT_FALSE(widget2.HasCapture());
932 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
933 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
936 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
938 #if !defined(OS_CHROMEOS)
939 if (create_desktop_native_widget)
940 return new PlatformDesktopNativeWidget(widget);
946 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
949 // See description in TestCapture().
950 TEST_F(WidgetCaptureTest, Capture) {
954 #if !defined(OS_CHROMEOS)
955 // See description in TestCapture(). Creates DesktopNativeWidget.
956 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
961 // Test that no state is set if capture fails.
962 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
964 Widget::InitParams params =
965 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
966 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
967 params.bounds = gfx::Rect(400, 400);
970 MouseView* mouse_view1 = new MouseView;
971 MouseView* mouse_view2 = new MouseView;
972 View* contents_view = new View;
973 contents_view->AddChildView(mouse_view1);
974 contents_view->AddChildView(mouse_view2);
975 widget.SetContentsView(contents_view);
977 mouse_view1->SetBounds(0, 0, 200, 400);
978 mouse_view2->SetBounds(200, 0, 200, 400);
980 // Setting capture should fail because |widget| is not visible.
981 widget.SetCapture(mouse_view1);
982 EXPECT_FALSE(widget.HasCapture());
985 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
986 generator.set_current_location(gfx::Point(300, 10));
987 generator.PressLeftButton();
989 EXPECT_FALSE(mouse_view1->pressed());
990 EXPECT_TRUE(mouse_view2->pressed());
993 #if !defined(OS_CHROMEOS) && !defined(OS_WIN)
994 // Test that a synthetic mouse exit is sent to the widget which was handling
995 // mouse events when a different widget grabs capture.
996 // TODO(pkotwicz): Make test pass on CrOS and Windows.
997 TEST_F(WidgetCaptureTest, MouseExitOnCaptureGrab) {
999 Widget::InitParams params1 =
1000 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1001 params1.native_widget = CreateNativeWidget(true, &widget1);
1002 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1003 widget1.Init(params1);
1004 MouseView* mouse_view1 = new MouseView;
1005 widget1.SetContentsView(mouse_view1);
1007 widget1.SetBounds(gfx::Rect(300, 300));
1010 Widget::InitParams params2 =
1011 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1012 params2.native_widget = CreateNativeWidget(true, &widget2);
1013 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1014 widget2.Init(params2);
1016 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1018 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1019 generator.set_current_location(gfx::Point(100, 100));
1020 generator.MoveMouseBy(0, 0);
1022 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1023 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1025 widget2.SetCapture(NULL);
1026 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1027 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1028 // in addition to the one generated by Chrome.
1029 EXPECT_LT(0, mouse_view1->ExitedCalls());
1031 #endif // !defined(OS_CHROMEOS)
1035 // Widget observer which grabs capture when the widget is activated.
1036 class CaptureOnActivationObserver : public WidgetObserver {
1038 CaptureOnActivationObserver() {
1040 ~CaptureOnActivationObserver() override {}
1043 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1045 widget->SetCapture(NULL);
1049 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1054 // Test that setting capture on widget activation of a non-toplevel widget
1055 // (e.g. a bubble on Linux) succeeds.
1056 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1058 Widget::InitParams toplevel_params =
1059 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1060 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel);
1061 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1062 toplevel.Init(toplevel_params);
1065 Widget* child = new Widget;
1066 Widget::InitParams child_params =
1067 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1068 child_params.parent = toplevel.GetNativeView();
1069 child_params.context = toplevel.GetNativeWindow();
1070 child->Init(child_params);
1072 CaptureOnActivationObserver observer;
1073 child->AddObserver(&observer);
1076 EXPECT_TRUE(child->HasCapture());
1083 // Used to verify OnMouseEvent() has been invoked.
1084 class MouseEventTrackingWidget : public Widget {
1086 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1087 virtual ~MouseEventTrackingWidget() {}
1089 bool GetAndClearGotMouseEvent() {
1090 bool value = got_mouse_event_;
1091 got_mouse_event_ = false;
1096 virtual void OnMouseEvent(ui::MouseEvent* event) override {
1097 got_mouse_event_ = true;
1098 Widget::OnMouseEvent(event);
1102 bool got_mouse_event_;
1104 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1109 // Verifies if a mouse event is received on a widget that doesn't have capture
1110 // on Windows that it is correctly processed by the widget that doesn't have
1111 // capture. This behavior is not desired on OSes other than Windows.
1112 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1113 MouseEventTrackingWidget widget1;
1114 Widget::InitParams params1 =
1115 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1116 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1117 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1118 widget1.Init(params1);
1121 MouseEventTrackingWidget widget2;
1122 Widget::InitParams params2 =
1123 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1124 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1125 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1126 widget2.Init(params2);
1129 // Set capture to widget2 and verity it gets it.
1130 widget2.SetCapture(widget2.GetRootView());
1131 EXPECT_FALSE(widget1.HasCapture());
1132 EXPECT_TRUE(widget2.HasCapture());
1134 widget1.GetAndClearGotMouseEvent();
1135 widget2.GetAndClearGotMouseEvent();
1136 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1137 // |widget2| has capture, |widget1| should still get the event.
1138 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1139 ui::EF_NONE, ui::EF_NONE);
1140 ui::EventDispatchDetails details = widget1.GetNativeWindow()->
1141 GetHost()->event_processor()->OnEventFromSource(&mouse_event);
1142 ASSERT_FALSE(details.dispatcher_destroyed);
1143 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1144 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1146 #endif // defined(OS_WIN)
1149 } // namespace views