Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / ui / views / widget / widget_interactive_uitest.cc
blobb8f138dad6e56767a3d4402526ddc15d97a33bcf
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/ime/input_method.h"
13 #include "ui/base/ime/text_input_client.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/base/ui_base_paths.h"
16 #include "ui/base/ui_base_switches.h"
17 #include "ui/events/event_processor.h"
18 #include "ui/events/event_utils.h"
19 #include "ui/events/test/event_generator.h"
20 #include "ui/gfx/native_widget_types.h"
21 #include "ui/gl/gl_surface.h"
22 #include "ui/views/controls/textfield/textfield.h"
23 #include "ui/views/controls/textfield/textfield_test_api.h"
24 #include "ui/views/focus/focus_manager.h"
25 #include "ui/views/ime/input_method.h"
26 #include "ui/views/test/focus_manager_test.h"
27 #include "ui/views/test/widget_test.h"
28 #include "ui/views/touchui/touch_selection_controller_impl.h"
29 #include "ui/views/widget/widget.h"
30 #include "ui/views/window/dialog_delegate.h"
31 #include "ui/wm/public/activation_client.h"
33 #if defined(OS_WIN)
34 #include "ui/aura/window.h"
35 #include "ui/aura/window_tree_host.h"
36 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
37 #include "ui/views/win/hwnd_util.h"
38 #endif
40 namespace views {
41 namespace test {
43 namespace {
45 // A View that closes the Widget and exits the current message-loop when it
46 // receives a mouse-release event.
47 class ExitLoopOnRelease : public View {
48 public:
49 ExitLoopOnRelease() {}
50 ~ExitLoopOnRelease() override {}
52 private:
53 // Overridden from View:
54 void OnMouseReleased(const ui::MouseEvent& event) override {
55 GetWidget()->Close();
56 base::MessageLoop::current()->QuitNow();
59 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
62 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
63 class GestureCaptureView : public View {
64 public:
65 GestureCaptureView() {}
66 ~GestureCaptureView() override {}
68 private:
69 // Overridden from View:
70 void OnGestureEvent(ui::GestureEvent* event) override {
71 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
72 GetWidget()->SetCapture(this);
73 event->StopPropagation();
77 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
80 // A view that always processes all mouse events.
81 class MouseView : public View {
82 public:
83 MouseView()
84 : View(),
85 entered_(0),
86 exited_(0),
87 pressed_(0) {
89 ~MouseView() override {}
91 bool OnMousePressed(const ui::MouseEvent& event) override {
92 pressed_++;
93 return true;
96 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
98 void OnMouseExited(const ui::MouseEvent& event) override { 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 ~NestedLoopCaptureView() override {}
132 private:
133 // Overridden from View:
134 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 // Spins a run loop until a Widget's active state matches a desired state.
154 class WidgetActivationWaiter : public WidgetObserver {
155 public:
156 WidgetActivationWaiter(Widget* widget, bool active) : observed_(false) {
157 #if defined(OS_WIN)
158 // On Windows, a HWND can receive a WM_ACTIVATE message without the value
159 // of ::GetActiveWindow() updating to reflect that change. This can cause
160 // the active window reported by IsActive() to get out of sync. Usually this
161 // happens after a call to HWNDMessageHandler::Deactivate() which works by
162 // activating some other window, which might be in another application.
163 // Doing this can trigger the native OS activation-blocker, causing the
164 // taskbar icon to flash instead. But since activation of native widgets on
165 // Windows is synchronous, we never have to wait anyway, so it's safe to
166 // return here.
167 if (active == widget->IsActive()) {
168 observed_ = true;
169 return;
171 #endif
172 // Always expect a change for tests using this.
173 EXPECT_NE(active, widget->IsActive());
174 widget->AddObserver(this);
177 void Wait() {
178 if (!observed_)
179 run_loop_.Run();
182 void OnWidgetActivationChanged(Widget* widget, bool active) override {
183 observed_ = true;
184 widget->RemoveObserver(this);
185 if (run_loop_.running())
186 run_loop_.Quit();
189 private:
190 base::RunLoop run_loop_;
191 bool observed_;
193 DISALLOW_COPY_AND_ASSIGN(WidgetActivationWaiter);
196 ui::WindowShowState GetWidgetShowState(const Widget* widget) {
197 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
198 // because the former is implemented on all platforms but the latter is not.
199 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN :
200 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED :
201 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED :
202 widget->IsActive() ? ui::SHOW_STATE_NORMAL :
203 ui::SHOW_STATE_INACTIVE;
206 // Give the OS an opportunity to process messages for an activation change, when
207 // there is actually no change expected (e.g. ShowInactive()).
208 void RunPendingMessagesForActiveStatusChange() {
209 #if defined(OS_MACOSX)
210 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
211 // and made active in two steps, so tests should follow up with a ShowSync()
212 // or ActivateSync to ensure a consistent state.
213 base::RunLoop().RunUntilIdle();
214 #endif
215 // TODO(tapted): Check for desktop aura widgets.
218 // Activate a widget, and wait for it to become active. On non-desktop Aura
219 // this is just an activation. For other widgets, it means activating and then
220 // spinning the run loop until the OS has activated the window.
221 void ActivateSync(Widget* widget) {
222 WidgetActivationWaiter waiter(widget, true);
223 widget->Activate();
224 waiter.Wait();
227 // Like for ActivateSync(), wait for a widget to become active, but Show() the
228 // widget rather than calling Activate().
229 void ShowSync(Widget* widget) {
230 WidgetActivationWaiter waiter(widget, true);
231 widget->Show();
232 waiter.Wait();
235 void DeactivateSync(Widget* widget) {
236 #if defined(OS_MACOSX)
237 // Deactivation of a window isn't a concept on Mac: If an application is
238 // active and it has any activatable windows, then one of them is always
239 // active. But we can simulate deactivation (e.g. as if another application
240 // became active) by temporarily making |widget| non-activatable, then
241 // activating (and closing) a temporary widget.
242 widget->widget_delegate()->set_can_activate(false);
243 Widget* stealer = new Widget;
244 stealer->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW));
245 ShowSync(stealer);
246 stealer->CloseNow();
247 widget->widget_delegate()->set_can_activate(true);
248 #else
249 WidgetActivationWaiter waiter(widget, false);
250 widget->Deactivate();
251 waiter.Wait();
252 #endif
255 #if defined(OS_WIN)
256 void ActivatePlatformWindow(Widget* widget) {
257 ::SetActiveWindow(
258 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
260 #endif
262 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
263 // the OS a chance to activate a widget. However, for this case, the test
264 // doesn't expect that to happen, so there is nothing to wait for.
265 void ShowInactiveSync(Widget* widget) {
266 widget->ShowInactive();
267 RunPendingMessagesForActiveStatusChange();
270 } // namespace
272 class WidgetTestInteractive : public WidgetTest {
273 public:
274 WidgetTestInteractive() {}
275 ~WidgetTestInteractive() override {}
277 void SetUp() override {
278 gfx::GLSurface::InitializeOneOffForTests();
279 ui::RegisterPathProvider();
280 base::FilePath ui_test_pak_path;
281 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
282 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
283 WidgetTest::SetUp();
286 protected:
287 #if defined(USE_AURA)
288 static void ShowQuickMenuImmediately(
289 TouchSelectionControllerImpl* controller) {
290 DCHECK(controller);
291 if (controller->quick_menu_timer_.IsRunning()) {
292 controller->quick_menu_timer_.Stop();
293 controller->QuickMenuTimerFired();
296 #endif // defined (USE_AURA)
298 Widget* CreateWidget() {
299 Widget* widget = CreateNativeDesktopWidget();
300 widget->SetBounds(gfx::Rect(0, 0, 200, 200));
301 return widget;
305 #if defined(OS_WIN)
306 // Tests whether activation and focus change works correctly in Windows.
307 // We test the following:-
308 // 1. If the active aura window is correctly set when a top level widget is
309 // created.
310 // 2. If the active aura window in widget 1 created above, is set to NULL when
311 // another top level widget is created and focused.
312 // 3. On focusing the native platform window for widget 1, the active aura
313 // window for widget 1 should be set and that for widget 2 should reset.
314 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
315 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
316 // Create widget 1 and expect the active window to be its window.
317 View* focusable_view1 = new View;
318 focusable_view1->SetFocusable(true);
319 Widget* widget1 = CreateWidget();
320 widget1->GetContentsView()->AddChildView(focusable_view1);
321 widget1->Show();
322 aura::Window* root_window1 = widget1->GetNativeView()->GetRootWindow();
323 focusable_view1->RequestFocus();
325 EXPECT_TRUE(root_window1 != NULL);
326 aura::client::ActivationClient* activation_client1 =
327 aura::client::GetActivationClient(root_window1);
328 EXPECT_TRUE(activation_client1 != NULL);
329 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
331 // Create widget 2 and expect the active window to be its window.
332 View* focusable_view2 = new View;
333 Widget* widget2 = CreateWidget();
334 widget1->GetContentsView()->AddChildView(focusable_view2);
335 widget2->Show();
336 aura::Window* root_window2 = widget2->GetNativeView()->GetRootWindow();
337 focusable_view2->RequestFocus();
338 ActivatePlatformWindow(widget2);
340 aura::client::ActivationClient* activation_client2 =
341 aura::client::GetActivationClient(root_window2);
342 EXPECT_TRUE(activation_client2 != NULL);
343 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2->GetNativeView());
344 EXPECT_EQ(activation_client1->GetActiveWindow(),
345 reinterpret_cast<aura::Window*>(NULL));
347 // Now set focus back to widget 1 and expect the active window to be its
348 // window.
349 focusable_view1->RequestFocus();
350 ActivatePlatformWindow(widget1);
351 EXPECT_EQ(activation_client2->GetActiveWindow(),
352 reinterpret_cast<aura::Window*>(NULL));
353 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
355 widget2->CloseNow();
356 widget1->CloseNow();
358 #endif // defined(OS_WIN)
360 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
361 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
362 View* container = new View;
363 toplevel->SetContentsView(container);
365 EXPECT_FALSE(toplevel->HasCapture());
366 toplevel->SetCapture(NULL);
367 EXPECT_TRUE(toplevel->HasCapture());
369 // By default, mouse release removes capture.
370 gfx::Point click_location(45, 15);
371 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
372 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
373 ui::EF_LEFT_MOUSE_BUTTON);
374 toplevel->OnMouseEvent(&release);
375 EXPECT_FALSE(toplevel->HasCapture());
377 // Now a mouse release shouldn't remove capture.
378 toplevel->set_auto_release_capture(false);
379 toplevel->SetCapture(NULL);
380 EXPECT_TRUE(toplevel->HasCapture());
381 toplevel->OnMouseEvent(&release);
382 EXPECT_TRUE(toplevel->HasCapture());
383 toplevel->ReleaseCapture();
384 EXPECT_FALSE(toplevel->HasCapture());
386 toplevel->Close();
387 RunPendingMessages();
390 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
391 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
392 View* container = new View;
393 toplevel->SetContentsView(container);
395 View* gesture = new GestureCaptureView;
396 gesture->SetBounds(0, 0, 30, 30);
397 container->AddChildView(gesture);
399 MouseView* mouse = new MouseView;
400 mouse->SetBounds(30, 0, 30, 30);
401 container->AddChildView(mouse);
403 toplevel->SetSize(gfx::Size(100, 100));
404 toplevel->Show();
406 // Start a gesture on |gesture|.
407 ui::GestureEvent tap_down(15,
410 base::TimeDelta(),
411 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
412 ui::GestureEvent end(15,
415 base::TimeDelta(),
416 ui::GestureEventDetails(ui::ET_GESTURE_END));
417 toplevel->OnGestureEvent(&tap_down);
419 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
420 // will not receive the event.
421 gfx::Point click_location(45, 15);
423 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
424 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
425 ui::EF_LEFT_MOUSE_BUTTON);
426 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
427 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
428 ui::EF_LEFT_MOUSE_BUTTON);
430 EXPECT_TRUE(toplevel->HasCapture());
432 toplevel->OnMouseEvent(&press);
433 toplevel->OnMouseEvent(&release);
434 EXPECT_EQ(0, mouse->pressed());
436 EXPECT_FALSE(toplevel->HasCapture());
438 // The end of the gesture should release the capture, and pressing on |mouse|
439 // should now reach |mouse|.
440 toplevel->OnGestureEvent(&end);
441 toplevel->OnMouseEvent(&press);
442 toplevel->OnMouseEvent(&release);
443 EXPECT_EQ(1, mouse->pressed());
445 toplevel->Close();
446 RunPendingMessages();
449 // Checks that if a mouse-press triggers a capture on a different widget (which
450 // consumes the mouse-release event), then the target of the press does not have
451 // capture.
452 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
453 // The test creates two widgets: |first| and |second|.
454 // The View in |first| makes |second| visible, sets capture on it, and starts
455 // a nested loop (like a menu does). The View in |second| terminates the
456 // nested loop and closes the widget.
457 // The test sends a mouse-press event to |first|, and posts a task to send a
458 // release event to |second|, to make sure that the release event is
459 // dispatched after the nested loop starts.
461 Widget* first = CreateTopLevelFramelessPlatformWidget();
462 Widget* second = CreateTopLevelFramelessPlatformWidget();
464 View* container = new NestedLoopCaptureView(second);
465 first->SetContentsView(container);
467 second->SetContentsView(new ExitLoopOnRelease());
469 first->SetSize(gfx::Size(100, 100));
470 first->Show();
472 gfx::Point location(20, 20);
473 base::MessageLoop::current()->PostTask(
474 FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second),
475 base::Owned(new ui::MouseEvent(
476 ui::ET_MOUSE_RELEASED, location, location,
477 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
478 ui::EF_LEFT_MOUSE_BUTTON))));
479 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
480 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
481 ui::EF_LEFT_MOUSE_BUTTON);
482 first->OnMouseEvent(&press);
483 EXPECT_FALSE(first->HasCapture());
484 first->Close();
485 RunPendingMessages();
488 // Tests some grab/ungrab events.
489 // TODO(estade): can this be enabled now that this is an interactive ui test?
490 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
491 Widget* toplevel = CreateTopLevelPlatformWidget();
492 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
493 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
495 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
497 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
498 View* view = new MouseView();
499 view->SetBounds(0, 0, 300, 300);
500 child1->GetRootView()->AddChildView(view);
502 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
503 view = new MouseView();
504 view->SetBounds(0, 0, 200, 200);
505 child2->GetRootView()->AddChildView(view);
507 toplevel->Show();
508 RunPendingMessages();
510 // Click on child1
511 gfx::Point p1(45, 45);
512 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
513 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
514 toplevel->OnMouseEvent(&pressed);
516 EXPECT_TRUE(toplevel->HasCapture());
517 EXPECT_TRUE(child1->HasCapture());
518 EXPECT_FALSE(child2->HasCapture());
520 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(),
521 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
522 toplevel->OnMouseEvent(&released);
524 EXPECT_FALSE(toplevel->HasCapture());
525 EXPECT_FALSE(child1->HasCapture());
526 EXPECT_FALSE(child2->HasCapture());
528 RunPendingMessages();
530 // Click on child2
531 gfx::Point p2(315, 45);
532 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(),
533 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
534 toplevel->OnMouseEvent(&pressed2);
535 EXPECT_TRUE(pressed2.handled());
536 EXPECT_TRUE(toplevel->HasCapture());
537 EXPECT_TRUE(child2->HasCapture());
538 EXPECT_FALSE(child1->HasCapture());
540 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(),
541 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
542 toplevel->OnMouseEvent(&released2);
543 EXPECT_FALSE(toplevel->HasCapture());
544 EXPECT_FALSE(child1->HasCapture());
545 EXPECT_FALSE(child2->HasCapture());
547 toplevel->CloseNow();
550 // Tests mouse move outside of the window into the "resize controller" and back
551 // will still generate an OnMouseEntered and OnMouseExited event..
552 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
553 Widget* toplevel = CreateTopLevelPlatformWidget();
555 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
557 MouseView* view = new MouseView();
558 view->SetBounds(90, 90, 10, 10);
559 toplevel->GetRootView()->AddChildView(view);
561 toplevel->Show();
562 RunPendingMessages();
564 // Move to an outside position.
565 gfx::Point p1(200, 200);
566 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
567 ui::EF_NONE, ui::EF_NONE);
568 toplevel->OnMouseEvent(&moved_out);
569 EXPECT_EQ(0, view->EnteredCalls());
570 EXPECT_EQ(0, view->ExitedCalls());
572 // Move onto the active view.
573 gfx::Point p2(95, 95);
574 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
575 ui::EF_NONE, ui::EF_NONE);
576 toplevel->OnMouseEvent(&moved_over);
577 EXPECT_EQ(1, view->EnteredCalls());
578 EXPECT_EQ(0, view->ExitedCalls());
580 // Move onto the outer resizing border.
581 gfx::Point p3(102, 95);
582 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
583 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
584 toplevel->OnMouseEvent(&moved_resizer);
585 EXPECT_EQ(0, view->EnteredCalls());
586 EXPECT_EQ(1, view->ExitedCalls());
588 // Move onto the view again.
589 toplevel->OnMouseEvent(&moved_over);
590 EXPECT_EQ(1, view->EnteredCalls());
591 EXPECT_EQ(0, view->ExitedCalls());
593 RunPendingMessages();
595 toplevel->CloseNow();
598 // Test view focus restoration when a widget is deactivated and re-activated.
599 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
600 Widget* widget1 = CreateTopLevelPlatformWidget();
601 View* view1 = new View;
602 view1->SetFocusable(true);
603 widget1->GetContentsView()->AddChildView(view1);
605 Widget* widget2 = CreateTopLevelPlatformWidget();
606 View* view2a = new View;
607 View* view2b = new View;
608 view2a->SetFocusable(true);
609 view2b->SetFocusable(true);
610 widget2->GetContentsView()->AddChildView(view2a);
611 widget2->GetContentsView()->AddChildView(view2b);
613 ShowSync(widget1);
614 EXPECT_TRUE(widget1->IsActive());
615 view1->RequestFocus();
616 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
618 ShowSync(widget2);
619 EXPECT_TRUE(widget2->IsActive());
620 EXPECT_FALSE(widget1->IsActive());
621 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
622 view2a->RequestFocus();
623 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
624 view2b->RequestFocus();
625 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
627 ActivateSync(widget1);
628 EXPECT_TRUE(widget1->IsActive());
629 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
630 EXPECT_FALSE(widget2->IsActive());
631 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
633 ActivateSync(widget2);
634 EXPECT_TRUE(widget2->IsActive());
635 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
636 EXPECT_FALSE(widget1->IsActive());
637 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
639 widget1->CloseNow();
640 widget2->CloseNow();
643 #if defined(OS_WIN)
645 // Test view focus retention when a widget's HWND is disabled and re-enabled.
646 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
647 Widget* widget = CreateTopLevelFramelessPlatformWidget();
648 widget->SetContentsView(new View);
649 for (size_t i = 0; i < 2; ++i) {
650 widget->GetContentsView()->AddChildView(new View);
651 widget->GetContentsView()->child_at(i)->SetFocusable(true);
654 widget->Show();
655 const HWND hwnd = HWNDForWidget(widget);
656 EXPECT_TRUE(::IsWindow(hwnd));
657 EXPECT_TRUE(::IsWindowEnabled(hwnd));
658 EXPECT_EQ(hwnd, ::GetActiveWindow());
660 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
661 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
662 View* view = widget->GetContentsView()->child_at(i);
664 view->RequestFocus();
665 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
666 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
667 EXPECT_FALSE(::IsWindowEnabled(hwnd));
669 // Oddly, disabling the HWND leaves it active with the focus unchanged.
670 EXPECT_EQ(hwnd, ::GetActiveWindow());
671 EXPECT_TRUE(widget->IsActive());
672 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
674 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
675 EXPECT_TRUE(::IsWindowEnabled(hwnd));
676 EXPECT_EQ(hwnd, ::GetActiveWindow());
677 EXPECT_TRUE(widget->IsActive());
678 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
681 widget->CloseNow();
684 // This class subclasses the Widget class to listen for activation change
685 // notifications and provides accessors to return information as to whether
686 // the widget is active. We need this to ensure that users of the widget
687 // class activate the widget only when the underlying window becomes really
688 // active. Previously we would activate the widget in the WM_NCACTIVATE
689 // message which is incorrect because APIs like FlashWindowEx flash the
690 // window caption by sending fake WM_NCACTIVATE messages.
691 class WidgetActivationTest : public Widget {
692 public:
693 WidgetActivationTest()
694 : active_(false) {}
696 ~WidgetActivationTest() override {}
698 void OnNativeWidgetActivationChanged(bool active) override {
699 active_ = active;
702 bool active() const { return active_; }
704 private:
705 bool active_;
707 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
710 // Tests whether the widget only becomes active when the underlying window
711 // is really active.
712 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
713 WidgetActivationTest widget1;
714 Widget::InitParams init_params =
715 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
716 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
717 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
718 init_params.bounds = gfx::Rect(0, 0, 200, 200);
719 widget1.Init(init_params);
720 widget1.Show();
721 EXPECT_EQ(true, widget1.active());
723 WidgetActivationTest widget2;
724 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
725 widget2.Init(init_params);
726 widget2.Show();
727 EXPECT_EQ(true, widget2.active());
728 EXPECT_EQ(false, widget1.active());
730 HWND win32_native_window1 = HWNDForWidget(&widget1);
731 EXPECT_TRUE(::IsWindow(win32_native_window1));
733 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
734 EXPECT_EQ(false, widget1.active());
735 EXPECT_EQ(true, widget2.active());
737 ::SetActiveWindow(win32_native_window1);
738 EXPECT_EQ(true, widget1.active());
739 EXPECT_EQ(false, widget2.active());
741 #endif // defined(OS_WIN)
743 #if !defined(OS_CHROMEOS)
744 // Provides functionality to create a window modal dialog.
745 class ModalDialogDelegate : public DialogDelegateView {
746 public:
747 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
748 ~ModalDialogDelegate() override {}
750 // WidgetDelegate overrides.
751 ui::ModalType GetModalType() const override { return type_; }
753 private:
754 ui::ModalType type_;
756 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
759 // Tests whether the focused window is set correctly when a modal window is
760 // created and destroyed. When it is destroyed it should focus the owner window.
761 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
762 TestWidgetFocusChangeListener focus_listener;
763 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
764 const std::vector<gfx::NativeView>& focus_changes =
765 focus_listener.focus_changes();
767 // Create a top level widget.
768 Widget top_level_widget;
769 Widget::InitParams init_params =
770 CreateParams(Widget::InitParams::TYPE_WINDOW);
771 init_params.show_state = ui::SHOW_STATE_NORMAL;
772 gfx::Rect initial_bounds(0, 0, 500, 500);
773 init_params.bounds = initial_bounds;
774 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
775 init_params.native_widget =
776 new PlatformDesktopNativeWidget(&top_level_widget);
777 top_level_widget.Init(init_params);
778 ShowSync(&top_level_widget);
780 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
781 ASSERT_FALSE(focus_listener.focus_changes().empty());
782 EXPECT_EQ(1u, focus_changes.size());
783 EXPECT_EQ(top_level_native_view, focus_changes[0]);
785 // Create a modal dialog.
786 // This instance will be destroyed when the dialog is destroyed.
787 ModalDialogDelegate* dialog_delegate =
788 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
790 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
791 dialog_delegate, NULL, top_level_widget.GetNativeView());
792 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
794 // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
795 // active status synchronously, even on Mac.
796 modal_dialog_widget->Show();
798 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
799 EXPECT_EQ(3u, focus_changes.size());
800 EXPECT_EQ(nullptr, focus_changes[1]);
801 EXPECT_EQ(modal_native_view, focus_changes[2]);
803 #if defined(OS_MACOSX)
804 // Window modal dialogs on Mac are "sheets", which animate to close before
805 // activating their parent widget.
806 WidgetActivationWaiter waiter(&top_level_widget, true);
807 modal_dialog_widget->Close();
808 waiter.Wait();
809 #else
810 modal_dialog_widget->CloseNow();
811 #endif
813 EXPECT_EQ(5u, focus_changes.size());
814 EXPECT_EQ(nullptr, focus_changes[3]);
815 EXPECT_EQ(top_level_native_view, focus_changes[4]);
817 top_level_widget.CloseNow();
818 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
821 // Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
822 // was deprecated. It does have application modal windows, but only Ash requests
823 // those.
824 #if defined(OS_MACOSX) && !defined(USE_AURA)
825 #define MAYBE_SystemModalWindowReleasesCapture \
826 DISABLED_SystemModalWindowReleasesCapture
827 #else
828 #define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
829 #endif
831 // Test that when opening a system-modal window, capture is released.
832 TEST_F(WidgetTestInteractive, MAYBE_SystemModalWindowReleasesCapture) {
833 TestWidgetFocusChangeListener focus_listener;
834 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
836 // Create a top level widget.
837 Widget top_level_widget;
838 Widget::InitParams init_params =
839 CreateParams(Widget::InitParams::TYPE_WINDOW);
840 init_params.show_state = ui::SHOW_STATE_NORMAL;
841 gfx::Rect initial_bounds(0, 0, 500, 500);
842 init_params.bounds = initial_bounds;
843 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
844 init_params.native_widget =
845 new PlatformDesktopNativeWidget(&top_level_widget);
846 top_level_widget.Init(init_params);
847 ShowSync(&top_level_widget);
849 ASSERT_FALSE(focus_listener.focus_changes().empty());
850 EXPECT_EQ(top_level_widget.GetNativeView(),
851 focus_listener.focus_changes().back());;
853 EXPECT_FALSE(top_level_widget.HasCapture());
854 top_level_widget.SetCapture(NULL);
855 EXPECT_TRUE(top_level_widget.HasCapture());
857 // Create a modal dialog.
858 ModalDialogDelegate* dialog_delegate =
859 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
861 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
862 dialog_delegate, NULL, top_level_widget.GetNativeView());
863 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
864 ShowSync(modal_dialog_widget);
866 EXPECT_FALSE(top_level_widget.HasCapture());
868 modal_dialog_widget->CloseNow();
869 top_level_widget.CloseNow();
870 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
873 #endif // !defined(OS_CHROMEOS)
875 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
876 Widget widget;
877 Widget::InitParams init_params =
878 CreateParams(Widget::InitParams::TYPE_WINDOW);
879 init_params.bounds = gfx::Rect(0, 0, 200, 200);
880 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
881 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
882 #if !defined(OS_CHROMEOS)
883 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
884 #endif // !defined(OS_CHROMEOS)
885 widget.Init(init_params);
887 widget.Show();
888 EXPECT_FALSE(widget.IsActive());
891 #if defined(USE_AURA)
892 // Test that touch selection quick menu is not activated when opened.
893 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
894 base::CommandLine::ForCurrentProcess()->AppendSwitch(
895 switches::kEnableTouchEditing);
896 #if defined(OS_WIN)
897 views_delegate().set_use_desktop_native_widgets(true);
898 #endif // !defined(OS_WIN)
900 Widget* widget = CreateWidget();
902 Textfield* textfield = new Textfield;
903 textfield->SetBounds(0, 0, 200, 20);
904 textfield->SetText(base::ASCIIToUTF16("some text"));
905 widget->GetRootView()->AddChildView(textfield);
907 widget->Show();
908 textfield->RequestFocus();
909 textfield->SelectAll(true);
910 TextfieldTestApi textfield_test_api(textfield);
912 RunPendingMessages();
914 ui::test::EventGenerator generator(widget->GetNativeWindow());
915 generator.GestureTapAt(gfx::Point(10, 10));
916 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
917 textfield_test_api.touch_selection_controller()));
919 EXPECT_TRUE(textfield->HasFocus());
920 EXPECT_TRUE(widget->IsActive());
921 EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
922 widget->CloseNow();
924 #endif // defined(USE_AURA)
926 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
927 #if defined(OS_WIN)
928 views_delegate().set_use_desktop_native_widgets(true);
929 #endif // !defined(OS_WIN)
931 // Create first widget and view, activate the widget, and focus the view.
932 Widget widget1;
933 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
934 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
935 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
936 widget1.Init(params1);
938 View* view1 = new View();
939 view1->SetFocusable(true);
940 widget1.GetRootView()->AddChildView(view1);
942 ActivateSync(&widget1);
944 FocusManager* focus_manager1 = widget1.GetFocusManager();
945 ASSERT_TRUE(focus_manager1);
946 focus_manager1->SetFocusedView(view1);
947 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
949 // Create second widget and view, activate the widget, and focus the view.
950 Widget widget2;
951 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
952 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
953 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
954 widget2.Init(params2);
956 View* view2 = new View();
957 view2->SetFocusable(true);
958 widget2.GetRootView()->AddChildView(view2);
960 ActivateSync(&widget2);
961 EXPECT_TRUE(widget2.IsActive());
962 EXPECT_FALSE(widget1.IsActive());
964 FocusManager* focus_manager2 = widget2.GetFocusManager();
965 ASSERT_TRUE(focus_manager2);
966 focus_manager2->SetFocusedView(view2);
967 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
969 // Disable the first view and make sure it loses focus, but its widget is not
970 // activated.
971 view1->SetEnabled(false);
972 EXPECT_NE(view1, focus_manager1->GetFocusedView());
973 EXPECT_FALSE(widget1.IsActive());
974 EXPECT_TRUE(widget2.IsActive());
977 TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
978 Widget* widget = CreateTopLevelPlatformWidget();
980 ShowSync(widget);
981 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
983 widget->CloseNow();
986 TEST_F(WidgetTestInteractive, ShowInactive) {
987 Widget* widget = CreateTopLevelPlatformWidget();
989 ShowInactiveSync(widget);
990 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE);
992 widget->CloseNow();
995 TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
996 Widget* widget = CreateTopLevelPlatformWidget();
998 EXPECT_FALSE(widget->IsActive());
999 EXPECT_FALSE(widget->IsVisible());
1001 ShowSync(widget);
1003 EXPECT_TRUE(widget->IsActive());
1004 EXPECT_TRUE(widget->IsVisible());
1006 widget->CloseNow();
1009 TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
1010 // Create 2 widgets to ensure window layering does not change.
1011 Widget* widget = CreateTopLevelPlatformWidget();
1012 Widget* widget2 = CreateTopLevelPlatformWidget();
1014 ShowSync(widget2);
1015 EXPECT_FALSE(widget->IsActive());
1016 EXPECT_TRUE(widget2->IsVisible());
1017 EXPECT_TRUE(widget2->IsActive());
1019 ShowSync(widget);
1020 EXPECT_TRUE(widget->IsActive());
1021 EXPECT_FALSE(widget2->IsActive());
1023 ShowInactiveSync(widget);
1024 EXPECT_TRUE(widget->IsActive());
1025 EXPECT_FALSE(widget2->IsActive());
1026 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1028 widget2->CloseNow();
1029 widget->CloseNow();
1032 TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
1033 Widget* widget = CreateTopLevelPlatformWidget();
1034 widget->SetBounds(gfx::Rect(100, 100, 100, 100));
1036 ShowInactiveSync(widget);
1037 ShowSync(widget);
1038 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1040 widget->CloseNow();
1043 #if !defined(OS_CHROMEOS)
1044 TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
1045 Widget* widget = CreateTopLevelPlatformWidget();
1046 ShowSync(widget);
1047 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1049 Widget widget2;
1050 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
1051 params.native_widget = new PlatformDesktopNativeWidget(&widget2);
1052 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1053 widget2.Init(params);
1054 widget2.Show();
1055 RunPendingMessagesForActiveStatusChange();
1057 EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE);
1058 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1060 widget->CloseNow();
1061 widget2.CloseNow();
1063 #endif // !defined(OS_CHROMEOS)
1065 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1066 // currently only Desktop widgets and fullscreen changes have to coordinate with
1067 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1068 // Maximize on mac is also (intentionally) a no-op.
1069 #if defined(OS_MACOSX) && !defined(USE_AURA)
1070 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1071 #else
1072 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1073 #endif
1075 // Test that window state is not changed after getting out of full screen.
1076 TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
1077 Widget* toplevel = CreateTopLevelPlatformWidget();
1079 toplevel->Show();
1080 RunPendingMessages();
1082 // This should be a normal state window.
1083 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1085 toplevel->SetFullscreen(true);
1086 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1087 toplevel->SetFullscreen(false);
1088 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1090 // And it should still be in normal state after getting out of full screen.
1091 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1093 // Now, make it maximized.
1094 toplevel->Maximize();
1095 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1097 toplevel->SetFullscreen(true);
1098 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1099 toplevel->SetFullscreen(false);
1100 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1102 // And it stays maximized after getting out of full screen.
1103 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1105 // Clean up.
1106 toplevel->Close();
1107 RunPendingMessages();
1110 namespace {
1112 // Used to veirfy OnMouseCaptureLost() has been invoked.
1113 class CaptureLostTrackingWidget : public Widget {
1114 public:
1115 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
1116 ~CaptureLostTrackingWidget() override {}
1118 bool GetAndClearGotCaptureLost() {
1119 bool value = got_capture_lost_;
1120 got_capture_lost_ = false;
1121 return value;
1124 // Widget:
1125 void OnMouseCaptureLost() override {
1126 got_capture_lost_ = true;
1127 Widget::OnMouseCaptureLost();
1130 private:
1131 bool got_capture_lost_;
1133 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
1136 } // namespace
1138 class WidgetCaptureTest : public ViewsTestBase {
1139 public:
1140 WidgetCaptureTest() {
1143 ~WidgetCaptureTest() override {}
1145 void SetUp() override {
1146 gfx::GLSurface::InitializeOneOffForTests();
1147 ui::RegisterPathProvider();
1148 base::FilePath ui_test_pak_path;
1149 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
1150 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
1151 ViewsTestBase::SetUp();
1154 // Verifies Widget::SetCapture() results in updating native capture along with
1155 // invoking the right Widget function.
1156 void TestCapture(bool use_desktop_native_widget) {
1157 CaptureLostTrackingWidget widget1;
1158 Widget::InitParams params1 =
1159 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1160 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
1161 &widget1);
1162 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1163 widget1.Init(params1);
1164 widget1.Show();
1166 CaptureLostTrackingWidget widget2;
1167 Widget::InitParams params2 =
1168 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1169 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1170 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
1171 &widget2);
1172 widget2.Init(params2);
1173 widget2.Show();
1175 // Set capture to widget2 and verity it gets it.
1176 widget2.SetCapture(widget2.GetRootView());
1177 EXPECT_FALSE(widget1.HasCapture());
1178 EXPECT_TRUE(widget2.HasCapture());
1179 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1180 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1182 // Set capture to widget1 and verify it gets it.
1183 widget1.SetCapture(widget1.GetRootView());
1184 EXPECT_TRUE(widget1.HasCapture());
1185 EXPECT_FALSE(widget2.HasCapture());
1186 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1187 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
1189 // Release and verify no one has it.
1190 widget1.ReleaseCapture();
1191 EXPECT_FALSE(widget1.HasCapture());
1192 EXPECT_FALSE(widget2.HasCapture());
1193 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
1194 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1197 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
1198 Widget* widget) {
1199 #if !defined(OS_CHROMEOS)
1200 if (create_desktop_native_widget)
1201 return new PlatformDesktopNativeWidget(widget);
1202 #endif
1203 return NULL;
1206 private:
1207 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
1210 // See description in TestCapture().
1211 TEST_F(WidgetCaptureTest, Capture) {
1212 TestCapture(false);
1215 #if !defined(OS_CHROMEOS)
1216 // See description in TestCapture(). Creates DesktopNativeWidget.
1217 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
1218 TestCapture(true);
1220 #endif
1222 // Test that no state is set if capture fails.
1223 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
1224 Widget widget;
1225 Widget::InitParams params =
1226 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1227 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1228 params.bounds = gfx::Rect(400, 400);
1229 widget.Init(params);
1231 MouseView* mouse_view1 = new MouseView;
1232 MouseView* mouse_view2 = new MouseView;
1233 View* contents_view = new View;
1234 contents_view->AddChildView(mouse_view1);
1235 contents_view->AddChildView(mouse_view2);
1236 widget.SetContentsView(contents_view);
1238 mouse_view1->SetBounds(0, 0, 200, 400);
1239 mouse_view2->SetBounds(200, 0, 200, 400);
1241 // Setting capture should fail because |widget| is not visible.
1242 widget.SetCapture(mouse_view1);
1243 EXPECT_FALSE(widget.HasCapture());
1245 widget.Show();
1246 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
1247 generator.set_current_location(gfx::Point(300, 10));
1248 generator.PressLeftButton();
1250 EXPECT_FALSE(mouse_view1->pressed());
1251 EXPECT_TRUE(mouse_view2->pressed());
1254 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1255 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1256 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1257 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
1258 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1259 #else
1260 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1261 #endif
1263 // Test that a synthetic mouse exit is sent to the widget which was handling
1264 // mouse events when a different widget grabs capture.
1265 TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
1266 Widget widget1;
1267 Widget::InitParams params1 =
1268 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1269 params1.native_widget = CreateNativeWidget(true, &widget1);
1270 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1271 widget1.Init(params1);
1272 MouseView* mouse_view1 = new MouseView;
1273 widget1.SetContentsView(mouse_view1);
1274 widget1.Show();
1275 widget1.SetBounds(gfx::Rect(300, 300));
1277 Widget widget2;
1278 Widget::InitParams params2 =
1279 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1280 params2.native_widget = CreateNativeWidget(true, &widget2);
1281 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1282 widget2.Init(params2);
1283 widget2.Show();
1284 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1286 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1287 generator.set_current_location(gfx::Point(100, 100));
1288 generator.MoveMouseBy(0, 0);
1290 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1291 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1293 widget2.SetCapture(NULL);
1294 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1295 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1296 // in addition to the one generated by Chrome.
1297 EXPECT_LT(0, mouse_view1->ExitedCalls());
1300 namespace {
1302 // Widget observer which grabs capture when the widget is activated.
1303 class CaptureOnActivationObserver : public WidgetObserver {
1304 public:
1305 CaptureOnActivationObserver() : activation_observed_(false) {}
1306 ~CaptureOnActivationObserver() override {}
1308 // WidgetObserver:
1309 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1310 if (active) {
1311 widget->SetCapture(nullptr);
1312 activation_observed_ = true;
1316 bool activation_observed() const { return activation_observed_; }
1318 private:
1319 bool activation_observed_;
1321 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1324 } // namespace
1326 // Test that setting capture on widget activation of a non-toplevel widget
1327 // (e.g. a bubble on Linux) succeeds.
1328 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1329 Widget toplevel;
1330 Widget::InitParams toplevel_params =
1331 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1332 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel);
1333 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1334 toplevel.Init(toplevel_params);
1335 toplevel.Show();
1337 Widget* child = new Widget;
1338 Widget::InitParams child_params =
1339 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1340 child_params.parent = toplevel.GetNativeView();
1341 child_params.context = toplevel.GetNativeWindow();
1342 child->Init(child_params);
1344 CaptureOnActivationObserver observer;
1345 child->AddObserver(&observer);
1346 child->Show();
1348 #if defined(OS_MACOSX) && !defined(USE_AURA)
1349 // On Mac, activation is asynchronous. A single trip to the runloop should be
1350 // sufficient. On Aura platforms, note that since the child widget isn't top-
1351 // level, the aura window manager gets asked whether the widget is active, not
1352 // the OS.
1353 base::RunLoop().RunUntilIdle();
1354 #endif
1356 EXPECT_TRUE(observer.activation_observed());
1357 EXPECT_TRUE(child->HasCapture());
1361 #if defined(OS_WIN)
1362 namespace {
1364 // Used to verify OnMouseEvent() has been invoked.
1365 class MouseEventTrackingWidget : public Widget {
1366 public:
1367 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1368 ~MouseEventTrackingWidget() override {}
1370 bool GetAndClearGotMouseEvent() {
1371 bool value = got_mouse_event_;
1372 got_mouse_event_ = false;
1373 return value;
1376 // Widget:
1377 void OnMouseEvent(ui::MouseEvent* event) override {
1378 got_mouse_event_ = true;
1379 Widget::OnMouseEvent(event);
1382 private:
1383 bool got_mouse_event_;
1385 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1388 } // namespace
1390 // Verifies if a mouse event is received on a widget that doesn't have capture
1391 // on Windows that it is correctly processed by the widget that doesn't have
1392 // capture. This behavior is not desired on OSes other than Windows.
1393 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1394 MouseEventTrackingWidget widget1;
1395 Widget::InitParams params1 =
1396 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1397 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1398 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1399 widget1.Init(params1);
1400 widget1.Show();
1402 MouseEventTrackingWidget widget2;
1403 Widget::InitParams params2 =
1404 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1405 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1406 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1407 widget2.Init(params2);
1408 widget2.Show();
1410 // Set capture to widget2 and verity it gets it.
1411 widget2.SetCapture(widget2.GetRootView());
1412 EXPECT_FALSE(widget1.HasCapture());
1413 EXPECT_TRUE(widget2.HasCapture());
1415 widget1.GetAndClearGotMouseEvent();
1416 widget2.GetAndClearGotMouseEvent();
1417 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1418 // |widget2| has capture, |widget1| should still get the event.
1419 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1420 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
1421 ui::EventDispatchDetails details = widget1.GetNativeWindow()->
1422 GetHost()->event_processor()->OnEventFromSource(&mouse_event);
1423 ASSERT_FALSE(details.dispatcher_destroyed);
1424 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1425 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1427 #endif // defined(OS_WIN)
1429 class WidgetInputMethodInteractiveTest : public WidgetTestInteractive {
1430 public:
1431 WidgetInputMethodInteractiveTest() {}
1433 // testing::Test:
1434 void SetUp() override {
1435 WidgetTestInteractive::SetUp();
1436 #if defined(OS_WIN)
1437 // On Windows, Widget::Deactivate() works by activating the next topmost
1438 // window on the z-order stack. This only works if there is at least one
1439 // other window, so make sure that is the case.
1440 deactivate_widget_ = CreateWidget();
1441 deactivate_widget_->Show();
1442 #endif
1445 void TearDown() override {
1446 if (deactivate_widget_)
1447 deactivate_widget_->CloseNow();
1448 WidgetTestInteractive::TearDown();
1451 private:
1452 Widget* deactivate_widget_ = nullptr;
1454 DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest);
1457 // Test input method focus changes affected by top window activaction.
1458 TEST_F(WidgetInputMethodInteractiveTest, Activation) {
1459 Widget* widget = CreateWidget();
1460 Textfield* textfield = new Textfield;
1461 widget->GetRootView()->AddChildView(textfield);
1462 textfield->RequestFocus();
1464 ShowSync(widget);
1466 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1467 widget->GetInputMethod()->GetTextInputType());
1469 DeactivateSync(widget);
1471 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1472 widget->GetInputMethod()->GetTextInputType());
1473 widget->CloseNow();
1476 // Test input method focus changes affected by focus changes within 1 window.
1477 TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
1478 Widget* widget = CreateWidget();
1479 Textfield* textfield1 = new Textfield;
1480 Textfield* textfield2 = new Textfield;
1481 textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1482 widget->GetRootView()->AddChildView(textfield1);
1483 widget->GetRootView()->AddChildView(textfield2);
1485 ShowSync(widget);
1487 textfield1->RequestFocus();
1488 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1489 widget->GetInputMethod()->GetTextInputType());
1491 textfield2->RequestFocus();
1492 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1493 widget->GetInputMethod()->GetTextInputType());
1495 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1496 // DNWA (which just activates the last active window) and involves the
1497 // AuraTestHelper which sets the input method as DummyInputMethod.
1498 #if !defined(OS_CHROMEOS)
1499 DeactivateSync(widget);
1500 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1501 widget->GetInputMethod()->GetTextInputType());
1503 ActivateSync(widget);
1504 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1505 widget->GetInputMethod()->GetTextInputType());
1507 DeactivateSync(widget);
1508 textfield1->RequestFocus();
1509 ActivateSync(widget);
1510 EXPECT_TRUE(widget->IsActive());
1511 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1512 widget->GetInputMethod()->GetTextInputType());
1513 #endif
1514 widget->CloseNow();
1517 // Test input method focus changes affected by focus changes cross 2 windows
1518 // which shares the same top window.
1519 TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
1520 Widget* parent = CreateWidget();
1521 parent->SetBounds(gfx::Rect(100, 100, 100, 100));
1523 Widget* child = CreateChildNativeWidgetWithParent(parent);
1524 child->SetBounds(gfx::Rect(0, 0, 50, 50));
1525 child->Show();
1527 Textfield* textfield_parent = new Textfield;
1528 Textfield* textfield_child = new Textfield;
1529 textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1530 parent->GetRootView()->AddChildView(textfield_parent);
1531 child->GetRootView()->AddChildView(textfield_child);
1532 ShowSync(parent);
1534 EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod());
1536 textfield_parent->RequestFocus();
1537 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1538 parent->GetInputMethod()->GetTextInputType());
1540 textfield_child->RequestFocus();
1541 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1542 parent->GetInputMethod()->GetTextInputType());
1544 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1545 // DNWA (which just activates the last active window) and involves the
1546 // AuraTestHelper which sets the input method as DummyInputMethod.
1547 #if !defined(OS_CHROMEOS)
1548 DeactivateSync(parent);
1549 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1550 parent->GetInputMethod()->GetTextInputType());
1552 ActivateSync(parent);
1553 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1554 parent->GetInputMethod()->GetTextInputType());
1556 textfield_parent->RequestFocus();
1557 DeactivateSync(parent);
1558 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1559 parent->GetInputMethod()->GetTextInputType());
1561 ActivateSync(parent);
1562 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1563 parent->GetInputMethod()->GetTextInputType());
1564 #endif
1566 parent->CloseNow();
1569 // Test input method focus changes affected by focus changes cross 2 top
1570 // windows.
1571 TEST_F(WidgetInputMethodInteractiveTest, TwoTopWindows) {
1572 Widget* widget1 = CreateWidget();
1573 Widget* widget2 = CreateWidget();
1574 Textfield* textfield1 = new Textfield;
1575 Textfield* textfield2 = new Textfield;
1576 textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1577 widget1->GetRootView()->AddChildView(textfield1);
1578 widget2->GetRootView()->AddChildView(textfield2);
1580 // Do the initial shows synchronously. Otherwise, on X11, the window server
1581 // messages may be interleaved with the activation requests below.
1582 ShowSync(widget1);
1583 ShowSync(widget2);
1585 textfield1->RequestFocus();
1586 textfield2->RequestFocus();
1588 ActivateSync(widget1);
1590 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1591 widget1->GetInputMethod()->GetTextInputType());
1592 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1593 widget2->GetInputMethod()->GetTextInputType());
1595 ActivateSync(widget2);
1597 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1598 widget1->GetInputMethod()->GetTextInputType());
1599 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1600 widget2->GetInputMethod()->GetTextInputType());
1602 widget2->CloseNow();
1603 widget1->CloseNow();
1606 // Test input method focus changes affected by textfield's state changes.
1607 TEST_F(WidgetInputMethodInteractiveTest, TextField) {
1608 Widget* widget = CreateWidget();
1609 Textfield* textfield = new Textfield;
1610 widget->GetRootView()->AddChildView(textfield);
1611 ShowSync(widget);
1612 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1613 widget->GetInputMethod()->GetTextInputType());
1615 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1616 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1617 widget->GetInputMethod()->GetTextInputType());
1619 textfield->RequestFocus();
1620 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1621 widget->GetInputMethod()->GetTextInputType());
1623 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
1624 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1625 widget->GetInputMethod()->GetTextInputType());
1627 textfield->SetReadOnly(true);
1628 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1629 widget->GetInputMethod()->GetTextInputType());
1630 widget->CloseNow();
1633 } // namespace test
1634 } // namespace views