base/threading: remove ScopedTracker placed for experiments
[chromium-blink-merge.git] / ui / views / widget / widget_interactive_uitest.cc
blobe1e0e49a0719ed3c28713410e4c504017be1d567
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/test/gl_surface_test_support.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/test/focus_manager_test.h"
26 #include "ui/views/test/widget_test.h"
27 #include "ui/views/touchui/touch_selection_controller_impl.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/views/window/dialog_delegate.h"
30 #include "ui/wm/public/activation_client.h"
32 #if defined(OS_WIN)
33 #include "ui/aura/window.h"
34 #include "ui/aura/window_tree_host.h"
35 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
36 #include "ui/views/win/hwnd_util.h"
37 #endif
39 namespace views {
40 namespace test {
42 namespace {
44 // A View that closes the Widget and exits the current message-loop when it
45 // receives a mouse-release event.
46 class ExitLoopOnRelease : public View {
47 public:
48 ExitLoopOnRelease() {}
49 ~ExitLoopOnRelease() override {}
51 private:
52 // Overridden from View:
53 void OnMouseReleased(const ui::MouseEvent& event) override {
54 GetWidget()->Close();
55 base::MessageLoop::current()->QuitNow();
58 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
61 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
62 class GestureCaptureView : public View {
63 public:
64 GestureCaptureView() {}
65 ~GestureCaptureView() override {}
67 private:
68 // Overridden from View:
69 void OnGestureEvent(ui::GestureEvent* event) override {
70 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
71 GetWidget()->SetCapture(this);
72 event->StopPropagation();
76 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
79 // A view that always processes all mouse events.
80 class MouseView : public View {
81 public:
82 MouseView()
83 : View(),
84 entered_(0),
85 exited_(0),
86 pressed_(0) {
88 ~MouseView() override {}
90 bool OnMousePressed(const ui::MouseEvent& event) override {
91 pressed_++;
92 return true;
95 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
97 void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
99 // Return the number of OnMouseEntered calls and reset the counter.
100 int EnteredCalls() {
101 int i = entered_;
102 entered_ = 0;
103 return i;
106 // Return the number of OnMouseExited calls and reset the counter.
107 int ExitedCalls() {
108 int i = exited_;
109 exited_ = 0;
110 return i;
113 int pressed() const { return pressed_; }
115 private:
116 int entered_;
117 int exited_;
119 int pressed_;
121 DISALLOW_COPY_AND_ASSIGN(MouseView);
124 // A View that shows a different widget, sets capture on that widget, and
125 // initiates a nested message-loop when it receives a mouse-press event.
126 class NestedLoopCaptureView : public View {
127 public:
128 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
129 ~NestedLoopCaptureView() override {}
131 private:
132 // Overridden from View:
133 bool OnMousePressed(const ui::MouseEvent& event) override {
134 // Start a nested loop.
135 widget_->Show();
136 widget_->SetCapture(widget_->GetContentsView());
137 EXPECT_TRUE(widget_->HasCapture());
139 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
140 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
142 base::RunLoop run_loop;
143 run_loop.Run();
144 return true;
147 Widget* widget_;
149 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
152 // Spins a run loop until a Widget's active state matches a desired state.
153 class WidgetActivationWaiter : public WidgetObserver {
154 public:
155 WidgetActivationWaiter(Widget* widget, bool active) : observed_(false) {
156 #if defined(OS_WIN)
157 // On Windows, a HWND can receive a WM_ACTIVATE message without the value
158 // of ::GetActiveWindow() updating to reflect that change. This can cause
159 // the active window reported by IsActive() to get out of sync. Usually this
160 // happens after a call to HWNDMessageHandler::Deactivate() which works by
161 // activating some other window, which might be in another application.
162 // Doing this can trigger the native OS activation-blocker, causing the
163 // taskbar icon to flash instead. But since activation of native widgets on
164 // Windows is synchronous, we never have to wait anyway, so it's safe to
165 // return here.
166 if (active == widget->IsActive()) {
167 observed_ = true;
168 return;
170 #endif
171 // Always expect a change for tests using this.
172 EXPECT_NE(active, widget->IsActive());
173 widget->AddObserver(this);
176 void Wait() {
177 if (!observed_)
178 run_loop_.Run();
181 void OnWidgetActivationChanged(Widget* widget, bool active) override {
182 observed_ = true;
183 widget->RemoveObserver(this);
184 if (run_loop_.running())
185 run_loop_.Quit();
188 private:
189 base::RunLoop run_loop_;
190 bool observed_;
192 DISALLOW_COPY_AND_ASSIGN(WidgetActivationWaiter);
195 ui::WindowShowState GetWidgetShowState(const Widget* widget) {
196 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
197 // because the former is implemented on all platforms but the latter is not.
198 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN :
199 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED :
200 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED :
201 widget->IsActive() ? ui::SHOW_STATE_NORMAL :
202 ui::SHOW_STATE_INACTIVE;
205 // Give the OS an opportunity to process messages for an activation change, when
206 // there is actually no change expected (e.g. ShowInactive()).
207 void RunPendingMessagesForActiveStatusChange() {
208 #if defined(OS_MACOSX)
209 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
210 // and made active in two steps, so tests should follow up with a ShowSync()
211 // or ActivateSync to ensure a consistent state.
212 base::RunLoop().RunUntilIdle();
213 #endif
214 // TODO(tapted): Check for desktop aura widgets.
217 // Activate a widget, and wait for it to become active. On non-desktop Aura
218 // this is just an activation. For other widgets, it means activating and then
219 // spinning the run loop until the OS has activated the window.
220 void ActivateSync(Widget* widget) {
221 WidgetActivationWaiter waiter(widget, true);
222 widget->Activate();
223 waiter.Wait();
226 // Like for ActivateSync(), wait for a widget to become active, but Show() the
227 // widget rather than calling Activate().
228 void ShowSync(Widget* widget) {
229 WidgetActivationWaiter waiter(widget, true);
230 widget->Show();
231 waiter.Wait();
234 void DeactivateSync(Widget* widget) {
235 #if defined(OS_MACOSX)
236 // Deactivation of a window isn't a concept on Mac: If an application is
237 // active and it has any activatable windows, then one of them is always
238 // active. But we can simulate deactivation (e.g. as if another application
239 // became active) by temporarily making |widget| non-activatable, then
240 // activating (and closing) a temporary widget.
241 widget->widget_delegate()->set_can_activate(false);
242 Widget* stealer = new Widget;
243 stealer->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW));
244 ShowSync(stealer);
245 stealer->CloseNow();
246 widget->widget_delegate()->set_can_activate(true);
247 #else
248 WidgetActivationWaiter waiter(widget, false);
249 widget->Deactivate();
250 waiter.Wait();
251 #endif
254 #if defined(OS_WIN)
255 void ActivatePlatformWindow(Widget* widget) {
256 ::SetActiveWindow(
257 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
259 #endif
261 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
262 // the OS a chance to activate a widget. However, for this case, the test
263 // doesn't expect that to happen, so there is nothing to wait for.
264 void ShowInactiveSync(Widget* widget) {
265 widget->ShowInactive();
266 RunPendingMessagesForActiveStatusChange();
269 } // namespace
271 class WidgetTestInteractive : public WidgetTest {
272 public:
273 WidgetTestInteractive() {}
274 ~WidgetTestInteractive() override {}
276 void SetUp() override {
277 gfx::GLSurfaceTestSupport::InitializeOneOff();
278 ui::RegisterPathProvider();
279 base::FilePath ui_test_pak_path;
280 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
281 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
282 WidgetTest::SetUp();
285 protected:
286 #if defined(USE_AURA)
287 static void ShowQuickMenuImmediately(
288 TouchSelectionControllerImpl* controller) {
289 DCHECK(controller);
290 if (controller->quick_menu_timer_.IsRunning()) {
291 controller->quick_menu_timer_.Stop();
292 controller->QuickMenuTimerFired();
295 #endif // defined (USE_AURA)
297 Widget* CreateWidget() {
298 Widget* widget = CreateNativeDesktopWidget();
299 widget->SetBounds(gfx::Rect(0, 0, 200, 200));
300 return widget;
304 #if defined(OS_WIN)
305 // Tests whether activation and focus change works correctly in Windows.
306 // We test the following:-
307 // 1. If the active aura window is correctly set when a top level widget is
308 // created.
309 // 2. If the active aura window in widget 1 created above, is set to NULL when
310 // another top level widget is created and focused.
311 // 3. On focusing the native platform window for widget 1, the active aura
312 // window for widget 1 should be set and that for widget 2 should reset.
313 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
314 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
315 // Create widget 1 and expect the active window to be its window.
316 View* focusable_view1 = new View;
317 focusable_view1->SetFocusable(true);
318 Widget* widget1 = CreateWidget();
319 widget1->GetContentsView()->AddChildView(focusable_view1);
320 widget1->Show();
321 aura::Window* root_window1 = widget1->GetNativeView()->GetRootWindow();
322 focusable_view1->RequestFocus();
324 EXPECT_TRUE(root_window1 != NULL);
325 aura::client::ActivationClient* activation_client1 =
326 aura::client::GetActivationClient(root_window1);
327 EXPECT_TRUE(activation_client1 != NULL);
328 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
330 // Create widget 2 and expect the active window to be its window.
331 View* focusable_view2 = new View;
332 Widget* widget2 = CreateWidget();
333 widget1->GetContentsView()->AddChildView(focusable_view2);
334 widget2->Show();
335 aura::Window* root_window2 = widget2->GetNativeView()->GetRootWindow();
336 focusable_view2->RequestFocus();
337 ActivatePlatformWindow(widget2);
339 aura::client::ActivationClient* activation_client2 =
340 aura::client::GetActivationClient(root_window2);
341 EXPECT_TRUE(activation_client2 != NULL);
342 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2->GetNativeView());
343 EXPECT_EQ(activation_client1->GetActiveWindow(),
344 reinterpret_cast<aura::Window*>(NULL));
346 // Now set focus back to widget 1 and expect the active window to be its
347 // window.
348 focusable_view1->RequestFocus();
349 ActivatePlatformWindow(widget1);
350 EXPECT_EQ(activation_client2->GetActiveWindow(),
351 reinterpret_cast<aura::Window*>(NULL));
352 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
354 widget2->CloseNow();
355 widget1->CloseNow();
357 #endif // defined(OS_WIN)
359 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
360 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
361 View* container = new View;
362 toplevel->SetContentsView(container);
364 EXPECT_FALSE(toplevel->HasCapture());
365 toplevel->SetCapture(NULL);
366 EXPECT_TRUE(toplevel->HasCapture());
368 // By default, mouse release removes capture.
369 gfx::Point click_location(45, 15);
370 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
371 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
372 ui::EF_LEFT_MOUSE_BUTTON);
373 toplevel->OnMouseEvent(&release);
374 EXPECT_FALSE(toplevel->HasCapture());
376 // Now a mouse release shouldn't remove capture.
377 toplevel->set_auto_release_capture(false);
378 toplevel->SetCapture(NULL);
379 EXPECT_TRUE(toplevel->HasCapture());
380 toplevel->OnMouseEvent(&release);
381 EXPECT_TRUE(toplevel->HasCapture());
382 toplevel->ReleaseCapture();
383 EXPECT_FALSE(toplevel->HasCapture());
385 toplevel->Close();
386 RunPendingMessages();
389 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
390 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
391 View* container = new View;
392 toplevel->SetContentsView(container);
394 View* gesture = new GestureCaptureView;
395 gesture->SetBounds(0, 0, 30, 30);
396 container->AddChildView(gesture);
398 MouseView* mouse = new MouseView;
399 mouse->SetBounds(30, 0, 30, 30);
400 container->AddChildView(mouse);
402 toplevel->SetSize(gfx::Size(100, 100));
403 toplevel->Show();
405 // Start a gesture on |gesture|.
406 ui::GestureEvent tap_down(15,
409 base::TimeDelta(),
410 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
411 ui::GestureEvent end(15,
414 base::TimeDelta(),
415 ui::GestureEventDetails(ui::ET_GESTURE_END));
416 toplevel->OnGestureEvent(&tap_down);
418 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
419 // will not receive the event.
420 gfx::Point click_location(45, 15);
422 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
423 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
424 ui::EF_LEFT_MOUSE_BUTTON);
425 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
426 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
427 ui::EF_LEFT_MOUSE_BUTTON);
429 EXPECT_TRUE(toplevel->HasCapture());
431 toplevel->OnMouseEvent(&press);
432 toplevel->OnMouseEvent(&release);
433 EXPECT_EQ(0, mouse->pressed());
435 EXPECT_FALSE(toplevel->HasCapture());
437 // The end of the gesture should release the capture, and pressing on |mouse|
438 // should now reach |mouse|.
439 toplevel->OnGestureEvent(&end);
440 toplevel->OnMouseEvent(&press);
441 toplevel->OnMouseEvent(&release);
442 EXPECT_EQ(1, mouse->pressed());
444 toplevel->Close();
445 RunPendingMessages();
448 // Checks that if a mouse-press triggers a capture on a different widget (which
449 // consumes the mouse-release event), then the target of the press does not have
450 // capture.
451 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
452 // The test creates two widgets: |first| and |second|.
453 // The View in |first| makes |second| visible, sets capture on it, and starts
454 // a nested loop (like a menu does). The View in |second| terminates the
455 // nested loop and closes the widget.
456 // The test sends a mouse-press event to |first|, and posts a task to send a
457 // release event to |second|, to make sure that the release event is
458 // dispatched after the nested loop starts.
460 Widget* first = CreateTopLevelFramelessPlatformWidget();
461 Widget* second = CreateTopLevelFramelessPlatformWidget();
463 View* container = new NestedLoopCaptureView(second);
464 first->SetContentsView(container);
466 second->SetContentsView(new ExitLoopOnRelease());
468 first->SetSize(gfx::Size(100, 100));
469 first->Show();
471 gfx::Point location(20, 20);
472 base::MessageLoop::current()->PostTask(
473 FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second),
474 base::Owned(new ui::MouseEvent(
475 ui::ET_MOUSE_RELEASED, location, location,
476 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
477 ui::EF_LEFT_MOUSE_BUTTON))));
478 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
479 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
480 ui::EF_LEFT_MOUSE_BUTTON);
481 first->OnMouseEvent(&press);
482 EXPECT_FALSE(first->HasCapture());
483 first->Close();
484 RunPendingMessages();
487 // Tests some grab/ungrab events.
488 // TODO(estade): can this be enabled now that this is an interactive ui test?
489 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
490 Widget* toplevel = CreateTopLevelPlatformWidget();
491 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
492 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
494 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
496 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
497 View* view = new MouseView();
498 view->SetBounds(0, 0, 300, 300);
499 child1->GetRootView()->AddChildView(view);
501 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
502 view = new MouseView();
503 view->SetBounds(0, 0, 200, 200);
504 child2->GetRootView()->AddChildView(view);
506 toplevel->Show();
507 RunPendingMessages();
509 // Click on child1
510 gfx::Point p1(45, 45);
511 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
512 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
513 toplevel->OnMouseEvent(&pressed);
515 EXPECT_TRUE(toplevel->HasCapture());
516 EXPECT_TRUE(child1->HasCapture());
517 EXPECT_FALSE(child2->HasCapture());
519 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(),
520 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
521 toplevel->OnMouseEvent(&released);
523 EXPECT_FALSE(toplevel->HasCapture());
524 EXPECT_FALSE(child1->HasCapture());
525 EXPECT_FALSE(child2->HasCapture());
527 RunPendingMessages();
529 // Click on child2
530 gfx::Point p2(315, 45);
531 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(),
532 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
533 toplevel->OnMouseEvent(&pressed2);
534 EXPECT_TRUE(pressed2.handled());
535 EXPECT_TRUE(toplevel->HasCapture());
536 EXPECT_TRUE(child2->HasCapture());
537 EXPECT_FALSE(child1->HasCapture());
539 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(),
540 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
541 toplevel->OnMouseEvent(&released2);
542 EXPECT_FALSE(toplevel->HasCapture());
543 EXPECT_FALSE(child1->HasCapture());
544 EXPECT_FALSE(child2->HasCapture());
546 toplevel->CloseNow();
549 // Tests mouse move outside of the window into the "resize controller" and back
550 // will still generate an OnMouseEntered and OnMouseExited event..
551 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
552 Widget* toplevel = CreateTopLevelPlatformWidget();
554 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
556 MouseView* view = new MouseView();
557 view->SetBounds(90, 90, 10, 10);
558 toplevel->GetRootView()->AddChildView(view);
560 toplevel->Show();
561 RunPendingMessages();
563 // Move to an outside position.
564 gfx::Point p1(200, 200);
565 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
566 ui::EF_NONE, ui::EF_NONE);
567 toplevel->OnMouseEvent(&moved_out);
568 EXPECT_EQ(0, view->EnteredCalls());
569 EXPECT_EQ(0, view->ExitedCalls());
571 // Move onto the active view.
572 gfx::Point p2(95, 95);
573 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
574 ui::EF_NONE, ui::EF_NONE);
575 toplevel->OnMouseEvent(&moved_over);
576 EXPECT_EQ(1, view->EnteredCalls());
577 EXPECT_EQ(0, view->ExitedCalls());
579 // Move onto the outer resizing border.
580 gfx::Point p3(102, 95);
581 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
582 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
583 toplevel->OnMouseEvent(&moved_resizer);
584 EXPECT_EQ(0, view->EnteredCalls());
585 EXPECT_EQ(1, view->ExitedCalls());
587 // Move onto the view again.
588 toplevel->OnMouseEvent(&moved_over);
589 EXPECT_EQ(1, view->EnteredCalls());
590 EXPECT_EQ(0, view->ExitedCalls());
592 RunPendingMessages();
594 toplevel->CloseNow();
597 // Test view focus restoration when a widget is deactivated and re-activated.
598 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
599 Widget* widget1 = CreateTopLevelPlatformWidget();
600 View* view1 = new View;
601 view1->SetFocusable(true);
602 widget1->GetContentsView()->AddChildView(view1);
604 Widget* widget2 = CreateTopLevelPlatformWidget();
605 View* view2a = new View;
606 View* view2b = new View;
607 view2a->SetFocusable(true);
608 view2b->SetFocusable(true);
609 widget2->GetContentsView()->AddChildView(view2a);
610 widget2->GetContentsView()->AddChildView(view2b);
612 ShowSync(widget1);
613 EXPECT_TRUE(widget1->IsActive());
614 view1->RequestFocus();
615 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
617 ShowSync(widget2);
618 EXPECT_TRUE(widget2->IsActive());
619 EXPECT_FALSE(widget1->IsActive());
620 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
621 view2a->RequestFocus();
622 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
623 view2b->RequestFocus();
624 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
626 ActivateSync(widget1);
627 EXPECT_TRUE(widget1->IsActive());
628 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
629 EXPECT_FALSE(widget2->IsActive());
630 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
632 ActivateSync(widget2);
633 EXPECT_TRUE(widget2->IsActive());
634 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
635 EXPECT_FALSE(widget1->IsActive());
636 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
638 widget1->CloseNow();
639 widget2->CloseNow();
642 #if defined(OS_WIN)
644 // Test view focus retention when a widget's HWND is disabled and re-enabled.
645 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
646 Widget* widget = CreateTopLevelFramelessPlatformWidget();
647 widget->SetContentsView(new View);
648 for (size_t i = 0; i < 2; ++i) {
649 widget->GetContentsView()->AddChildView(new View);
650 widget->GetContentsView()->child_at(i)->SetFocusable(true);
653 widget->Show();
654 const HWND hwnd = HWNDForWidget(widget);
655 EXPECT_TRUE(::IsWindow(hwnd));
656 EXPECT_TRUE(::IsWindowEnabled(hwnd));
657 EXPECT_EQ(hwnd, ::GetActiveWindow());
659 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
660 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
661 View* view = widget->GetContentsView()->child_at(i);
663 view->RequestFocus();
664 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
665 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
666 EXPECT_FALSE(::IsWindowEnabled(hwnd));
668 // Oddly, disabling the HWND leaves it active with the focus unchanged.
669 EXPECT_EQ(hwnd, ::GetActiveWindow());
670 EXPECT_TRUE(widget->IsActive());
671 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
673 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
674 EXPECT_TRUE(::IsWindowEnabled(hwnd));
675 EXPECT_EQ(hwnd, ::GetActiveWindow());
676 EXPECT_TRUE(widget->IsActive());
677 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
680 widget->CloseNow();
683 // This class subclasses the Widget class to listen for activation change
684 // notifications and provides accessors to return information as to whether
685 // the widget is active. We need this to ensure that users of the widget
686 // class activate the widget only when the underlying window becomes really
687 // active. Previously we would activate the widget in the WM_NCACTIVATE
688 // message which is incorrect because APIs like FlashWindowEx flash the
689 // window caption by sending fake WM_NCACTIVATE messages.
690 class WidgetActivationTest : public Widget {
691 public:
692 WidgetActivationTest()
693 : active_(false) {}
695 ~WidgetActivationTest() override {}
697 void OnNativeWidgetActivationChanged(bool active) override {
698 active_ = active;
701 bool active() const { return active_; }
703 private:
704 bool active_;
706 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
709 // Tests whether the widget only becomes active when the underlying window
710 // is really active.
711 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
712 WidgetActivationTest widget1;
713 Widget::InitParams init_params =
714 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
715 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
716 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
717 init_params.bounds = gfx::Rect(0, 0, 200, 200);
718 widget1.Init(init_params);
719 widget1.Show();
720 EXPECT_EQ(true, widget1.active());
722 WidgetActivationTest widget2;
723 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
724 widget2.Init(init_params);
725 widget2.Show();
726 EXPECT_EQ(true, widget2.active());
727 EXPECT_EQ(false, widget1.active());
729 HWND win32_native_window1 = HWNDForWidget(&widget1);
730 EXPECT_TRUE(::IsWindow(win32_native_window1));
732 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
733 EXPECT_EQ(false, widget1.active());
734 EXPECT_EQ(true, widget2.active());
736 ::SetActiveWindow(win32_native_window1);
737 EXPECT_EQ(true, widget1.active());
738 EXPECT_EQ(false, widget2.active());
740 #endif // defined(OS_WIN)
742 #if !defined(OS_CHROMEOS)
743 // Provides functionality to create a window modal dialog.
744 class ModalDialogDelegate : public DialogDelegateView {
745 public:
746 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
747 ~ModalDialogDelegate() override {}
749 // WidgetDelegate overrides.
750 ui::ModalType GetModalType() const override { return type_; }
752 private:
753 ui::ModalType type_;
755 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
758 // Tests whether the focused window is set correctly when a modal window is
759 // created and destroyed. When it is destroyed it should focus the owner window.
760 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
761 TestWidgetFocusChangeListener focus_listener;
762 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
763 const std::vector<gfx::NativeView>& focus_changes =
764 focus_listener.focus_changes();
766 // Create a top level widget.
767 Widget top_level_widget;
768 Widget::InitParams init_params =
769 CreateParams(Widget::InitParams::TYPE_WINDOW);
770 init_params.show_state = ui::SHOW_STATE_NORMAL;
771 gfx::Rect initial_bounds(0, 0, 500, 500);
772 init_params.bounds = initial_bounds;
773 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
774 init_params.native_widget =
775 new PlatformDesktopNativeWidget(&top_level_widget);
776 top_level_widget.Init(init_params);
777 ShowSync(&top_level_widget);
779 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
780 ASSERT_FALSE(focus_listener.focus_changes().empty());
781 EXPECT_EQ(1u, focus_changes.size());
782 EXPECT_EQ(top_level_native_view, focus_changes[0]);
784 // Create a modal dialog.
785 // This instance will be destroyed when the dialog is destroyed.
786 ModalDialogDelegate* dialog_delegate =
787 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
789 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
790 dialog_delegate, NULL, top_level_widget.GetNativeView());
791 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
793 // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
794 // active status synchronously, even on Mac.
795 modal_dialog_widget->Show();
797 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
798 EXPECT_EQ(3u, focus_changes.size());
799 EXPECT_EQ(nullptr, focus_changes[1]);
800 EXPECT_EQ(modal_native_view, focus_changes[2]);
802 #if defined(OS_MACOSX)
803 // Window modal dialogs on Mac are "sheets", which animate to close before
804 // activating their parent widget.
805 WidgetActivationWaiter waiter(&top_level_widget, true);
806 modal_dialog_widget->Close();
807 waiter.Wait();
808 #else
809 modal_dialog_widget->CloseNow();
810 #endif
812 EXPECT_EQ(5u, focus_changes.size());
813 EXPECT_EQ(nullptr, focus_changes[3]);
814 EXPECT_EQ(top_level_native_view, focus_changes[4]);
816 top_level_widget.CloseNow();
817 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
820 // Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
821 // was deprecated. It does have application modal windows, but only Ash requests
822 // those.
823 #if defined(OS_MACOSX) && !defined(USE_AURA)
824 #define MAYBE_SystemModalWindowReleasesCapture \
825 DISABLED_SystemModalWindowReleasesCapture
826 #else
827 #define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
828 #endif
830 // Test that when opening a system-modal window, capture is released.
831 TEST_F(WidgetTestInteractive, MAYBE_SystemModalWindowReleasesCapture) {
832 TestWidgetFocusChangeListener focus_listener;
833 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
835 // Create a top level widget.
836 Widget top_level_widget;
837 Widget::InitParams init_params =
838 CreateParams(Widget::InitParams::TYPE_WINDOW);
839 init_params.show_state = ui::SHOW_STATE_NORMAL;
840 gfx::Rect initial_bounds(0, 0, 500, 500);
841 init_params.bounds = initial_bounds;
842 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
843 init_params.native_widget =
844 new PlatformDesktopNativeWidget(&top_level_widget);
845 top_level_widget.Init(init_params);
846 ShowSync(&top_level_widget);
848 ASSERT_FALSE(focus_listener.focus_changes().empty());
849 EXPECT_EQ(top_level_widget.GetNativeView(),
850 focus_listener.focus_changes().back());;
852 EXPECT_FALSE(top_level_widget.HasCapture());
853 top_level_widget.SetCapture(NULL);
854 EXPECT_TRUE(top_level_widget.HasCapture());
856 // Create a modal dialog.
857 ModalDialogDelegate* dialog_delegate =
858 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
860 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
861 dialog_delegate, NULL, top_level_widget.GetNativeView());
862 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
863 ShowSync(modal_dialog_widget);
865 EXPECT_FALSE(top_level_widget.HasCapture());
867 modal_dialog_widget->CloseNow();
868 top_level_widget.CloseNow();
869 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
872 #endif // !defined(OS_CHROMEOS)
874 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
875 Widget widget;
876 Widget::InitParams init_params =
877 CreateParams(Widget::InitParams::TYPE_WINDOW);
878 init_params.bounds = gfx::Rect(0, 0, 200, 200);
879 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
880 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
881 #if !defined(OS_CHROMEOS)
882 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
883 #endif // !defined(OS_CHROMEOS)
884 widget.Init(init_params);
886 widget.Show();
887 EXPECT_FALSE(widget.IsActive());
890 #if defined(USE_AURA)
891 // Test that touch selection quick menu is not activated when opened.
892 TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
893 base::CommandLine::ForCurrentProcess()->AppendSwitch(
894 switches::kEnableTouchEditing);
895 #if defined(OS_WIN)
896 views_delegate()->set_use_desktop_native_widgets(true);
897 #endif // !defined(OS_WIN)
899 Widget* widget = CreateWidget();
901 Textfield* textfield = new Textfield;
902 textfield->SetBounds(0, 0, 200, 20);
903 textfield->SetText(base::ASCIIToUTF16("some text"));
904 widget->GetRootView()->AddChildView(textfield);
906 widget->Show();
907 textfield->RequestFocus();
908 textfield->SelectAll(true);
909 TextfieldTestApi textfield_test_api(textfield);
911 RunPendingMessages();
913 ui::test::EventGenerator generator(widget->GetNativeWindow());
914 generator.GestureTapAt(gfx::Point(10, 10));
915 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
916 textfield_test_api.touch_selection_controller()));
918 EXPECT_TRUE(textfield->HasFocus());
919 EXPECT_TRUE(widget->IsActive());
920 EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
921 widget->CloseNow();
923 #endif // defined(USE_AURA)
925 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
926 #if defined(OS_WIN)
927 views_delegate()->set_use_desktop_native_widgets(true);
928 #endif // !defined(OS_WIN)
930 // Create first widget and view, activate the widget, and focus the view.
931 Widget widget1;
932 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
933 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
934 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
935 widget1.Init(params1);
937 View* view1 = new View();
938 view1->SetFocusable(true);
939 widget1.GetRootView()->AddChildView(view1);
941 ActivateSync(&widget1);
943 FocusManager* focus_manager1 = widget1.GetFocusManager();
944 ASSERT_TRUE(focus_manager1);
945 focus_manager1->SetFocusedView(view1);
946 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
948 // Create second widget and view, activate the widget, and focus the view.
949 Widget widget2;
950 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
951 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
952 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
953 widget2.Init(params2);
955 View* view2 = new View();
956 view2->SetFocusable(true);
957 widget2.GetRootView()->AddChildView(view2);
959 ActivateSync(&widget2);
960 EXPECT_TRUE(widget2.IsActive());
961 EXPECT_FALSE(widget1.IsActive());
963 FocusManager* focus_manager2 = widget2.GetFocusManager();
964 ASSERT_TRUE(focus_manager2);
965 focus_manager2->SetFocusedView(view2);
966 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
968 // Disable the first view and make sure it loses focus, but its widget is not
969 // activated.
970 view1->SetEnabled(false);
971 EXPECT_NE(view1, focus_manager1->GetFocusedView());
972 EXPECT_FALSE(widget1.IsActive());
973 EXPECT_TRUE(widget2.IsActive());
976 TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
977 Widget* widget = CreateTopLevelPlatformWidget();
979 ShowSync(widget);
980 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
982 widget->CloseNow();
985 TEST_F(WidgetTestInteractive, ShowInactive) {
986 Widget* widget = CreateTopLevelPlatformWidget();
988 ShowInactiveSync(widget);
989 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE);
991 widget->CloseNow();
994 TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
995 Widget* widget = CreateTopLevelPlatformWidget();
997 EXPECT_FALSE(widget->IsActive());
998 EXPECT_FALSE(widget->IsVisible());
1000 ShowSync(widget);
1002 EXPECT_TRUE(widget->IsActive());
1003 EXPECT_TRUE(widget->IsVisible());
1005 widget->CloseNow();
1008 TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
1009 // Create 2 widgets to ensure window layering does not change.
1010 Widget* widget = CreateTopLevelPlatformWidget();
1011 Widget* widget2 = CreateTopLevelPlatformWidget();
1013 ShowSync(widget2);
1014 EXPECT_FALSE(widget->IsActive());
1015 EXPECT_TRUE(widget2->IsVisible());
1016 EXPECT_TRUE(widget2->IsActive());
1018 ShowSync(widget);
1019 EXPECT_TRUE(widget->IsActive());
1020 EXPECT_FALSE(widget2->IsActive());
1022 ShowInactiveSync(widget);
1023 EXPECT_TRUE(widget->IsActive());
1024 EXPECT_FALSE(widget2->IsActive());
1025 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1027 widget2->CloseNow();
1028 widget->CloseNow();
1031 TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
1032 Widget* widget = CreateTopLevelPlatformWidget();
1033 widget->SetBounds(gfx::Rect(100, 100, 100, 100));
1035 ShowInactiveSync(widget);
1036 ShowSync(widget);
1037 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1039 widget->CloseNow();
1042 #if !defined(OS_CHROMEOS)
1043 TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
1044 Widget* widget = CreateTopLevelPlatformWidget();
1045 ShowSync(widget);
1046 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1048 Widget widget2;
1049 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
1050 params.native_widget = new PlatformDesktopNativeWidget(&widget2);
1051 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1052 widget2.Init(params);
1053 widget2.Show();
1054 RunPendingMessagesForActiveStatusChange();
1056 EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE);
1057 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1059 widget->CloseNow();
1060 widget2.CloseNow();
1062 #endif // !defined(OS_CHROMEOS)
1064 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1065 // currently only Desktop widgets and fullscreen changes have to coordinate with
1066 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1067 // Maximize on mac is also (intentionally) a no-op.
1068 #if defined(OS_MACOSX) && !defined(USE_AURA)
1069 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1070 #else
1071 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1072 #endif
1074 // Test that window state is not changed after getting out of full screen.
1075 TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
1076 Widget* toplevel = CreateTopLevelPlatformWidget();
1078 toplevel->Show();
1079 RunPendingMessages();
1081 // This should be a normal state window.
1082 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1084 toplevel->SetFullscreen(true);
1085 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1086 toplevel->SetFullscreen(false);
1087 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1089 // And it should still be in normal state after getting out of full screen.
1090 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1092 // Now, make it maximized.
1093 toplevel->Maximize();
1094 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1096 toplevel->SetFullscreen(true);
1097 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1098 toplevel->SetFullscreen(false);
1099 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1101 // And it stays maximized after getting out of full screen.
1102 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1104 // Clean up.
1105 toplevel->Close();
1106 RunPendingMessages();
1109 namespace {
1111 // Used to veirfy OnMouseCaptureLost() has been invoked.
1112 class CaptureLostTrackingWidget : public Widget {
1113 public:
1114 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
1115 ~CaptureLostTrackingWidget() override {}
1117 bool GetAndClearGotCaptureLost() {
1118 bool value = got_capture_lost_;
1119 got_capture_lost_ = false;
1120 return value;
1123 // Widget:
1124 void OnMouseCaptureLost() override {
1125 got_capture_lost_ = true;
1126 Widget::OnMouseCaptureLost();
1129 private:
1130 bool got_capture_lost_;
1132 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
1135 } // namespace
1137 class WidgetCaptureTest : public ViewsTestBase {
1138 public:
1139 WidgetCaptureTest() {
1142 ~WidgetCaptureTest() override {}
1144 void SetUp() override {
1145 gfx::GLSurfaceTestSupport::InitializeOneOff();
1146 ui::RegisterPathProvider();
1147 base::FilePath ui_test_pak_path;
1148 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
1149 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
1150 ViewsTestBase::SetUp();
1153 // Verifies Widget::SetCapture() results in updating native capture along with
1154 // invoking the right Widget function.
1155 void TestCapture(bool use_desktop_native_widget) {
1156 CaptureLostTrackingWidget widget1;
1157 Widget::InitParams params1 =
1158 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1159 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
1160 &widget1);
1161 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1162 widget1.Init(params1);
1163 widget1.Show();
1165 CaptureLostTrackingWidget widget2;
1166 Widget::InitParams params2 =
1167 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1168 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1169 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
1170 &widget2);
1171 widget2.Init(params2);
1172 widget2.Show();
1174 // Set capture to widget2 and verity it gets it.
1175 widget2.SetCapture(widget2.GetRootView());
1176 EXPECT_FALSE(widget1.HasCapture());
1177 EXPECT_TRUE(widget2.HasCapture());
1178 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1179 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1181 // Set capture to widget1 and verify it gets it.
1182 widget1.SetCapture(widget1.GetRootView());
1183 EXPECT_TRUE(widget1.HasCapture());
1184 EXPECT_FALSE(widget2.HasCapture());
1185 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1186 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
1188 // Release and verify no one has it.
1189 widget1.ReleaseCapture();
1190 EXPECT_FALSE(widget1.HasCapture());
1191 EXPECT_FALSE(widget2.HasCapture());
1192 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
1193 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1196 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
1197 Widget* widget) {
1198 #if !defined(OS_CHROMEOS)
1199 if (create_desktop_native_widget)
1200 return new PlatformDesktopNativeWidget(widget);
1201 #endif
1202 return NULL;
1205 private:
1206 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
1209 // See description in TestCapture().
1210 TEST_F(WidgetCaptureTest, Capture) {
1211 TestCapture(false);
1214 #if !defined(OS_CHROMEOS)
1215 // See description in TestCapture(). Creates DesktopNativeWidget.
1216 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
1217 TestCapture(true);
1219 #endif
1221 // Test that no state is set if capture fails.
1222 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
1223 Widget widget;
1224 Widget::InitParams params =
1225 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1226 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1227 params.bounds = gfx::Rect(400, 400);
1228 widget.Init(params);
1230 MouseView* mouse_view1 = new MouseView;
1231 MouseView* mouse_view2 = new MouseView;
1232 View* contents_view = new View;
1233 contents_view->AddChildView(mouse_view1);
1234 contents_view->AddChildView(mouse_view2);
1235 widget.SetContentsView(contents_view);
1237 mouse_view1->SetBounds(0, 0, 200, 400);
1238 mouse_view2->SetBounds(200, 0, 200, 400);
1240 // Setting capture should fail because |widget| is not visible.
1241 widget.SetCapture(mouse_view1);
1242 EXPECT_FALSE(widget.HasCapture());
1244 widget.Show();
1245 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
1246 generator.set_current_location(gfx::Point(300, 10));
1247 generator.PressLeftButton();
1249 EXPECT_FALSE(mouse_view1->pressed());
1250 EXPECT_TRUE(mouse_view2->pressed());
1253 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1254 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1255 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1256 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
1257 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1258 #else
1259 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1260 #endif
1262 // Test that a synthetic mouse exit is sent to the widget which was handling
1263 // mouse events when a different widget grabs capture.
1264 TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
1265 Widget widget1;
1266 Widget::InitParams params1 =
1267 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1268 params1.native_widget = CreateNativeWidget(true, &widget1);
1269 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1270 widget1.Init(params1);
1271 MouseView* mouse_view1 = new MouseView;
1272 widget1.SetContentsView(mouse_view1);
1273 widget1.Show();
1274 widget1.SetBounds(gfx::Rect(300, 300));
1276 Widget widget2;
1277 Widget::InitParams params2 =
1278 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1279 params2.native_widget = CreateNativeWidget(true, &widget2);
1280 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1281 widget2.Init(params2);
1282 widget2.Show();
1283 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1285 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1286 generator.set_current_location(gfx::Point(100, 100));
1287 generator.MoveMouseBy(0, 0);
1289 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1290 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1292 widget2.SetCapture(NULL);
1293 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1294 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1295 // in addition to the one generated by Chrome.
1296 EXPECT_LT(0, mouse_view1->ExitedCalls());
1299 namespace {
1301 // Widget observer which grabs capture when the widget is activated.
1302 class CaptureOnActivationObserver : public WidgetObserver {
1303 public:
1304 CaptureOnActivationObserver() : activation_observed_(false) {}
1305 ~CaptureOnActivationObserver() override {}
1307 // WidgetObserver:
1308 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1309 if (active) {
1310 widget->SetCapture(nullptr);
1311 activation_observed_ = true;
1315 bool activation_observed() const { return activation_observed_; }
1317 private:
1318 bool activation_observed_;
1320 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1323 } // namespace
1325 // Test that setting capture on widget activation of a non-toplevel widget
1326 // (e.g. a bubble on Linux) succeeds.
1327 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1328 Widget toplevel;
1329 Widget::InitParams toplevel_params =
1330 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1331 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel);
1332 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1333 toplevel.Init(toplevel_params);
1334 toplevel.Show();
1336 Widget* child = new Widget;
1337 Widget::InitParams child_params =
1338 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1339 child_params.parent = toplevel.GetNativeView();
1340 child_params.context = toplevel.GetNativeWindow();
1341 child->Init(child_params);
1343 CaptureOnActivationObserver observer;
1344 child->AddObserver(&observer);
1345 child->Show();
1347 #if defined(OS_MACOSX) && !defined(USE_AURA)
1348 // On Mac, activation is asynchronous. A single trip to the runloop should be
1349 // sufficient. On Aura platforms, note that since the child widget isn't top-
1350 // level, the aura window manager gets asked whether the widget is active, not
1351 // the OS.
1352 base::RunLoop().RunUntilIdle();
1353 #endif
1355 EXPECT_TRUE(observer.activation_observed());
1356 EXPECT_TRUE(child->HasCapture());
1360 #if defined(OS_WIN)
1361 namespace {
1363 // Used to verify OnMouseEvent() has been invoked.
1364 class MouseEventTrackingWidget : public Widget {
1365 public:
1366 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1367 ~MouseEventTrackingWidget() override {}
1369 bool GetAndClearGotMouseEvent() {
1370 bool value = got_mouse_event_;
1371 got_mouse_event_ = false;
1372 return value;
1375 // Widget:
1376 void OnMouseEvent(ui::MouseEvent* event) override {
1377 got_mouse_event_ = true;
1378 Widget::OnMouseEvent(event);
1381 private:
1382 bool got_mouse_event_;
1384 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1387 } // namespace
1389 // Verifies if a mouse event is received on a widget that doesn't have capture
1390 // on Windows that it is correctly processed by the widget that doesn't have
1391 // capture. This behavior is not desired on OSes other than Windows.
1392 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1393 MouseEventTrackingWidget widget1;
1394 Widget::InitParams params1 =
1395 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1396 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1397 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1398 widget1.Init(params1);
1399 widget1.Show();
1401 MouseEventTrackingWidget widget2;
1402 Widget::InitParams params2 =
1403 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1404 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1405 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1406 widget2.Init(params2);
1407 widget2.Show();
1409 // Set capture to widget2 and verity it gets it.
1410 widget2.SetCapture(widget2.GetRootView());
1411 EXPECT_FALSE(widget1.HasCapture());
1412 EXPECT_TRUE(widget2.HasCapture());
1414 widget1.GetAndClearGotMouseEvent();
1415 widget2.GetAndClearGotMouseEvent();
1416 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1417 // |widget2| has capture, |widget1| should still get the event.
1418 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1419 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
1420 ui::EventDispatchDetails details = widget1.GetNativeWindow()->
1421 GetHost()->event_processor()->OnEventFromSource(&mouse_event);
1422 ASSERT_FALSE(details.dispatcher_destroyed);
1423 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1424 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1426 #endif // defined(OS_WIN)
1428 class WidgetInputMethodInteractiveTest : public WidgetTestInteractive {
1429 public:
1430 WidgetInputMethodInteractiveTest() {}
1432 // testing::Test:
1433 void SetUp() override {
1434 WidgetTestInteractive::SetUp();
1435 #if defined(OS_WIN)
1436 // On Windows, Widget::Deactivate() works by activating the next topmost
1437 // window on the z-order stack. This only works if there is at least one
1438 // other window, so make sure that is the case.
1439 deactivate_widget_ = CreateWidget();
1440 deactivate_widget_->Show();
1441 #endif
1444 void TearDown() override {
1445 if (deactivate_widget_)
1446 deactivate_widget_->CloseNow();
1447 WidgetTestInteractive::TearDown();
1450 private:
1451 Widget* deactivate_widget_ = nullptr;
1453 DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest);
1456 // Test input method focus changes affected by top window activaction.
1457 TEST_F(WidgetInputMethodInteractiveTest, Activation) {
1458 Widget* widget = CreateWidget();
1459 Textfield* textfield = new Textfield;
1460 widget->GetRootView()->AddChildView(textfield);
1461 textfield->RequestFocus();
1463 ShowSync(widget);
1465 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1466 widget->GetInputMethod()->GetTextInputType());
1468 DeactivateSync(widget);
1470 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1471 widget->GetInputMethod()->GetTextInputType());
1472 widget->CloseNow();
1475 // Test input method focus changes affected by focus changes within 1 window.
1476 TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
1477 Widget* widget = CreateWidget();
1478 Textfield* textfield1 = new Textfield;
1479 Textfield* textfield2 = new Textfield;
1480 textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1481 widget->GetRootView()->AddChildView(textfield1);
1482 widget->GetRootView()->AddChildView(textfield2);
1484 ShowSync(widget);
1486 textfield1->RequestFocus();
1487 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1488 widget->GetInputMethod()->GetTextInputType());
1490 textfield2->RequestFocus();
1491 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1492 widget->GetInputMethod()->GetTextInputType());
1494 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1495 // DNWA (which just activates the last active window) and involves the
1496 // AuraTestHelper which sets the input method as DummyInputMethod.
1497 #if !defined(OS_CHROMEOS)
1498 DeactivateSync(widget);
1499 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1500 widget->GetInputMethod()->GetTextInputType());
1502 ActivateSync(widget);
1503 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1504 widget->GetInputMethod()->GetTextInputType());
1506 DeactivateSync(widget);
1507 textfield1->RequestFocus();
1508 ActivateSync(widget);
1509 EXPECT_TRUE(widget->IsActive());
1510 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1511 widget->GetInputMethod()->GetTextInputType());
1512 #endif
1513 widget->CloseNow();
1516 // Test input method focus changes affected by focus changes cross 2 windows
1517 // which shares the same top window.
1518 TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
1519 Widget* parent = CreateWidget();
1520 parent->SetBounds(gfx::Rect(100, 100, 100, 100));
1522 Widget* child = CreateChildNativeWidgetWithParent(parent);
1523 child->SetBounds(gfx::Rect(0, 0, 50, 50));
1524 child->Show();
1526 Textfield* textfield_parent = new Textfield;
1527 Textfield* textfield_child = new Textfield;
1528 textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1529 parent->GetRootView()->AddChildView(textfield_parent);
1530 child->GetRootView()->AddChildView(textfield_child);
1531 ShowSync(parent);
1533 EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod());
1535 textfield_parent->RequestFocus();
1536 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1537 parent->GetInputMethod()->GetTextInputType());
1539 textfield_child->RequestFocus();
1540 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1541 parent->GetInputMethod()->GetTextInputType());
1543 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1544 // DNWA (which just activates the last active window) and involves the
1545 // AuraTestHelper which sets the input method as DummyInputMethod.
1546 #if !defined(OS_CHROMEOS)
1547 DeactivateSync(parent);
1548 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1549 parent->GetInputMethod()->GetTextInputType());
1551 ActivateSync(parent);
1552 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1553 parent->GetInputMethod()->GetTextInputType());
1555 textfield_parent->RequestFocus();
1556 DeactivateSync(parent);
1557 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1558 parent->GetInputMethod()->GetTextInputType());
1560 ActivateSync(parent);
1561 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1562 parent->GetInputMethod()->GetTextInputType());
1563 #endif
1565 parent->CloseNow();
1568 // Test input method focus changes affected by textfield's state changes.
1569 TEST_F(WidgetInputMethodInteractiveTest, TextField) {
1570 Widget* widget = CreateWidget();
1571 Textfield* textfield = new Textfield;
1572 widget->GetRootView()->AddChildView(textfield);
1573 ShowSync(widget);
1574 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1575 widget->GetInputMethod()->GetTextInputType());
1577 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1578 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1579 widget->GetInputMethod()->GetTextInputType());
1581 textfield->RequestFocus();
1582 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1583 widget->GetInputMethod()->GetTextInputType());
1585 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
1586 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1587 widget->GetInputMethod()->GetTextInputType());
1589 textfield->SetReadOnly(true);
1590 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1591 widget->GetInputMethod()->GetTextInputType());
1592 widget->CloseNow();
1595 } // namespace test
1596 } // namespace views