MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / ui / views / widget / widget_interactive_uitest.cc
blob681f1d1893f331819c6e26b750efcd0bfd99c426
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/public/activation_client.h"
29 #if defined(OS_WIN)
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"
34 #endif
36 namespace views {
37 namespace test {
39 namespace {
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 {
44 public:
45 ExitLoopOnRelease() {}
46 virtual ~ExitLoopOnRelease() {}
48 private:
49 // Overridden from View:
50 virtual void OnMouseReleased(const ui::MouseEvent& event) override {
51 GetWidget()->Close();
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 {
60 public:
61 GestureCaptureView() {}
62 virtual ~GestureCaptureView() {}
64 private:
65 // Overridden from View:
66 virtual 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 {
78 public:
79 MouseView()
80 : View(),
81 entered_(0),
82 exited_(0),
83 pressed_(0) {
85 virtual ~MouseView() {}
87 virtual bool OnMousePressed(const ui::MouseEvent& event) override {
88 pressed_++;
89 return true;
92 virtual void OnMouseEntered(const ui::MouseEvent& event) override {
93 entered_++;
96 virtual void OnMouseExited(const ui::MouseEvent& event) override {
97 exited_++;
100 // Return the number of OnMouseEntered calls and reset the counter.
101 int EnteredCalls() {
102 int i = entered_;
103 entered_ = 0;
104 return i;
107 // Return the number of OnMouseExited calls and reset the counter.
108 int ExitedCalls() {
109 int i = exited_;
110 exited_ = 0;
111 return i;
114 int pressed() const { return pressed_; }
116 private:
117 int entered_;
118 int exited_;
120 int pressed_;
122 DISALLOW_COPY_AND_ASSIGN(MouseView);
125 // A View that shows a different widget, sets capture on that widget, and
126 // initiates a nested message-loop when it receives a mouse-press event.
127 class NestedLoopCaptureView : public View {
128 public:
129 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
130 virtual ~NestedLoopCaptureView() {}
132 private:
133 // Overridden from View:
134 virtual bool OnMousePressed(const ui::MouseEvent& event) override {
135 // Start a nested loop.
136 widget_->Show();
137 widget_->SetCapture(widget_->GetContentsView());
138 EXPECT_TRUE(widget_->HasCapture());
140 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
141 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
143 base::RunLoop run_loop;
144 run_loop.Run();
145 return true;
148 Widget* widget_;
150 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
153 } // namespace
155 class WidgetTestInteractive : public WidgetTest {
156 public:
157 WidgetTestInteractive() {}
158 virtual ~WidgetTestInteractive() {}
160 virtual void SetUp() override {
161 gfx::GLSurface::InitializeOneOffForTests();
162 ui::RegisterPathProvider();
163 base::FilePath ui_test_pak_path;
164 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
165 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
166 WidgetTest::SetUp();
169 protected:
170 static void ShowQuickMenuImmediately(
171 TouchSelectionControllerImpl* controller) {
172 DCHECK(controller);
173 if (controller->context_menu_timer_.IsRunning()) {
174 controller->context_menu_timer_.Stop();
175 // TODO(tapted): Enable this when porting ui/views/touchui to Mac.
176 #if !defined(OS_MACOSX)
177 controller->ContextMenuTimerFired();
178 #endif
182 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
183 DCHECK(controller);
184 return controller->context_menu_ && controller->context_menu_->visible();
188 #if defined(OS_WIN)
189 // Tests whether activation and focus change works correctly in Windows.
190 // We test the following:-
191 // 1. If the active aura window is correctly set when a top level widget is
192 // created.
193 // 2. If the active aura window in widget 1 created above, is set to NULL when
194 // another top level widget is created and focused.
195 // 3. On focusing the native platform window for widget 1, the active aura
196 // window for widget 1 should be set and that for widget 2 should reset.
197 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
198 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
199 // Create widget 1 and expect the active window to be its window.
200 View* contents_view1 = new View;
201 contents_view1->SetFocusable(true);
202 Widget widget1;
203 Widget::InitParams init_params =
204 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
205 init_params.bounds = gfx::Rect(0, 0, 200, 200);
206 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
207 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
208 widget1.Init(init_params);
209 widget1.SetContentsView(contents_view1);
210 widget1.Show();
211 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
212 contents_view1->RequestFocus();
214 EXPECT_TRUE(root_window1 != NULL);
215 aura::client::ActivationClient* activation_client1 =
216 aura::client::GetActivationClient(root_window1);
217 EXPECT_TRUE(activation_client1 != NULL);
218 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
220 // Create widget 2 and expect the active window to be its window.
221 View* contents_view2 = new View;
222 Widget widget2;
223 Widget::InitParams init_params2 =
224 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
225 init_params2.bounds = gfx::Rect(0, 0, 200, 200);
226 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
227 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
228 widget2.Init(init_params2);
229 widget2.SetContentsView(contents_view2);
230 widget2.Show();
231 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
232 contents_view2->RequestFocus();
233 ::SetActiveWindow(
234 root_window2->GetHost()->GetAcceleratedWidget());
236 aura::client::ActivationClient* activation_client2 =
237 aura::client::GetActivationClient(root_window2);
238 EXPECT_TRUE(activation_client2 != NULL);
239 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
240 EXPECT_EQ(activation_client1->GetActiveWindow(),
241 reinterpret_cast<aura::Window*>(NULL));
243 // Now set focus back to widget 1 and expect the active window to be its
244 // window.
245 contents_view1->RequestFocus();
246 ::SetActiveWindow(
247 root_window1->GetHost()->GetAcceleratedWidget());
248 EXPECT_EQ(activation_client2->GetActiveWindow(),
249 reinterpret_cast<aura::Window*>(NULL));
250 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
252 #endif // defined(OS_WIN)
254 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
255 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
256 View* container = new View;
257 toplevel->SetContentsView(container);
259 EXPECT_FALSE(toplevel->HasCapture());
260 toplevel->SetCapture(NULL);
261 EXPECT_TRUE(toplevel->HasCapture());
263 // By default, mouse release removes capture.
264 gfx::Point click_location(45, 15);
265 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
266 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
267 toplevel->OnMouseEvent(&release);
268 EXPECT_FALSE(toplevel->HasCapture());
270 // Now a mouse release shouldn't remove capture.
271 toplevel->set_auto_release_capture(false);
272 toplevel->SetCapture(NULL);
273 EXPECT_TRUE(toplevel->HasCapture());
274 toplevel->OnMouseEvent(&release);
275 EXPECT_TRUE(toplevel->HasCapture());
276 toplevel->ReleaseCapture();
277 EXPECT_FALSE(toplevel->HasCapture());
279 toplevel->Close();
280 RunPendingMessages();
283 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
284 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
285 View* container = new View;
286 toplevel->SetContentsView(container);
288 View* gesture = new GestureCaptureView;
289 gesture->SetBounds(0, 0, 30, 30);
290 container->AddChildView(gesture);
292 MouseView* mouse = new MouseView;
293 mouse->SetBounds(30, 0, 30, 30);
294 container->AddChildView(mouse);
296 toplevel->SetSize(gfx::Size(100, 100));
297 toplevel->Show();
299 // Start a gesture on |gesture|.
300 ui::GestureEvent tap_down(15,
303 base::TimeDelta(),
304 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
305 ui::GestureEvent end(15,
308 base::TimeDelta(),
309 ui::GestureEventDetails(ui::ET_GESTURE_END));
310 toplevel->OnGestureEvent(&tap_down);
312 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
313 // will not receive the event.
314 gfx::Point click_location(45, 15);
316 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
317 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
318 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
319 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
321 EXPECT_TRUE(toplevel->HasCapture());
323 toplevel->OnMouseEvent(&press);
324 toplevel->OnMouseEvent(&release);
325 EXPECT_EQ(0, mouse->pressed());
327 EXPECT_FALSE(toplevel->HasCapture());
329 // The end of the gesture should release the capture, and pressing on |mouse|
330 // should now reach |mouse|.
331 toplevel->OnGestureEvent(&end);
332 toplevel->OnMouseEvent(&press);
333 toplevel->OnMouseEvent(&release);
334 EXPECT_EQ(1, mouse->pressed());
336 toplevel->Close();
337 RunPendingMessages();
340 // Checks that if a mouse-press triggers a capture on a different widget (which
341 // consumes the mouse-release event), then the target of the press does not have
342 // capture.
343 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
344 // The test creates two widgets: |first| and |second|.
345 // The View in |first| makes |second| visible, sets capture on it, and starts
346 // a nested loop (like a menu does). The View in |second| terminates the
347 // nested loop and closes the widget.
348 // The test sends a mouse-press event to |first|, and posts a task to send a
349 // release event to |second|, to make sure that the release event is
350 // dispatched after the nested loop starts.
352 Widget* first = CreateTopLevelFramelessPlatformWidget();
353 Widget* second = CreateTopLevelFramelessPlatformWidget();
355 View* container = new NestedLoopCaptureView(second);
356 first->SetContentsView(container);
358 second->SetContentsView(new ExitLoopOnRelease());
360 first->SetSize(gfx::Size(100, 100));
361 first->Show();
363 gfx::Point location(20, 20);
364 base::MessageLoop::current()->PostTask(FROM_HERE,
365 base::Bind(&Widget::OnMouseEvent,
366 base::Unretained(second),
367 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
368 location,
369 location,
370 ui::EF_LEFT_MOUSE_BUTTON,
371 ui::EF_LEFT_MOUSE_BUTTON))));
372 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
373 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
374 first->OnMouseEvent(&press);
375 EXPECT_FALSE(first->HasCapture());
376 first->Close();
377 RunPendingMessages();
380 // Tests some grab/ungrab events.
381 // TODO(estade): can this be enabled now that this is an interactive ui test?
382 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
383 Widget* toplevel = CreateTopLevelPlatformWidget();
384 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
385 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
387 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
389 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
390 View* view = new MouseView();
391 view->SetBounds(0, 0, 300, 300);
392 child1->GetRootView()->AddChildView(view);
394 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
395 view = new MouseView();
396 view->SetBounds(0, 0, 200, 200);
397 child2->GetRootView()->AddChildView(view);
399 toplevel->Show();
400 RunPendingMessages();
402 // Click on child1
403 gfx::Point p1(45, 45);
404 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
405 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
406 toplevel->OnMouseEvent(&pressed);
408 EXPECT_TRUE(toplevel->HasCapture());
409 EXPECT_TRUE(child1->HasCapture());
410 EXPECT_FALSE(child2->HasCapture());
412 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
413 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
414 toplevel->OnMouseEvent(&released);
416 EXPECT_FALSE(toplevel->HasCapture());
417 EXPECT_FALSE(child1->HasCapture());
418 EXPECT_FALSE(child2->HasCapture());
420 RunPendingMessages();
422 // Click on child2
423 gfx::Point p2(315, 45);
424 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
425 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
426 toplevel->OnMouseEvent(&pressed2);
427 EXPECT_TRUE(pressed2.handled());
428 EXPECT_TRUE(toplevel->HasCapture());
429 EXPECT_TRUE(child2->HasCapture());
430 EXPECT_FALSE(child1->HasCapture());
432 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
433 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
434 toplevel->OnMouseEvent(&released2);
435 EXPECT_FALSE(toplevel->HasCapture());
436 EXPECT_FALSE(child1->HasCapture());
437 EXPECT_FALSE(child2->HasCapture());
439 toplevel->CloseNow();
442 // Tests mouse move outside of the window into the "resize controller" and back
443 // will still generate an OnMouseEntered and OnMouseExited event..
444 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
445 Widget* toplevel = CreateTopLevelPlatformWidget();
447 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
449 MouseView* view = new MouseView();
450 view->SetBounds(90, 90, 10, 10);
451 toplevel->GetRootView()->AddChildView(view);
453 toplevel->Show();
454 RunPendingMessages();
456 // Move to an outside position.
457 gfx::Point p1(200, 200);
458 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE,
459 ui::EF_NONE);
460 toplevel->OnMouseEvent(&moved_out);
461 EXPECT_EQ(0, view->EnteredCalls());
462 EXPECT_EQ(0, view->ExitedCalls());
464 // Move onto the active view.
465 gfx::Point p2(95, 95);
466 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE,
467 ui::EF_NONE);
468 toplevel->OnMouseEvent(&moved_over);
469 EXPECT_EQ(1, view->EnteredCalls());
470 EXPECT_EQ(0, view->ExitedCalls());
472 // Move onto the outer resizing border.
473 gfx::Point p3(102, 95);
474 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE,
475 ui::EF_NONE);
476 toplevel->OnMouseEvent(&moved_resizer);
477 EXPECT_EQ(0, view->EnteredCalls());
478 EXPECT_EQ(1, view->ExitedCalls());
480 // Move onto the view again.
481 toplevel->OnMouseEvent(&moved_over);
482 EXPECT_EQ(1, view->EnteredCalls());
483 EXPECT_EQ(0, view->ExitedCalls());
485 RunPendingMessages();
487 toplevel->CloseNow();
490 // Test view focus restoration when a widget is deactivated and re-activated.
491 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
492 Widget* widget1 = CreateTopLevelPlatformWidget();
493 View* view1 = new View;
494 view1->SetFocusable(true);
495 widget1->GetContentsView()->AddChildView(view1);
497 Widget* widget2 = CreateTopLevelPlatformWidget();
498 View* view2a = new View;
499 View* view2b = new View;
500 view2a->SetFocusable(true);
501 view2b->SetFocusable(true);
502 widget2->GetContentsView()->AddChildView(view2a);
503 widget2->GetContentsView()->AddChildView(view2b);
505 widget1->Show();
506 EXPECT_TRUE(widget1->IsActive());
507 view1->RequestFocus();
508 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
510 widget2->Show();
511 EXPECT_TRUE(widget2->IsActive());
512 EXPECT_FALSE(widget1->IsActive());
513 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
514 view2a->RequestFocus();
515 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
516 view2b->RequestFocus();
517 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
519 widget1->Activate();
520 EXPECT_TRUE(widget1->IsActive());
521 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
522 EXPECT_FALSE(widget2->IsActive());
523 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
525 widget2->Activate();
526 EXPECT_TRUE(widget2->IsActive());
527 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
528 EXPECT_FALSE(widget1->IsActive());
529 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
531 widget1->CloseNow();
532 widget2->CloseNow();
535 #if defined(OS_WIN)
537 // Test view focus retention when a widget's HWND is disabled and re-enabled.
538 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
539 Widget* widget = CreateTopLevelFramelessPlatformWidget();
540 widget->SetContentsView(new View);
541 for (size_t i = 0; i < 2; ++i) {
542 widget->GetContentsView()->AddChildView(new View);
543 widget->GetContentsView()->child_at(i)->SetFocusable(true);
546 widget->Show();
547 const HWND hwnd = HWNDForWidget(widget);
548 EXPECT_TRUE(::IsWindow(hwnd));
549 EXPECT_TRUE(::IsWindowEnabled(hwnd));
550 EXPECT_EQ(hwnd, ::GetActiveWindow());
552 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
553 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
554 View* view = widget->GetContentsView()->child_at(i);
556 view->RequestFocus();
557 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
558 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
559 EXPECT_FALSE(::IsWindowEnabled(hwnd));
561 // Oddly, disabling the HWND leaves it active with the focus unchanged.
562 EXPECT_EQ(hwnd, ::GetActiveWindow());
563 EXPECT_TRUE(widget->IsActive());
564 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
566 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
567 EXPECT_TRUE(::IsWindowEnabled(hwnd));
568 EXPECT_EQ(hwnd, ::GetActiveWindow());
569 EXPECT_TRUE(widget->IsActive());
570 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
573 widget->CloseNow();
576 // This class subclasses the Widget class to listen for activation change
577 // notifications and provides accessors to return information as to whether
578 // the widget is active. We need this to ensure that users of the widget
579 // class activate the widget only when the underlying window becomes really
580 // active. Previously we would activate the widget in the WM_NCACTIVATE
581 // message which is incorrect because APIs like FlashWindowEx flash the
582 // window caption by sending fake WM_NCACTIVATE messages.
583 class WidgetActivationTest : public Widget {
584 public:
585 WidgetActivationTest()
586 : active_(false) {}
588 virtual ~WidgetActivationTest() {}
590 virtual void OnNativeWidgetActivationChanged(bool active) override {
591 active_ = active;
594 bool active() const { return active_; }
596 private:
597 bool active_;
599 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
602 // Tests whether the widget only becomes active when the underlying window
603 // is really active.
604 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
605 WidgetActivationTest widget1;
606 Widget::InitParams init_params =
607 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
608 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
609 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
610 init_params.bounds = gfx::Rect(0, 0, 200, 200);
611 widget1.Init(init_params);
612 widget1.Show();
613 EXPECT_EQ(true, widget1.active());
615 WidgetActivationTest widget2;
616 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
617 widget2.Init(init_params);
618 widget2.Show();
619 EXPECT_EQ(true, widget2.active());
620 EXPECT_EQ(false, widget1.active());
622 HWND win32_native_window1 = HWNDForWidget(&widget1);
623 EXPECT_TRUE(::IsWindow(win32_native_window1));
625 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
626 EXPECT_EQ(false, widget1.active());
627 EXPECT_EQ(true, widget2.active());
629 ::SetActiveWindow(win32_native_window1);
630 EXPECT_EQ(true, widget1.active());
631 EXPECT_EQ(false, widget2.active());
633 #endif // defined(OS_WIN)
635 #if !defined(OS_CHROMEOS)
636 // Provides functionality to create a window modal dialog.
637 class ModalDialogDelegate : public DialogDelegateView {
638 public:
639 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
640 virtual ~ModalDialogDelegate() {}
642 // WidgetDelegate overrides.
643 virtual ui::ModalType GetModalType() const override {
644 return type_;
647 private:
648 ui::ModalType type_;
650 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
653 // Tests whether the focused window is set correctly when a modal window is
654 // created and destroyed. When it is destroyed it should focus the owner window.
655 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
656 TestWidgetFocusChangeListener focus_listener;
657 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
658 const std::vector<NativeViewPair>& focus_changes =
659 focus_listener.focus_changes();
661 // Create a top level widget.
662 Widget top_level_widget;
663 Widget::InitParams init_params =
664 CreateParams(Widget::InitParams::TYPE_WINDOW);
665 init_params.show_state = ui::SHOW_STATE_NORMAL;
666 gfx::Rect initial_bounds(0, 0, 500, 500);
667 init_params.bounds = initial_bounds;
668 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
669 init_params.native_widget =
670 new PlatformDesktopNativeWidget(&top_level_widget);
671 top_level_widget.Init(init_params);
672 top_level_widget.Show();
674 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
675 EXPECT_EQ(1u, focus_changes.size());
676 EXPECT_EQ(NativeViewPair(NULL, top_level_native_view), focus_changes[0]);
678 // Create a modal dialog.
679 // This instance will be destroyed when the dialog is destroyed.
680 ModalDialogDelegate* dialog_delegate =
681 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
683 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
684 dialog_delegate, NULL, top_level_widget.GetNativeView());
685 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
686 modal_dialog_widget->Show();
688 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
689 EXPECT_EQ(3u, focus_changes.size());
690 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
691 focus_changes[1]);
692 EXPECT_EQ(NativeViewPair(top_level_native_view, modal_native_view),
693 focus_changes[2]);
695 modal_dialog_widget->CloseNow();
697 EXPECT_EQ(5u, focus_changes.size());
698 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
699 focus_changes[3]);
700 EXPECT_EQ(NativeViewPair(modal_native_view, top_level_native_view),
701 focus_changes[4]);
703 top_level_widget.CloseNow();
704 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
707 // Test that when opening a system-modal window, capture is released.
708 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) {
709 TestWidgetFocusChangeListener focus_listener;
710 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
712 // Create a top level widget.
713 Widget top_level_widget;
714 Widget::InitParams init_params =
715 CreateParams(Widget::InitParams::TYPE_WINDOW);
716 init_params.show_state = ui::SHOW_STATE_NORMAL;
717 gfx::Rect initial_bounds(0, 0, 500, 500);
718 init_params.bounds = initial_bounds;
719 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
720 init_params.native_widget =
721 new PlatformDesktopNativeWidget(&top_level_widget);
722 top_level_widget.Init(init_params);
723 top_level_widget.Show();
725 EXPECT_EQ(top_level_widget.GetNativeView(),
726 focus_listener.focus_changes().back().second);;
728 EXPECT_FALSE(top_level_widget.HasCapture());
729 top_level_widget.SetCapture(NULL);
730 EXPECT_TRUE(top_level_widget.HasCapture());
732 // Create a modal dialog.
733 ModalDialogDelegate* dialog_delegate =
734 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
736 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
737 dialog_delegate, NULL, top_level_widget.GetNativeView());
738 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
739 modal_dialog_widget->Show();
741 EXPECT_FALSE(top_level_widget.HasCapture());
743 modal_dialog_widget->CloseNow();
744 top_level_widget.CloseNow();
745 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
748 #endif // !defined(OS_CHROMEOS)
750 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
751 Widget widget;
752 Widget::InitParams init_params =
753 CreateParams(Widget::InitParams::TYPE_WINDOW);
754 init_params.bounds = gfx::Rect(0, 0, 200, 200);
755 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
756 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
757 #if !defined(OS_CHROMEOS)
758 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
759 #endif // !defined(OS_CHROMEOS)
760 widget.Init(init_params);
762 widget.Show();
763 EXPECT_FALSE(widget.IsActive());
766 // Test that touch selection quick menu is not activated when opened.
767 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
768 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableTouchEditing);
769 #if defined(OS_WIN)
770 views_delegate().set_use_desktop_native_widgets(true);
771 #endif // !defined(OS_WIN)
773 Widget widget;
774 Widget::InitParams init_params =
775 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
776 init_params.bounds = gfx::Rect(0, 0, 200, 200);
777 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
778 widget.Init(init_params);
780 Textfield* textfield = new Textfield;
781 textfield->SetBounds(0, 0, 200, 20);
782 textfield->SetText(base::ASCIIToUTF16("some text"));
783 widget.GetRootView()->AddChildView(textfield);
785 widget.Show();
786 textfield->RequestFocus();
787 textfield->SelectAll(true);
788 TextfieldTestApi textfield_test_api(textfield);
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())));
803 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
804 #if defined(OS_WIN)
805 views_delegate().set_use_desktop_native_widgets(true);
806 #endif // !defined(OS_WIN)
808 // Create first widget and view, activate the widget, and focus the view.
809 Widget widget1;
810 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
811 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
812 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
813 widget1.Init(params1);
815 View* view1 = new View();
816 view1->SetFocusable(true);
817 widget1.GetRootView()->AddChildView(view1);
819 widget1.Activate();
820 EXPECT_TRUE(widget1.IsActive());
822 FocusManager* focus_manager1 = widget1.GetFocusManager();
823 ASSERT_TRUE(focus_manager1);
824 focus_manager1->SetFocusedView(view1);
825 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
827 // Create second widget and view, activate the widget, and focus the view.
828 Widget widget2;
829 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
830 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
831 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
832 widget2.Init(params2);
834 View* view2 = new View();
835 view2->SetFocusable(true);
836 widget2.GetRootView()->AddChildView(view2);
838 widget2.Activate();
839 EXPECT_TRUE(widget2.IsActive());
840 EXPECT_FALSE(widget1.IsActive());
842 FocusManager* focus_manager2 = widget2.GetFocusManager();
843 ASSERT_TRUE(focus_manager2);
844 focus_manager2->SetFocusedView(view2);
845 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
847 // Disable the first view and make sure it loses focus, but its widget is not
848 // activated.
849 view1->SetEnabled(false);
850 EXPECT_NE(view1, focus_manager1->GetFocusedView());
851 EXPECT_FALSE(widget1.IsActive());
852 EXPECT_TRUE(widget2.IsActive());
855 namespace {
857 // Used to veirfy OnMouseCaptureLost() has been invoked.
858 class CaptureLostTrackingWidget : public Widget {
859 public:
860 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
861 virtual ~CaptureLostTrackingWidget() {}
863 bool GetAndClearGotCaptureLost() {
864 bool value = got_capture_lost_;
865 got_capture_lost_ = false;
866 return value;
869 // Widget:
870 virtual void OnMouseCaptureLost() override {
871 got_capture_lost_ = true;
872 Widget::OnMouseCaptureLost();
875 private:
876 bool got_capture_lost_;
878 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
881 } // namespace
883 class WidgetCaptureTest : public ViewsTestBase {
884 public:
885 WidgetCaptureTest() {
888 virtual ~WidgetCaptureTest() {
891 virtual void SetUp() override {
892 gfx::GLSurface::InitializeOneOffForTests();
893 ui::RegisterPathProvider();
894 base::FilePath ui_test_pak_path;
895 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
896 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
897 ViewsTestBase::SetUp();
900 // Verifies Widget::SetCapture() results in updating native capture along with
901 // invoking the right Widget function.
902 void TestCapture(bool use_desktop_native_widget) {
903 CaptureLostTrackingWidget widget1;
904 Widget::InitParams params1 =
905 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
906 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
907 &widget1);
908 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
909 widget1.Init(params1);
910 widget1.Show();
912 CaptureLostTrackingWidget widget2;
913 Widget::InitParams params2 =
914 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
915 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
916 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
917 &widget2);
918 widget2.Init(params2);
919 widget2.Show();
921 // Set capture to widget2 and verity it gets it.
922 widget2.SetCapture(widget2.GetRootView());
923 EXPECT_FALSE(widget1.HasCapture());
924 EXPECT_TRUE(widget2.HasCapture());
925 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
926 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
928 // Set capture to widget1 and verify it gets it.
929 widget1.SetCapture(widget1.GetRootView());
930 EXPECT_TRUE(widget1.HasCapture());
931 EXPECT_FALSE(widget2.HasCapture());
932 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
933 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
935 // Release and verify no one has it.
936 widget1.ReleaseCapture();
937 EXPECT_FALSE(widget1.HasCapture());
938 EXPECT_FALSE(widget2.HasCapture());
939 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
940 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
943 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
944 Widget* widget) {
945 #if !defined(OS_CHROMEOS)
946 if (create_desktop_native_widget)
947 return new PlatformDesktopNativeWidget(widget);
948 #endif
949 return NULL;
952 private:
953 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
956 // See description in TestCapture().
957 TEST_F(WidgetCaptureTest, Capture) {
958 TestCapture(false);
961 #if !defined(OS_CHROMEOS)
962 // See description in TestCapture(). Creates DesktopNativeWidget.
963 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
964 TestCapture(true);
966 #endif
968 // Test that no state is set if capture fails.
969 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
970 Widget widget;
971 Widget::InitParams params =
972 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
973 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
974 params.bounds = gfx::Rect(400, 400);
975 widget.Init(params);
977 MouseView* mouse_view1 = new MouseView;
978 MouseView* mouse_view2 = new MouseView;
979 View* contents_view = new View;
980 contents_view->AddChildView(mouse_view1);
981 contents_view->AddChildView(mouse_view2);
982 widget.SetContentsView(contents_view);
984 mouse_view1->SetBounds(0, 0, 200, 400);
985 mouse_view2->SetBounds(200, 0, 200, 400);
987 // Setting capture should fail because |widget| is not visible.
988 widget.SetCapture(mouse_view1);
989 EXPECT_FALSE(widget.HasCapture());
991 widget.Show();
992 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
993 generator.set_current_location(gfx::Point(300, 10));
994 generator.PressLeftButton();
996 EXPECT_FALSE(mouse_view1->pressed());
997 EXPECT_TRUE(mouse_view2->pressed());
1000 #if !defined(OS_CHROMEOS) && !defined(OS_WIN)
1001 // Test that a synthetic mouse exit is sent to the widget which was handling
1002 // mouse events when a different widget grabs capture.
1003 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1004 TEST_F(WidgetCaptureTest, MouseExitOnCaptureGrab) {
1005 Widget widget1;
1006 Widget::InitParams params1 =
1007 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1008 params1.native_widget = CreateNativeWidget(true, &widget1);
1009 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1010 widget1.Init(params1);
1011 MouseView* mouse_view1 = new MouseView;
1012 widget1.SetContentsView(mouse_view1);
1013 widget1.Show();
1014 widget1.SetBounds(gfx::Rect(300, 300));
1016 Widget widget2;
1017 Widget::InitParams params2 =
1018 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1019 params2.native_widget = CreateNativeWidget(true, &widget2);
1020 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1021 widget2.Init(params2);
1022 widget2.Show();
1023 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1025 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1026 generator.set_current_location(gfx::Point(100, 100));
1027 generator.MoveMouseBy(0, 0);
1029 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1030 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1032 widget2.SetCapture(NULL);
1033 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1034 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1035 // in addition to the one generated by Chrome.
1036 EXPECT_LT(0, mouse_view1->ExitedCalls());
1038 #endif // !defined(OS_CHROMEOS)
1040 namespace {
1042 // Widget observer which grabs capture when the widget is activated.
1043 class CaptureOnActivationObserver : public WidgetObserver {
1044 public:
1045 CaptureOnActivationObserver() {
1047 virtual ~CaptureOnActivationObserver() {
1050 // WidgetObserver:
1051 virtual 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