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.
4
5 #include <stddef.h>
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/location.h"
14 #include "base/macros.h"
15 #include "base/run_loop.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "base/timer/timer.h"
21 #include "base/win/windows_version.h"
22 #include "build/build_config.h"
23 #include "ui/base/ime/input_method.h"
24 #include "ui/base/ime/text_input_client.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/base/test/ui_controls.h"
27 #include "ui/base/ui_base_features.h"
28 #include "ui/base/ui_base_switches.h"
29 #include "ui/events/event_processor.h"
30 #include "ui/events/event_utils.h"
31 #include "ui/events/test/event_generator.h"
32 #include "ui/gfx/native_widget_types.h"
33 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
34 #include "ui/views/controls/textfield/textfield.h"
35 #include "ui/views/controls/textfield/textfield_test_api.h"
36 #include "ui/views/focus/focus_manager.h"
37 #include "ui/views/test/focus_manager_test.h"
38 #include "ui/views/test/native_widget_factory.h"
39 #include "ui/views/test/widget_test.h"
40 #include "ui/views/touchui/touch_selection_controller_impl.h"
41 #include "ui/views/widget/root_view.h"
42 #include "ui/views/widget/widget.h"
43 #include "ui/views/widget/widget_utils.h"
44 #include "ui/views/window/dialog_delegate.h"
45 #include "ui/wm/public/activation_client.h"
46
47 #if defined(OS_WIN)
48 #include "ui/aura/window.h"
49 #include "ui/aura/window_tree_host.h"
50 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
51 #include "ui/views/win/hwnd_util.h"
52 #endif
53
54 namespace views {
55 namespace test {
56
57 namespace {
58
59 // A View that closes the Widget and exits the current message-loop when it
60 // receives a mouse-release event.
61 class ExitLoopOnRelease : public View {
62 public:
ExitLoopOnRelease(base::OnceClosure quit_closure)63 explicit ExitLoopOnRelease(base::OnceClosure quit_closure)
64 : quit_closure_(std::move(quit_closure)) {
65 DCHECK(quit_closure_);
66 }
67
68 ~ExitLoopOnRelease() override = default;
69
70 private:
71 // View:
OnMouseReleased(const ui::MouseEvent & event)72 void OnMouseReleased(const ui::MouseEvent& event) override {
73 GetWidget()->Close();
74 std::move(quit_closure_).Run();
75 }
76
77 base::OnceClosure quit_closure_;
78
79 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
80 };
81
82 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
83 class GestureCaptureView : public View {
84 public:
85 GestureCaptureView() = default;
86 ~GestureCaptureView() override = default;
87
88 private:
89 // View:
OnGestureEvent(ui::GestureEvent * event)90 void OnGestureEvent(ui::GestureEvent* event) override {
91 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
92 GetWidget()->SetCapture(this);
93 event->StopPropagation();
94 }
95 }
96
97 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
98 };
99
100 // A view that always processes all mouse events.
101 class MouseView : public View {
102 public:
103 MouseView() = default;
104 ~MouseView() override = default;
105
OnMousePressed(const ui::MouseEvent & event)106 bool OnMousePressed(const ui::MouseEvent& event) override {
107 pressed_++;
108 return true;
109 }
110
OnMouseEntered(const ui::MouseEvent & event)111 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
112
OnMouseExited(const ui::MouseEvent & event)113 void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
114
115 // Return the number of OnMouseEntered calls and reset the counter.
EnteredCalls()116 int EnteredCalls() {
117 int i = entered_;
118 entered_ = 0;
119 return i;
120 }
121
122 // Return the number of OnMouseExited calls and reset the counter.
ExitedCalls()123 int ExitedCalls() {
124 int i = exited_;
125 exited_ = 0;
126 return i;
127 }
128
pressed() const129 int pressed() const { return pressed_; }
130
131 private:
132 int entered_ = 0;
133 int exited_ = 0;
134
135 int pressed_ = 0;
136
137 DISALLOW_COPY_AND_ASSIGN(MouseView);
138 };
139
140 // A View that shows a different widget, sets capture on that widget, and
141 // initiates a nested message-loop when it receives a mouse-press event.
142 class NestedLoopCaptureView : public View {
143 public:
NestedLoopCaptureView(Widget * widget)144 explicit NestedLoopCaptureView(Widget* widget)
145 : run_loop_(base::RunLoop::Type::kNestableTasksAllowed),
146 widget_(widget) {}
147 ~NestedLoopCaptureView() override = default;
148
GetQuitClosure()149 base::OnceClosure GetQuitClosure() { return run_loop_.QuitClosure(); }
150
151 private:
152 // View:
OnMousePressed(const ui::MouseEvent & event)153 bool OnMousePressed(const ui::MouseEvent& event) override {
154 // Start a nested loop.
155 widget_->Show();
156 widget_->SetCapture(widget_->GetContentsView());
157 EXPECT_TRUE(widget_->HasCapture());
158
159 run_loop_.Run();
160 return true;
161 }
162
163 base::RunLoop run_loop_;
164
165 Widget* widget_;
166
167 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
168 };
169
GetWidgetShowState(const Widget * widget)170 ui::WindowShowState GetWidgetShowState(const Widget* widget) {
171 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
172 // because the former is implemented on all platforms but the latter is not.
173 if (widget->IsFullscreen())
174 return ui::SHOW_STATE_FULLSCREEN;
175 if (widget->IsMaximized())
176 return ui::SHOW_STATE_MAXIMIZED;
177 if (widget->IsMinimized())
178 return ui::SHOW_STATE_MINIMIZED;
179 return widget->IsActive() ? ui::SHOW_STATE_NORMAL : ui::SHOW_STATE_INACTIVE;
180 }
181
182 // Give the OS an opportunity to process messages for an activation change, when
183 // there is actually no change expected (e.g. ShowInactive()).
RunPendingMessagesForActiveStatusChange()184 void RunPendingMessagesForActiveStatusChange() {
185 #if defined(OS_APPLE)
186 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
187 // and made active in two steps, so tests should follow up with a ShowSync()
188 // or ActivateSync to ensure a consistent state.
189 base::RunLoop().RunUntilIdle();
190 #endif
191 // TODO(tapted): Check for desktop aura widgets.
192 }
193
194 // Activate a widget, and wait for it to become active. On non-desktop Aura
195 // this is just an activation. For other widgets, it means activating and then
196 // spinning the run loop until the OS has activated the window.
ActivateSync(Widget * widget)197 void ActivateSync(Widget* widget) {
198 views::test::WidgetActivationWaiter waiter(widget, true);
199 widget->Activate();
200 waiter.Wait();
201 }
202
203 // Like for ActivateSync(), wait for a widget to become active, but Show() the
204 // widget rather than calling Activate().
ShowSync(Widget * widget)205 void ShowSync(Widget* widget) {
206 views::test::WidgetActivationWaiter waiter(widget, true);
207 widget->Show();
208 waiter.Wait();
209 }
210
DeactivateSync(Widget * widget)211 void DeactivateSync(Widget* widget) {
212 #if defined(OS_APPLE)
213 // Deactivation of a window isn't a concept on Mac: If an application is
214 // active and it has any activatable windows, then one of them is always
215 // active. But we can simulate deactivation (e.g. as if another application
216 // became active) by temporarily making |widget| non-activatable, then
217 // activating (and closing) a temporary widget.
218 widget->widget_delegate()->SetCanActivate(false);
219 Widget* stealer = new Widget;
220 stealer->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW));
221 ShowSync(stealer);
222 stealer->CloseNow();
223 widget->widget_delegate()->SetCanActivate(true);
224 #else
225 views::test::WidgetActivationWaiter waiter(widget, false);
226 widget->Deactivate();
227 waiter.Wait();
228 #endif
229 }
230
231 #if defined(OS_WIN)
ActivatePlatformWindow(Widget * widget)232 void ActivatePlatformWindow(Widget* widget) {
233 ::SetActiveWindow(
234 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
235 }
236 #endif
237
238 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
239 // the OS a chance to activate a widget. However, for this case, the test
240 // doesn't expect that to happen, so there is nothing to wait for.
ShowInactiveSync(Widget * widget)241 void ShowInactiveSync(Widget* widget) {
242 widget->ShowInactive();
243 RunPendingMessagesForActiveStatusChange();
244 }
245
246 // Wait until |callback| returns |expected_value|, but no longer than 1 second.
247 class PropertyWaiter {
248 public:
PropertyWaiter(base::RepeatingCallback<bool (void)> callback,bool expected_value)249 PropertyWaiter(base::RepeatingCallback<bool(void)> callback,
250 bool expected_value)
251 : callback_(std::move(callback)), expected_value_(expected_value) {}
252
Wait()253 bool Wait() {
254 if (callback_.Run() == expected_value_) {
255 success_ = true;
256 return success_;
257 }
258 start_time_ = base::TimeTicks::Now();
259 timer_.Start(FROM_HERE, base::TimeDelta(), this, &PropertyWaiter::Check);
260 run_loop_.Run();
261 return success_;
262 }
263
264 private:
Check()265 void Check() {
266 DCHECK(!success_);
267 success_ = callback_.Run() == expected_value_;
268 if (success_ || base::TimeTicks::Now() - start_time_ > kTimeout) {
269 timer_.Stop();
270 run_loop_.Quit();
271 }
272 }
273
274 const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1);
275 base::RepeatingCallback<bool(void)> callback_;
276 const bool expected_value_;
277 bool success_ = false;
278 base::TimeTicks start_time_;
279 base::RunLoop run_loop_;
280 base::RepeatingTimer timer_;
281 };
282
283 } // namespace
284
285 class WidgetTestInteractive : public WidgetTest {
286 public:
287 WidgetTestInteractive() = default;
288 ~WidgetTestInteractive() override = default;
289
SetUp()290 void SetUp() override {
291 SetUpForInteractiveTests();
292 WidgetTest::SetUp();
293 }
294 };
295
296 #if defined(OS_WIN)
297 // Tests whether activation and focus change works correctly in Windows.
298 // We test the following:-
299 // 1. If the active aura window is correctly set when a top level widget is
300 // created.
301 // 2. If the active aura window in widget 1 created above, is set to NULL when
302 // another top level widget is created and focused.
303 // 3. On focusing the native platform window for widget 1, the active aura
304 // window for widget 1 should be set and that for widget 2 should reset.
305 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
TEST_F(DesktopWidgetTestInteractive,DesktopNativeWidgetAuraActivationAndFocusTest)306 TEST_F(DesktopWidgetTestInteractive,
307 DesktopNativeWidgetAuraActivationAndFocusTest) {
308 // Create widget 1 and expect the active window to be its window.
309 View* focusable_view1 = new View;
310 focusable_view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
311 WidgetAutoclosePtr widget1(CreateTopLevelNativeWidget());
312 widget1->GetContentsView()->AddChildView(focusable_view1);
313 widget1->Show();
314 aura::Window* root_window1 = GetRootWindow(widget1.get());
315 focusable_view1->RequestFocus();
316
317 EXPECT_TRUE(root_window1 != nullptr);
318 wm::ActivationClient* activation_client1 =
319 wm::GetActivationClient(root_window1);
320 EXPECT_TRUE(activation_client1 != nullptr);
321 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
322
323 // Create widget 2 and expect the active window to be its window.
324 View* focusable_view2 = new View;
325 WidgetAutoclosePtr widget2(CreateTopLevelNativeWidget());
326 widget1->GetContentsView()->AddChildView(focusable_view2);
327 widget2->Show();
328 aura::Window* root_window2 = GetRootWindow(widget2.get());
329 focusable_view2->RequestFocus();
330 ActivatePlatformWindow(widget2.get());
331
332 wm::ActivationClient* activation_client2 =
333 wm::GetActivationClient(root_window2);
334 EXPECT_TRUE(activation_client2 != nullptr);
335 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2->GetNativeView());
336 EXPECT_EQ(activation_client1->GetActiveWindow(),
337 reinterpret_cast<aura::Window*>(NULL));
338
339 // Now set focus back to widget 1 and expect the active window to be its
340 // window.
341 focusable_view1->RequestFocus();
342 ActivatePlatformWindow(widget1.get());
343 EXPECT_EQ(activation_client2->GetActiveWindow(),
344 reinterpret_cast<aura::Window*>(NULL));
345 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1->GetNativeView());
346 }
347
348 // Verifies bubbles result in a focus lost when shown.
TEST_F(DesktopWidgetTestInteractive,FocusChangesOnBubble)349 TEST_F(DesktopWidgetTestInteractive, FocusChangesOnBubble) {
350 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
351 View* focusable_view =
352 widget->GetContentsView()->AddChildView(std::make_unique<View>());
353 focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
354 widget->Show();
355 focusable_view->RequestFocus();
356 EXPECT_TRUE(focusable_view->HasFocus());
357
358 // Show a bubble.
359 auto owned_bubble_delegate_view =
360 std::make_unique<views::BubbleDialogDelegateView>(focusable_view,
361 BubbleBorder::NONE);
362 owned_bubble_delegate_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
363 BubbleDialogDelegateView* bubble_delegate_view =
364 owned_bubble_delegate_view.get();
365 BubbleDialogDelegateView::CreateBubble(std::move(owned_bubble_delegate_view))
366 ->Show();
367 bubble_delegate_view->RequestFocus();
368
369 // |focusable_view| should no longer have focus.
370 EXPECT_FALSE(focusable_view->HasFocus());
371 EXPECT_TRUE(bubble_delegate_view->HasFocus());
372
373 bubble_delegate_view->GetWidget()->CloseNow();
374
375 // Closing the bubble should result in focus going back to the contents view.
376 EXPECT_TRUE(focusable_view->HasFocus());
377 }
378
379 class TouchEventHandler : public ui::EventHandler {
380 public:
TouchEventHandler(Widget * widget)381 explicit TouchEventHandler(Widget* widget) : widget_(widget) {
382 widget_->GetNativeWindow()->GetHost()->window()->AddPreTargetHandler(this);
383 }
384
~TouchEventHandler()385 ~TouchEventHandler() override {
386 widget_->GetNativeWindow()->GetHost()->window()->RemovePreTargetHandler(
387 this);
388 }
389
WaitForEvents()390 void WaitForEvents() {
391 base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
392 quit_closure_ = run_loop.QuitClosure();
393 run_loop.Run();
394 }
395
AsyncActivateMouse(HWND hwnd,UINT msg,ULONG_PTR data,LRESULT result)396 static void __stdcall AsyncActivateMouse(HWND hwnd,
397 UINT msg,
398 ULONG_PTR data,
399 LRESULT result) {
400 EXPECT_EQ(MA_NOACTIVATE, result);
401 std::move(reinterpret_cast<TouchEventHandler*>(data)->quit_closure_).Run();
402 }
403
ActivateViaMouse()404 void ActivateViaMouse() {
405 SendMessageCallback(
406 widget_->GetNativeWindow()->GetHost()->GetAcceleratedWidget(),
407 WM_MOUSEACTIVATE, 0, 0, AsyncActivateMouse,
408 reinterpret_cast<ULONG_PTR>(this));
409 }
410
411 private:
412 // ui::EventHandler:
OnTouchEvent(ui::TouchEvent * event)413 void OnTouchEvent(ui::TouchEvent* event) override {
414 if (event->type() == ui::ET_TOUCH_PRESSED)
415 ActivateViaMouse();
416 }
417
418 Widget* widget_;
419 base::OnceClosure quit_closure_;
420 DISALLOW_COPY_AND_ASSIGN(TouchEventHandler);
421 };
422
423 // TODO(dtapuska): Disabled due to it being flaky crbug.com/817531
TEST_F(DesktopWidgetTestInteractive,DISABLED_TouchNoActivateWindow)424 TEST_F(DesktopWidgetTestInteractive, DISABLED_TouchNoActivateWindow) {
425 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
426 // on Windows 8 and up.
427 if (base::win::GetVersion() <= base::win::Version::WIN7)
428 return;
429
430 View* focusable_view = new View;
431 focusable_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
432 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
433 widget->GetContentsView()->AddChildView(focusable_view);
434 widget->Show();
435
436 {
437 TouchEventHandler touch_event_handler(widget.get());
438 ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS, 1, 100, 100));
439 touch_event_handler.WaitForEvents();
440 }
441 }
442
443 #endif // defined(OS_WIN)
444
445 // Tests mouse move outside of the window into the "resize controller" and back
446 // will still generate an OnMouseEntered and OnMouseExited event..
TEST_F(WidgetTestInteractive,CheckResizeControllerEvents)447 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
448 WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget());
449
450 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
451
452 MouseView* view = new MouseView();
453 view->SetBounds(90, 90, 10, 10);
454 // |view| needs to be a particular size. Reset the LayoutManager so that
455 // it doesn't get resized.
456 toplevel->GetRootView()->SetLayoutManager(nullptr);
457 toplevel->GetRootView()->AddChildView(view);
458
459 toplevel->Show();
460 RunPendingMessages();
461
462 // Move to an outside position.
463 gfx::Point p1(200, 200);
464 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
465 ui::EF_NONE, ui::EF_NONE);
466 toplevel->OnMouseEvent(&moved_out);
467 EXPECT_EQ(0, view->EnteredCalls());
468 EXPECT_EQ(0, view->ExitedCalls());
469
470 // Move onto the active view.
471 gfx::Point p2(95, 95);
472 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
473 ui::EF_NONE, ui::EF_NONE);
474 toplevel->OnMouseEvent(&moved_over);
475 EXPECT_EQ(1, view->EnteredCalls());
476 EXPECT_EQ(0, view->ExitedCalls());
477
478 // Move onto the outer resizing border.
479 gfx::Point p3(102, 95);
480 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
481 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
482 toplevel->OnMouseEvent(&moved_resizer);
483 EXPECT_EQ(0, view->EnteredCalls());
484 EXPECT_EQ(1, view->ExitedCalls());
485
486 // Move onto the view again.
487 toplevel->OnMouseEvent(&moved_over);
488 EXPECT_EQ(1, view->EnteredCalls());
489 EXPECT_EQ(0, view->ExitedCalls());
490 }
491
492 // Test view focus restoration when a widget is deactivated and re-activated.
TEST_F(WidgetTestInteractive,ViewFocusOnWidgetActivationChanges)493 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
494 WidgetAutoclosePtr widget1(CreateTopLevelPlatformWidget());
495 View* view1 = new View;
496 view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
497 widget1->GetContentsView()->AddChildView(view1);
498
499 WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget());
500 View* view2a = new View;
501 View* view2b = new View;
502 view2a->SetFocusBehavior(View::FocusBehavior::ALWAYS);
503 view2b->SetFocusBehavior(View::FocusBehavior::ALWAYS);
504 widget2->GetContentsView()->AddChildView(view2a);
505 widget2->GetContentsView()->AddChildView(view2b);
506
507 ShowSync(widget1.get());
508 EXPECT_TRUE(widget1->IsActive());
509 view1->RequestFocus();
510 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
511
512 ShowSync(widget2.get());
513 EXPECT_TRUE(widget2->IsActive());
514 EXPECT_FALSE(widget1->IsActive());
515 EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView());
516 view2a->RequestFocus();
517 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
518 view2b->RequestFocus();
519 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
520
521 ActivateSync(widget1.get());
522 EXPECT_TRUE(widget1->IsActive());
523 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
524 EXPECT_FALSE(widget2->IsActive());
525 EXPECT_EQ(nullptr, widget2->GetFocusManager()->GetFocusedView());
526
527 ActivateSync(widget2.get());
528 EXPECT_TRUE(widget2->IsActive());
529 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
530 EXPECT_FALSE(widget1->IsActive());
531 EXPECT_EQ(nullptr, widget1->GetFocusManager()->GetFocusedView());
532 }
533
534 // Test z-order of child widgets relative to their parent.
TEST_F(WidgetTestInteractive,ChildStackedRelativeToParent)535 TEST_F(WidgetTestInteractive, ChildStackedRelativeToParent) {
536 WidgetAutoclosePtr parent(CreateTopLevelPlatformWidget());
537 Widget* child = CreateChildPlatformWidget(parent->GetNativeView());
538
539 parent->SetBounds(gfx::Rect(160, 100, 320, 200));
540 child->SetBounds(gfx::Rect(50, 50, 30, 20));
541
542 // Child shown first. Initially not visible, but on top of parent when shown.
543 // Use ShowInactive whenever showing the child, otherwise the usual activation
544 // logic will just put it on top anyway. Here, we want to ensure it is on top
545 // of its parent regardless.
546 child->ShowInactive();
547 EXPECT_FALSE(child->IsVisible());
548
549 ShowSync(parent.get());
550 EXPECT_TRUE(child->IsVisible());
551 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
552 EXPECT_FALSE(IsWindowStackedAbove(parent.get(), child)); // Sanity check.
553
554 WidgetAutoclosePtr popover(CreateTopLevelPlatformWidget());
555 popover->SetBounds(gfx::Rect(150, 90, 340, 240));
556 ShowSync(popover.get());
557
558 // NOTE: for aura-mus-client stacking of top-levels is not maintained in the
559 // client, so z-order of top-levels can't be determined.
560 EXPECT_TRUE(IsWindowStackedAbove(popover.get(), child));
561 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
562
563 // Showing the parent again should raise it and its child above the popover.
564 ShowSync(parent.get());
565 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
566 EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
567
568 // Test grandchildren.
569 Widget* grandchild = CreateChildPlatformWidget(child->GetNativeView());
570 grandchild->SetBounds(gfx::Rect(5, 5, 15, 10));
571 grandchild->ShowInactive();
572 EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
573 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
574 EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
575
576 ShowSync(popover.get());
577 EXPECT_TRUE(IsWindowStackedAbove(popover.get(), grandchild));
578 EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
579
580 ShowSync(parent.get());
581 EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
582 EXPECT_TRUE(IsWindowStackedAbove(child, popover.get()));
583
584 // Test hiding and reshowing.
585 parent->Hide();
586 EXPECT_FALSE(grandchild->IsVisible());
587 ShowSync(parent.get());
588
589 EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
590 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
591 EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
592
593 grandchild->Hide();
594 EXPECT_FALSE(grandchild->IsVisible());
595 grandchild->ShowInactive();
596
597 EXPECT_TRUE(IsWindowStackedAbove(grandchild, child));
598 EXPECT_TRUE(IsWindowStackedAbove(child, parent.get()));
599 EXPECT_TRUE(IsWindowStackedAbove(parent.get(), popover.get()));
600 }
601
602 #if defined(OS_WIN)
603
604 // Test view focus retention when a widget's HWND is disabled and re-enabled.
TEST_F(WidgetTestInteractive,ViewFocusOnHWNDEnabledChanges)605 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
606 WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget());
607 widget->SetContentsView(std::make_unique<View>());
608 for (size_t i = 0; i < 2; ++i) {
609 auto child = std::make_unique<View>();
610 child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
611 widget->GetContentsView()->AddChildView(std::move(child));
612 }
613
614 widget->Show();
615 widget->GetNativeWindow()->GetHost()->Show();
616 const HWND hwnd = HWNDForWidget(widget.get());
617 EXPECT_TRUE(::IsWindow(hwnd));
618 EXPECT_TRUE(::IsWindowEnabled(hwnd));
619 EXPECT_EQ(hwnd, ::GetActiveWindow());
620
621 for (View* view : widget->GetContentsView()->children()) {
622 SCOPED_TRACE(base::StringPrintf(
623 "Child view %d", widget->GetContentsView()->GetIndexOf(view)));
624
625 view->RequestFocus();
626 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
627 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
628 EXPECT_FALSE(::IsWindowEnabled(hwnd));
629
630 // Oddly, disabling the HWND leaves it active with the focus unchanged.
631 EXPECT_EQ(hwnd, ::GetActiveWindow());
632 EXPECT_TRUE(widget->IsActive());
633 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
634
635 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
636 EXPECT_TRUE(::IsWindowEnabled(hwnd));
637 EXPECT_EQ(hwnd, ::GetActiveWindow());
638 EXPECT_TRUE(widget->IsActive());
639 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
640 }
641 }
642
643 // This class subclasses the Widget class to listen for activation change
644 // notifications and provides accessors to return information as to whether
645 // the widget is active. We need this to ensure that users of the widget
646 // class activate the widget only when the underlying window becomes really
647 // active. Previously we would activate the widget in the WM_NCACTIVATE
648 // message which is incorrect because APIs like FlashWindowEx flash the
649 // window caption by sending fake WM_NCACTIVATE messages.
650 class WidgetActivationTest : public Widget {
651 public:
652 WidgetActivationTest() = default;
653
654 ~WidgetActivationTest() override = default;
655
OnNativeWidgetActivationChanged(bool active)656 bool OnNativeWidgetActivationChanged(bool active) override {
657 active_ = active;
658 return true;
659 }
660
active() const661 bool active() const { return active_; }
662
663 private:
664 bool active_ = false;
665
666 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
667 };
668
669 // Tests whether the widget only becomes active when the underlying window
670 // is really active.
TEST_F(WidgetTestInteractive,WidgetNotActivatedOnFakeActivationMessages)671 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
672 WidgetActivationTest widget1;
673 Widget::InitParams init_params =
674 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
675 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
676 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
677 init_params.bounds = gfx::Rect(0, 0, 200, 200);
678 widget1.Init(std::move(init_params));
679 widget1.Show();
680 EXPECT_EQ(true, widget1.active());
681
682 WidgetActivationTest widget2;
683 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
684 widget2.Init(std::move(init_params));
685 widget2.Show();
686 EXPECT_EQ(true, widget2.active());
687 EXPECT_EQ(false, widget1.active());
688
689 HWND win32_native_window1 = HWNDForWidget(&widget1);
690 EXPECT_TRUE(::IsWindow(win32_native_window1));
691
692 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
693 EXPECT_EQ(false, widget1.active());
694 EXPECT_EQ(true, widget2.active());
695
696 ::SetActiveWindow(win32_native_window1);
697 EXPECT_EQ(true, widget1.active());
698 EXPECT_EQ(false, widget2.active());
699 }
700
701 // On Windows if we create a fullscreen window on a thread, then it affects the
702 // way other windows on the thread interact with the taskbar. To workaround
703 // this we reduce the bounds of a fullscreen window by 1px when it loses
704 // activation. This test verifies the same.
TEST_F(WidgetTestInteractive,FullscreenBoundsReducedOnActivationLoss)705 TEST_F(WidgetTestInteractive, FullscreenBoundsReducedOnActivationLoss) {
706 Widget widget1;
707 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
708 params.native_widget = new DesktopNativeWidgetAura(&widget1);
709 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
710 widget1.Init(std::move(params));
711 widget1.SetBounds(gfx::Rect(0, 0, 200, 200));
712 widget1.Show();
713
714 widget1.Activate();
715 RunPendingMessages();
716 EXPECT_EQ(::GetActiveWindow(),
717 widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
718
719 widget1.SetFullscreen(true);
720 EXPECT_TRUE(widget1.IsFullscreen());
721 // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs.
722 // This task is queued when a widget becomes fullscreen.
723 RunPendingMessages();
724 EXPECT_EQ(::GetActiveWindow(),
725 widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
726 gfx::Rect fullscreen_bounds = widget1.GetWindowBoundsInScreen();
727
728 Widget widget2;
729 params.native_widget = new DesktopNativeWidgetAura(&widget2);
730 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
731 widget2.Init(std::move(params));
732 widget2.SetBounds(gfx::Rect(0, 0, 200, 200));
733 widget2.Show();
734
735 widget2.Activate();
736 RunPendingMessages();
737 EXPECT_EQ(::GetActiveWindow(),
738 widget2.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
739
740 gfx::Rect fullscreen_bounds_after_activation_loss =
741 widget1.GetWindowBoundsInScreen();
742
743 // After deactivation loss the bounds of the fullscreen widget should be
744 // reduced by 1px.
745 EXPECT_EQ(fullscreen_bounds.height() -
746 fullscreen_bounds_after_activation_loss.height(),
747 1);
748
749 widget1.Activate();
750 RunPendingMessages();
751 EXPECT_EQ(::GetActiveWindow(),
752 widget1.GetNativeWindow()->GetHost()->GetAcceleratedWidget());
753
754 gfx::Rect fullscreen_bounds_after_activate =
755 widget1.GetWindowBoundsInScreen();
756
757 // After activation the bounds of the fullscreen widget should be restored.
758 EXPECT_EQ(fullscreen_bounds, fullscreen_bounds_after_activate);
759
760 widget1.CloseNow();
761 widget2.CloseNow();
762 }
763
764 // Ensure the window rect and client rects are correct with a window that was
765 // maximized.
TEST_F(WidgetTestInteractive,FullscreenMaximizedWindowBounds)766 TEST_F(WidgetTestInteractive, FullscreenMaximizedWindowBounds) {
767 Widget widget;
768 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
769 params.native_widget = new DesktopNativeWidgetAura(&widget);
770 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
771 widget.set_frame_type(Widget::FrameType::kForceCustom);
772 widget.Init(std::move(params));
773 widget.SetBounds(gfx::Rect(0, 0, 200, 200));
774 widget.Show();
775
776 widget.Maximize();
777 EXPECT_TRUE(widget.IsMaximized());
778
779 widget.SetFullscreen(true);
780 EXPECT_TRUE(widget.IsFullscreen());
781 EXPECT_FALSE(widget.IsMaximized());
782 // Ensure that the StopIgnoringPosChanges task in HWNDMessageHandler runs.
783 // This task is queued when a widget becomes fullscreen.
784 RunPendingMessages();
785
786 aura::WindowTreeHost* host = widget.GetNativeWindow()->GetHost();
787 HWND hwnd = host->GetAcceleratedWidget();
788
789 HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
790 ASSERT_TRUE(!!monitor);
791 MONITORINFO monitor_info;
792 monitor_info.cbSize = sizeof(monitor_info);
793 ASSERT_TRUE(::GetMonitorInfo(monitor, &monitor_info));
794
795 gfx::Rect monitor_bounds(monitor_info.rcMonitor);
796 gfx::Rect window_bounds = widget.GetWindowBoundsInScreen();
797 gfx::Rect client_area_bounds = host->GetBoundsInPixels();
798
799 EXPECT_EQ(window_bounds, monitor_bounds);
800 EXPECT_EQ(monitor_bounds, client_area_bounds);
801
802 // Setting not fullscreen should return it to maximized.
803 widget.SetFullscreen(false);
804 EXPECT_FALSE(widget.IsFullscreen());
805 EXPECT_TRUE(widget.IsMaximized());
806
807 client_area_bounds = host->GetBoundsInPixels();
808 EXPECT_TRUE(monitor_bounds.Contains(client_area_bounds));
809 EXPECT_NE(monitor_bounds, client_area_bounds);
810
811 widget.CloseNow();
812 }
813 #endif // defined(OS_WIN)
814
815 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
816 // Tests whether the focused window is set correctly when a modal window is
817 // created and destroyed. When it is destroyed it should focus the owner window.
TEST_F(DesktopWidgetTestInteractive,WindowModalWindowDestroyedActivationTest)818 TEST_F(DesktopWidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
819 TestWidgetFocusChangeListener focus_listener;
820 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
821 const std::vector<gfx::NativeView>& focus_changes =
822 focus_listener.focus_changes();
823
824 // Create a top level widget.
825 Widget top_level_widget;
826 Widget::InitParams init_params =
827 CreateParams(Widget::InitParams::TYPE_WINDOW);
828 init_params.show_state = ui::SHOW_STATE_NORMAL;
829 gfx::Rect initial_bounds(0, 0, 500, 500);
830 init_params.bounds = initial_bounds;
831 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
832 top_level_widget.Init(std::move(init_params));
833 ShowSync(&top_level_widget);
834
835 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
836 ASSERT_FALSE(focus_listener.focus_changes().empty());
837 EXPECT_EQ(1u, focus_changes.size());
838 EXPECT_EQ(top_level_native_view, focus_changes[0]);
839
840 // Create a modal dialog.
841 auto dialog_delegate = std::make_unique<DialogDelegateView>();
842 dialog_delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
843
844 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
845 dialog_delegate.release(), nullptr, top_level_widget.GetNativeView());
846 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
847
848 // Note the dialog widget doesn't need a ShowSync. Since it is modal, it gains
849 // active status synchronously, even on Mac.
850 modal_dialog_widget->Show();
851
852 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
853 ASSERT_EQ(3u, focus_changes.size());
854 EXPECT_EQ(gfx::kNullNativeView, focus_changes[1]);
855 EXPECT_EQ(modal_native_view, focus_changes[2]);
856
857 #if defined(OS_APPLE)
858 // Window modal dialogs on Mac are "sheets", which animate to close before
859 // activating their parent widget.
860 views::test::WidgetActivationWaiter waiter(&top_level_widget, true);
861 modal_dialog_widget->Close();
862 waiter.Wait();
863 #else
864 modal_dialog_widget->CloseNow();
865 #endif
866
867 ASSERT_EQ(5u, focus_changes.size());
868 EXPECT_EQ(gfx::kNullNativeView, focus_changes[3]);
869 EXPECT_EQ(top_level_native_view, focus_changes[4]);
870
871 top_level_widget.CloseNow();
872 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
873 }
874 #endif
875
TEST_F(DesktopWidgetTestInteractive,CanActivateFlagIsHonored)876 TEST_F(DesktopWidgetTestInteractive, CanActivateFlagIsHonored) {
877 Widget widget;
878 Widget::InitParams init_params =
879 CreateParams(Widget::InitParams::TYPE_WINDOW);
880 init_params.bounds = gfx::Rect(0, 0, 200, 200);
881 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
882 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
883 widget.Init(std::move(init_params));
884
885 widget.Show();
886 EXPECT_FALSE(widget.IsActive());
887 }
888
889 #if defined(USE_AURA)
890
891 #if defined(OS_CHROMEOS)
892 // TODO(crbug.com/916272): investigate fixing and enabling on Chrome OS.
893 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
894 DISABLED_TouchSelectionQuickMenuIsNotActivated
895 #else
896 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
897 TouchSelectionQuickMenuIsNotActivated
898 #endif
899
900 // Test that touch selection quick menu is not activated when opened.
TEST_F(DesktopWidgetTestInteractive,MAYBE_TouchSelectionQuickMenuIsNotActivated)901 TEST_F(DesktopWidgetTestInteractive,
902 MAYBE_TouchSelectionQuickMenuIsNotActivated) {
903 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
904 widget->SetBounds(gfx::Rect(0, 0, 200, 200));
905
906 Textfield* textfield = new Textfield;
907 textfield->SetBounds(0, 0, 200, 20);
908 textfield->SetText(base::ASCIIToUTF16("some text"));
909 widget->GetRootView()->AddChildView(textfield);
910
911 widget->Show();
912 textfield->RequestFocus();
913 textfield->SelectAll(true);
914 TextfieldTestApi textfield_test_api(textfield);
915
916 RunPendingMessages();
917
918 ui::test::EventGenerator generator(GetRootWindow(widget.get()));
919 generator.GestureTapAt(textfield->GetBoundsInScreen().origin() +
920 gfx::Vector2d(10, 10));
921 static_cast<TouchSelectionControllerImpl*>(
922 textfield_test_api.touch_selection_controller())
923 ->ShowQuickMenuImmediatelyForTesting();
924
925 EXPECT_TRUE(textfield->HasFocus());
926 EXPECT_TRUE(widget->IsActive());
927 EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
928 }
929 #endif // defined(USE_AURA)
930
931 #if defined(OS_WIN)
TEST_F(DesktopWidgetTestInteractive,DisableViewDoesNotActivateWidget)932 TEST_F(DesktopWidgetTestInteractive, DisableViewDoesNotActivateWidget) {
933 #else
934 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
935 #endif // !defined(OS_WIN)
936
937 // Create first widget and view, activate the widget, and focus the view.
938 Widget widget1;
939 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
940 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
941 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
942 widget1.Init(std::move(params1));
943
944 View* view1 = new View();
945 view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
946 widget1.GetRootView()->AddChildView(view1);
947
948 widget1.Show();
949 ActivateSync(&widget1);
950
951 FocusManager* focus_manager1 = widget1.GetFocusManager();
952 ASSERT_TRUE(focus_manager1);
953 focus_manager1->SetFocusedView(view1);
954 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
955
956 // Create second widget and view, activate the widget, and focus the view.
957 Widget widget2;
958 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
959 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
960 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
961 widget2.Init(std::move(params2));
962
963 View* view2 = new View();
964 view2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
965 widget2.GetRootView()->AddChildView(view2);
966
967 widget2.Show();
968 ActivateSync(&widget2);
969 EXPECT_TRUE(widget2.IsActive());
970 EXPECT_FALSE(widget1.IsActive());
971
972 FocusManager* focus_manager2 = widget2.GetFocusManager();
973 ASSERT_TRUE(focus_manager2);
974 focus_manager2->SetFocusedView(view2);
975 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
976
977 // Disable the first view and make sure it loses focus, but its widget is not
978 // activated.
979 view1->SetEnabled(false);
980 EXPECT_NE(view1, focus_manager1->GetFocusedView());
981 EXPECT_FALSE(widget1.IsActive());
982 EXPECT_TRUE(widget2.IsActive());
983 } // namespace test
984
985 TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
986 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
987
988 ShowSync(widget.get());
989 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
990 }
991
992 TEST_F(WidgetTestInteractive, ShowInactive) {
993 WidgetTest::WaitForSystemAppActivation();
994 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
995
996 ShowInactiveSync(widget.get());
997 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_INACTIVE);
998 }
999
1000 TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
1001 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
1002
1003 EXPECT_FALSE(widget->IsActive());
1004 EXPECT_FALSE(widget->IsVisible());
1005
1006 ShowSync(widget.get());
1007
1008 EXPECT_TRUE(widget->IsActive());
1009 EXPECT_TRUE(widget->IsVisible());
1010 }
1011
1012 TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
1013 // Create 2 widgets to ensure window layering does not change.
1014 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
1015 WidgetAutoclosePtr widget2(CreateTopLevelPlatformWidget());
1016
1017 ShowSync(widget2.get());
1018 EXPECT_FALSE(widget->IsActive());
1019 EXPECT_TRUE(widget2->IsVisible());
1020 EXPECT_TRUE(widget2->IsActive());
1021
1022 ShowSync(widget.get());
1023 EXPECT_TRUE(widget->IsActive());
1024 EXPECT_FALSE(widget2->IsActive());
1025
1026 ShowInactiveSync(widget.get());
1027 EXPECT_TRUE(widget->IsActive());
1028 EXPECT_FALSE(widget2->IsActive());
1029 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
1030 }
1031
1032 TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
1033 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
1034 widget->SetBounds(gfx::Rect(100, 100, 100, 100));
1035
1036 ShowInactiveSync(widget.get());
1037 ShowSync(widget.get());
1038 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
1039 }
1040
1041 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
1042 TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
1043 WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
1044 ShowSync(widget.get());
1045 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
1046
1047 WidgetAutoclosePtr widget2(new Widget());
1048 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
1049 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1050 widget2->Init(std::move(params));
1051 widget2->Show();
1052 RunPendingMessagesForActiveStatusChange();
1053
1054 EXPECT_EQ(GetWidgetShowState(widget2.get()), ui::SHOW_STATE_INACTIVE);
1055 EXPECT_EQ(GetWidgetShowState(widget.get()), ui::SHOW_STATE_NORMAL);
1056 }
1057 #endif // BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
1058
1059 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1060 // currently only Desktop widgets and fullscreen changes have to coordinate with
1061 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1062 // Maximize on mac is also (intentionally) a no-op.
1063 #if defined(OS_APPLE)
1064 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1065 #else
1066 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1067 #endif
1068
1069 // Test that window state is not changed after getting out of full screen.
1070 TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
1071 WidgetAutoclosePtr toplevel(CreateTopLevelPlatformWidget());
1072
1073 toplevel->Show();
1074 RunPendingMessages();
1075
1076 // This should be a normal state window.
1077 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel.get()));
1078
1079 toplevel->SetFullscreen(true);
1080 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get()));
1081 toplevel->SetFullscreen(false);
1082 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get()));
1083
1084 // And it should still be in normal state after getting out of full screen.
1085 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel.get()));
1086
1087 // Now, make it maximized.
1088 toplevel->Maximize();
1089 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel.get()));
1090
1091 toplevel->SetFullscreen(true);
1092 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get()));
1093 toplevel->SetFullscreen(false);
1094 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel.get()));
1095
1096 // And it stays maximized after getting out of full screen.
1097 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel.get()));
1098 }
1099
1100 // Testing initial focus is assigned properly for normal top-level widgets,
1101 // and subclasses that specify a initially focused child view.
1102 TEST_F(WidgetTestInteractive, InitialFocus) {
1103 // By default, there is no initially focused view (even if there is a
1104 // focusable subview).
1105 Widget* toplevel(CreateTopLevelPlatformWidget());
1106 View* view = new View;
1107 view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
1108 toplevel->GetContentsView()->AddChildView(view);
1109
1110 ShowSync(toplevel);
1111 toplevel->Show();
1112 EXPECT_FALSE(view->HasFocus());
1113 EXPECT_FALSE(toplevel->GetFocusManager()->GetStoredFocusView());
1114 toplevel->CloseNow();
1115
1116 // Testing a widget which specifies a initially focused view.
1117 TestInitialFocusWidgetDelegate delegate(GetContext());
1118
1119 Widget* widget = delegate.GetWidget();
1120 ShowSync(widget);
1121 widget->Show();
1122 EXPECT_TRUE(delegate.view()->HasFocus());
1123 EXPECT_EQ(delegate.view(), widget->GetFocusManager()->GetStoredFocusView());
1124 }
1125
1126 TEST_F(DesktopWidgetTestInteractive, RestoreAfterMinimize) {
1127 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1128 ShowSync(widget.get());
1129 ASSERT_FALSE(widget->IsMinimized());
1130
1131 PropertyWaiter minimize_waiter(
1132 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1133 true);
1134 widget->Minimize();
1135 EXPECT_TRUE(minimize_waiter.Wait());
1136
1137 PropertyWaiter restore_waiter(
1138 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1139 false);
1140 widget->Restore();
1141 EXPECT_TRUE(restore_waiter.Wait());
1142 }
1143
1144 #if defined(OS_WIN)
1145 // TODO(davidbienvenu): Get this test to pass on Linux and ChromeOS by hiding
1146 // the root window when desktop widget is minimized.
1147 // Tests that root window visibility toggles correctly when the desktop widget
1148 // is minimized and maximized on Windows, and the Widget remains visible.
1149 TEST_F(DesktopWidgetTestInteractive, RestoreAndMinimizeVisibility) {
1150 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1151 aura::Window* root_window = GetRootWindow(widget.get());
1152 ShowSync(widget.get());
1153 ASSERT_FALSE(widget->IsMinimized());
1154 EXPECT_TRUE(root_window->IsVisible());
1155
1156 PropertyWaiter minimize_widget_waiter(
1157 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1158 true);
1159 widget->Minimize();
1160 EXPECT_TRUE(minimize_widget_waiter.Wait());
1161 EXPECT_TRUE(widget->IsVisible());
1162 EXPECT_FALSE(root_window->IsVisible());
1163
1164 PropertyWaiter restore_widget_waiter(
1165 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1166 false);
1167 widget->Restore();
1168 EXPECT_TRUE(restore_widget_waiter.Wait());
1169 EXPECT_TRUE(widget->IsVisible());
1170 EXPECT_TRUE(root_window->IsVisible());
1171 }
1172
1173 // Test that focus is restored to the widget after a minimized window
1174 // is activated.
1175 TEST_F(DesktopWidgetTestInteractive, MinimizeAndActivateFocus) {
1176 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1177 aura::Window* root_window = GetRootWindow(widget.get());
1178 auto* widget_window = widget->GetNativeWindow();
1179 ShowSync(widget.get());
1180 ASSERT_FALSE(widget->IsMinimized());
1181 EXPECT_TRUE(root_window->IsVisible());
1182 widget_window->Focus();
1183 EXPECT_TRUE(widget_window->HasFocus());
1184 widget->GetContentsView()->SetFocusBehavior(View::FocusBehavior::ALWAYS);
1185 widget->GetContentsView()->RequestFocus();
1186 EXPECT_TRUE(widget->GetContentsView()->HasFocus());
1187
1188 PropertyWaiter minimize_widget_waiter(
1189 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1190 true);
1191 widget->Minimize();
1192 EXPECT_TRUE(minimize_widget_waiter.Wait());
1193 EXPECT_TRUE(widget->IsVisible());
1194 EXPECT_FALSE(root_window->IsVisible());
1195
1196 PropertyWaiter restore_widget_waiter(
1197 base::BindRepeating(&Widget::IsMinimized, base::Unretained(widget.get())),
1198 false);
1199 widget->Activate();
1200 EXPECT_TRUE(widget->GetContentsView()->HasFocus());
1201 EXPECT_TRUE(restore_widget_waiter.Wait());
1202 EXPECT_TRUE(widget->IsVisible());
1203 EXPECT_TRUE(root_window->IsVisible());
1204 EXPECT_TRUE(widget_window->CanFocus());
1205 }
1206
1207 #endif // defined(OS_WIN)
1208
1209 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
1210 // Tests that minimizing a widget causes the gesture_handler
1211 // to be cleared when the widget is minimized.
1212 TEST_F(DesktopWidgetTestInteractive, EventHandlersClearedOnWidgetMinimize) {
1213 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1214 ShowSync(widget.get());
1215 ASSERT_FALSE(widget->IsMinimized());
1216 View mouse_handler_view;
1217 internal::RootView* root_view =
1218 static_cast<internal::RootView*>(widget->GetRootView());
1219 // This also sets the gesture_handler, and we'll verify that it
1220 // gets cleared when the widget is minimized.
1221 root_view->SetMouseHandler(&mouse_handler_view);
1222 EXPECT_TRUE(GetGestureHandler(root_view));
1223
1224 widget->Minimize();
1225 EXPECT_FALSE(GetGestureHandler(root_view));
1226 }
1227 #endif
1228
1229 #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && \
1230 BUILDFLAG(ENABLE_DESKTOP_AURA)
1231 // Tests that when a desktop native widget has modal transient child, it should
1232 // avoid restore focused view itself as the modal transient child window will do
1233 // that, thus avoids having multiple focused view visually (crbug.com/727641).
1234 TEST_F(DesktopWidgetTestInteractive,
1235 DesktopNativeWidgetWithModalTransientChild) {
1236 // Create a desktop native Widget for Widget::Deactivate().
1237 WidgetAutoclosePtr deactivate_widget(CreateTopLevelNativeWidget());
1238 ShowSync(deactivate_widget.get());
1239
1240 // Create a top level desktop native widget.
1241 WidgetAutoclosePtr top_level(CreateTopLevelNativeWidget());
1242
1243 Textfield* textfield = new Textfield;
1244 textfield->SetBounds(0, 0, 200, 20);
1245 top_level->GetRootView()->AddChildView(textfield);
1246 ShowSync(top_level.get());
1247 textfield->RequestFocus();
1248 EXPECT_TRUE(textfield->HasFocus());
1249
1250 // Create a modal dialog.
1251 // This instance will be destroyed when the dialog is destroyed.
1252 auto dialog_delegate = std::make_unique<DialogDelegateView>();
1253 dialog_delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
1254 Widget* modal_dialog_widget = DialogDelegate::CreateDialogWidget(
1255 dialog_delegate.release(), nullptr, top_level->GetNativeView());
1256 modal_dialog_widget->SetBounds(gfx::Rect(0, 0, 100, 10));
1257 Textfield* dialog_textfield = new Textfield;
1258 dialog_textfield->SetBounds(0, 0, 50, 5);
1259 modal_dialog_widget->GetRootView()->AddChildView(dialog_textfield);
1260 // Dialog widget doesn't need a ShowSync as it gains active status
1261 // synchronously.
1262 modal_dialog_widget->Show();
1263 dialog_textfield->RequestFocus();
1264 EXPECT_TRUE(dialog_textfield->HasFocus());
1265 EXPECT_FALSE(textfield->HasFocus());
1266
1267 DeactivateSync(top_level.get());
1268 EXPECT_FALSE(dialog_textfield->HasFocus());
1269 EXPECT_FALSE(textfield->HasFocus());
1270
1271 // After deactivation and activation of top level widget, only modal dialog
1272 // should restore focused view.
1273 ActivateSync(top_level.get());
1274 EXPECT_TRUE(dialog_textfield->HasFocus());
1275 EXPECT_FALSE(textfield->HasFocus());
1276 }
1277 #endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) &&
1278 // BUILDFLAG(ENABLE_DESKTOP_AURA)
1279
1280 namespace {
1281
1282 // Helper class for CaptureLostTrackingWidget to store whether
1283 // OnMouseCaptureLost has been invoked for a widget.
1284 class CaptureLostState {
1285 public:
1286 CaptureLostState() = default;
1287
1288 bool GetAndClearGotCaptureLost() {
1289 bool value = got_capture_lost_;
1290 got_capture_lost_ = false;
1291 return value;
1292 }
1293
1294 void OnMouseCaptureLost() { got_capture_lost_ = true; }
1295
1296 private:
1297 bool got_capture_lost_ = false;
1298
1299 DISALLOW_COPY_AND_ASSIGN(CaptureLostState);
1300 };
1301
1302 // Used to verify OnMouseCaptureLost() has been invoked.
1303 class CaptureLostTrackingWidget : public Widget {
1304 public:
1305 explicit CaptureLostTrackingWidget(CaptureLostState* capture_lost_state)
1306 : capture_lost_state_(capture_lost_state) {}
1307
1308 // Widget:
1309 void OnMouseCaptureLost() override {
1310 capture_lost_state_->OnMouseCaptureLost();
1311 Widget::OnMouseCaptureLost();
1312 }
1313
1314 private:
1315 // Weak. Stores whether OnMouseCaptureLost has been invoked for this widget.
1316 CaptureLostState* capture_lost_state_;
1317
1318 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
1319 };
1320
1321 } // namespace
1322
1323 class WidgetCaptureTest : public DesktopWidgetTestInteractive {
1324 public:
1325 WidgetCaptureTest() = default;
1326 ~WidgetCaptureTest() override = default;
1327
1328 // Verifies Widget::SetCapture() results in updating native capture along with
1329 // invoking the right Widget function.
1330 void TestCapture(bool use_desktop_native_widget) {
1331 CaptureLostState capture_state1;
1332 CaptureLostTrackingWidget widget1(&capture_state1);
1333 InitPlatformWidget(&widget1, use_desktop_native_widget);
1334 widget1.Show();
1335
1336 CaptureLostState capture_state2;
1337 CaptureLostTrackingWidget widget2(&capture_state2);
1338 InitPlatformWidget(&widget2, use_desktop_native_widget);
1339 widget2.Show();
1340
1341 // Set capture to widget2 and verity it gets it.
1342 widget2.SetCapture(widget2.GetRootView());
1343 EXPECT_FALSE(widget1.HasCapture());
1344 EXPECT_TRUE(widget2.HasCapture());
1345 EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost());
1346 EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost());
1347
1348 // Set capture to widget1 and verify it gets it.
1349 widget1.SetCapture(widget1.GetRootView());
1350 EXPECT_TRUE(widget1.HasCapture());
1351 EXPECT_FALSE(widget2.HasCapture());
1352 EXPECT_FALSE(capture_state1.GetAndClearGotCaptureLost());
1353 EXPECT_TRUE(capture_state2.GetAndClearGotCaptureLost());
1354
1355 // Release and verify no one has it.
1356 widget1.ReleaseCapture();
1357 EXPECT_FALSE(widget1.HasCapture());
1358 EXPECT_FALSE(widget2.HasCapture());
1359 EXPECT_TRUE(capture_state1.GetAndClearGotCaptureLost());
1360 EXPECT_FALSE(capture_state2.GetAndClearGotCaptureLost());
1361 }
1362
1363 void InitPlatformWidget(Widget* widget, bool use_desktop_native_widget) {
1364 Widget::InitParams params =
1365 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1366 // The test class by default returns DesktopNativeWidgetAura.
1367 params.native_widget =
1368 use_desktop_native_widget
1369 ? nullptr
1370 : CreatePlatformNativeWidgetImpl(widget, kDefault, nullptr);
1371 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1372 widget->Init(std::move(params));
1373 }
1374
1375 private:
1376 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
1377 };
1378
1379 // See description in TestCapture().
1380 TEST_F(WidgetCaptureTest, Capture) {
1381 TestCapture(false);
1382 }
1383
1384 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
1385 // See description in TestCapture(). Creates DesktopNativeWidget.
1386 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
1387 TestCapture(true);
1388 }
1389 #endif
1390
1391 // Tests to ensure capture is correctly released from a Widget with capture when
1392 // it is destroyed. Test for crbug.com/622201.
1393 TEST_F(WidgetCaptureTest, DestroyWithCapture_CloseNow) {
1394 CaptureLostState capture_state;
1395 CaptureLostTrackingWidget* widget =
1396 new CaptureLostTrackingWidget(&capture_state);
1397 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
1398 widget->Init(std::move(params));
1399 widget->Show();
1400
1401 widget->SetCapture(widget->GetRootView());
1402 EXPECT_TRUE(widget->HasCapture());
1403 EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
1404 widget->CloseNow();
1405
1406 EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
1407 }
1408
1409 TEST_F(WidgetCaptureTest, DestroyWithCapture_Close) {
1410 CaptureLostState capture_state;
1411 CaptureLostTrackingWidget* widget =
1412 new CaptureLostTrackingWidget(&capture_state);
1413 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
1414 widget->Init(std::move(params));
1415 widget->Show();
1416
1417 widget->SetCapture(widget->GetRootView());
1418 EXPECT_TRUE(widget->HasCapture());
1419 EXPECT_FALSE(capture_state.GetAndClearGotCaptureLost());
1420 widget->Close();
1421 EXPECT_TRUE(capture_state.GetAndClearGotCaptureLost());
1422 }
1423
1424 TEST_F(WidgetCaptureTest, DestroyWithCapture_WidgetOwnsNativeWidget) {
1425 Widget widget;
1426 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
1427 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1428 widget.Init(std::move(params));
1429 widget.Show();
1430
1431 widget.SetCapture(widget.GetRootView());
1432 EXPECT_TRUE(widget.HasCapture());
1433 }
1434
1435 // Test that no state is set if capture fails.
1436 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
1437 Widget widget;
1438 Widget::InitParams params =
1439 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1440 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1441 params.bounds = gfx::Rect(400, 400);
1442 widget.Init(std::move(params));
1443
1444 MouseView* mouse_view1 = new MouseView;
1445 MouseView* mouse_view2 = new MouseView;
1446 auto contents_view = std::make_unique<View>();
1447 contents_view->AddChildView(mouse_view1);
1448 contents_view->AddChildView(mouse_view2);
1449 widget.SetContentsView(std::move(contents_view));
1450
1451 mouse_view1->SetBounds(0, 0, 200, 400);
1452 mouse_view2->SetBounds(200, 0, 200, 400);
1453
1454 // Setting capture should fail because |widget| is not visible.
1455 widget.SetCapture(mouse_view1);
1456 EXPECT_FALSE(widget.HasCapture());
1457
1458 widget.Show();
1459 ui::test::EventGenerator generator(GetRootWindow(&widget),
1460 widget.GetNativeWindow());
1461 generator.set_current_screen_location(gfx::Point(300, 10));
1462 generator.PressLeftButton();
1463
1464 EXPECT_FALSE(mouse_view1->pressed());
1465 EXPECT_TRUE(mouse_view2->pressed());
1466 }
1467
1468 TEST_F(WidgetCaptureTest, CaptureAutoReset) {
1469 WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget());
1470 toplevel->SetContentsView(std::make_unique<View>());
1471
1472 EXPECT_FALSE(toplevel->HasCapture());
1473 toplevel->SetCapture(nullptr);
1474 EXPECT_TRUE(toplevel->HasCapture());
1475
1476 // By default, mouse release removes capture.
1477 gfx::Point click_location(45, 15);
1478 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
1479 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
1480 ui::EF_LEFT_MOUSE_BUTTON);
1481 toplevel->OnMouseEvent(&release);
1482 EXPECT_FALSE(toplevel->HasCapture());
1483
1484 // Now a mouse release shouldn't remove capture.
1485 toplevel->set_auto_release_capture(false);
1486 toplevel->SetCapture(nullptr);
1487 EXPECT_TRUE(toplevel->HasCapture());
1488 toplevel->OnMouseEvent(&release);
1489 EXPECT_TRUE(toplevel->HasCapture());
1490 toplevel->ReleaseCapture();
1491 EXPECT_FALSE(toplevel->HasCapture());
1492 }
1493
1494 TEST_F(WidgetCaptureTest, ResetCaptureOnGestureEnd) {
1495 WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget());
1496 View* container = toplevel->SetContentsView(std::make_unique<View>());
1497
1498 View* gesture = new GestureCaptureView;
1499 gesture->SetBounds(0, 0, 30, 30);
1500 container->AddChildView(gesture);
1501
1502 MouseView* mouse = new MouseView;
1503 mouse->SetBounds(30, 0, 30, 30);
1504 container->AddChildView(mouse);
1505
1506 toplevel->SetSize(gfx::Size(100, 100));
1507 toplevel->Show();
1508
1509 // Start a gesture on |gesture|.
1510 ui::GestureEvent tap_down(15, 15, 0, base::TimeTicks(),
1511 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
1512 ui::GestureEvent end(15, 15, 0, base::TimeTicks(),
1513 ui::GestureEventDetails(ui::ET_GESTURE_END));
1514 toplevel->OnGestureEvent(&tap_down);
1515
1516 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
1517 // will not receive the event.
1518 gfx::Point click_location(45, 15);
1519
1520 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
1521 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
1522 ui::EF_LEFT_MOUSE_BUTTON);
1523 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
1524 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
1525 ui::EF_LEFT_MOUSE_BUTTON);
1526
1527 EXPECT_TRUE(toplevel->HasCapture());
1528
1529 toplevel->OnMouseEvent(&press);
1530 toplevel->OnMouseEvent(&release);
1531 EXPECT_EQ(0, mouse->pressed());
1532
1533 EXPECT_FALSE(toplevel->HasCapture());
1534
1535 // The end of the gesture should release the capture, and pressing on |mouse|
1536 // should now reach |mouse|.
1537 toplevel->OnGestureEvent(&end);
1538 toplevel->OnMouseEvent(&press);
1539 toplevel->OnMouseEvent(&release);
1540 EXPECT_EQ(1, mouse->pressed());
1541 }
1542
1543 // Checks that if a mouse-press triggers a capture on a different widget (which
1544 // consumes the mouse-release event), then the target of the press does not have
1545 // capture.
1546 TEST_F(WidgetCaptureTest, DisableCaptureWidgetFromMousePress) {
1547 // The test creates two widgets: |first| and |second|.
1548 // The View in |first| makes |second| visible, sets capture on it, and starts
1549 // a nested loop (like a menu does). The View in |second| terminates the
1550 // nested loop and closes the widget.
1551 // The test sends a mouse-press event to |first|, and posts a task to send a
1552 // release event to |second|, to make sure that the release event is
1553 // dispatched after the nested loop starts.
1554
1555 WidgetAutoclosePtr first(CreateTopLevelFramelessPlatformWidget());
1556 Widget* second = CreateTopLevelFramelessPlatformWidget();
1557
1558 NestedLoopCaptureView* container =
1559 first->SetContentsView(std::make_unique<NestedLoopCaptureView>(second));
1560
1561 second->SetContentsView(
1562 std::make_unique<ExitLoopOnRelease>(container->GetQuitClosure()));
1563
1564 first->SetSize(gfx::Size(100, 100));
1565 first->Show();
1566
1567 gfx::Point location(20, 20);
1568 base::ThreadTaskRunnerHandle::Get()->PostTask(
1569 FROM_HERE,
1570 base::BindOnce(
1571 &Widget::OnMouseEvent, base::Unretained(second),
1572 base::Owned(new ui::MouseEvent(
1573 ui::ET_MOUSE_RELEASED, location, location, ui::EventTimeForNow(),
1574 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON))));
1575 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
1576 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
1577 ui::EF_LEFT_MOUSE_BUTTON);
1578 first->OnMouseEvent(&press);
1579 EXPECT_FALSE(first->HasCapture());
1580 }
1581
1582 // Tests some grab/ungrab events. Only one Widget can have capture at any given
1583 // time.
1584 TEST_F(WidgetCaptureTest, GrabUngrab) {
1585 auto top_level = CreateTestWidget();
1586 top_level->SetContentsView(std::make_unique<MouseView>());
1587
1588 Widget* child1 = new Widget;
1589 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_CONTROL);
1590 params1.parent = top_level->GetNativeView();
1591 params1.bounds = gfx::Rect(10, 10, 100, 100);
1592 child1->Init(std::move(params1));
1593 child1->SetContentsView(std::make_unique<MouseView>());
1594
1595 Widget* child2 = new Widget;
1596 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_CONTROL);
1597 params2.parent = top_level->GetNativeView();
1598 params2.bounds = gfx::Rect(110, 10, 100, 100);
1599 child2->Init(std::move(params2));
1600 child2->SetContentsView(std::make_unique<MouseView>());
1601
1602 top_level->Show();
1603 RunPendingMessages();
1604
1605 // Click on child1.
1606 ui::test::EventGenerator generator(GetRootWindow(top_level.get()),
1607 child1->GetNativeWindow());
1608 generator.PressLeftButton();
1609
1610 EXPECT_FALSE(top_level->HasCapture());
1611 EXPECT_TRUE(child1->HasCapture());
1612 EXPECT_FALSE(child2->HasCapture());
1613
1614 generator.ReleaseLeftButton();
1615 EXPECT_FALSE(top_level->HasCapture());
1616 EXPECT_FALSE(child1->HasCapture());
1617 EXPECT_FALSE(child2->HasCapture());
1618
1619 // Click on child2.
1620 generator.SetTargetWindow(child2->GetNativeWindow());
1621 generator.set_current_screen_location(
1622 generator.delegate()->CenterOfWindow(child2->GetNativeWindow()));
1623 generator.PressLeftButton();
1624
1625 EXPECT_FALSE(top_level->HasCapture());
1626 EXPECT_FALSE(child1->HasCapture());
1627 EXPECT_TRUE(child2->HasCapture());
1628
1629 generator.ReleaseLeftButton();
1630 EXPECT_FALSE(top_level->HasCapture());
1631 EXPECT_FALSE(child1->HasCapture());
1632 EXPECT_FALSE(child2->HasCapture());
1633
1634 // Click on top_level.
1635 generator.SetTargetWindow(top_level->GetNativeWindow());
1636 generator.set_current_screen_location(gfx::Point());
1637 generator.PressLeftButton();
1638
1639 EXPECT_TRUE(top_level->HasCapture());
1640 EXPECT_FALSE(child1->HasCapture());
1641 EXPECT_FALSE(child2->HasCapture());
1642
1643 generator.ReleaseLeftButton();
1644 EXPECT_FALSE(top_level->HasCapture());
1645 EXPECT_FALSE(child1->HasCapture());
1646 EXPECT_FALSE(child2->HasCapture());
1647 }
1648
1649 // Disabled on Mac. Desktop Mac doesn't have system modal windows since Carbon
1650 // was deprecated. It does have application modal windows, but only Ash requests
1651 // those.
1652 #if defined(OS_APPLE)
1653 #define MAYBE_SystemModalWindowReleasesCapture \
1654 DISABLED_SystemModalWindowReleasesCapture
1655 #elif defined(OS_CHROMEOS)
1656 // Investigate enabling for Chrome OS. It probably requires help from the window
1657 // service.
1658 #define MAYBE_SystemModalWindowReleasesCapture \
1659 DISABLED_SystemModalWindowReleasesCapture
1660 #else
1661 #define MAYBE_SystemModalWindowReleasesCapture SystemModalWindowReleasesCapture
1662 #endif
1663
1664 // Test that when opening a system-modal window, capture is released.
1665 TEST_F(WidgetCaptureTest, MAYBE_SystemModalWindowReleasesCapture) {
1666 TestWidgetFocusChangeListener focus_listener;
1667 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
1668
1669 // Create a top level widget.
1670 Widget top_level_widget;
1671 Widget::InitParams init_params =
1672 CreateParams(Widget::InitParams::TYPE_WINDOW);
1673 init_params.show_state = ui::SHOW_STATE_NORMAL;
1674 gfx::Rect initial_bounds(0, 0, 500, 500);
1675 init_params.bounds = initial_bounds;
1676 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1677 top_level_widget.Init(std::move(init_params));
1678 ShowSync(&top_level_widget);
1679
1680 ASSERT_FALSE(focus_listener.focus_changes().empty());
1681 EXPECT_EQ(top_level_widget.GetNativeView(),
1682 focus_listener.focus_changes().back());
1683
1684 EXPECT_FALSE(top_level_widget.HasCapture());
1685 top_level_widget.SetCapture(nullptr);
1686 EXPECT_TRUE(top_level_widget.HasCapture());
1687
1688 // Create a modal dialog.
1689 auto dialog_delegate = std::make_unique<DialogDelegateView>();
1690 dialog_delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
1691
1692 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
1693 dialog_delegate.release(), nullptr, top_level_widget.GetNativeView());
1694 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
1695 ShowSync(modal_dialog_widget);
1696
1697 EXPECT_FALSE(top_level_widget.HasCapture());
1698
1699 modal_dialog_widget->CloseNow();
1700 top_level_widget.CloseNow();
1701 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
1702 }
1703
1704 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1705 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1706 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1707 #if defined(OS_CHROMEOS) || defined(OS_APPLE)
1708 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1709 #else
1710 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1711 #endif
1712
1713 // Test that a synthetic mouse exit is sent to the widget which was handling
1714 // mouse events when a different widget grabs capture. Except for Windows,
1715 // which does not send a synthetic mouse exit.
1716 TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
1717 Widget widget1;
1718 Widget::InitParams params1 =
1719 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1720 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1721 widget1.Init(std::move(params1));
1722 MouseView* mouse_view1 =
1723 widget1.SetContentsView(std::make_unique<MouseView>());
1724 widget1.Show();
1725 widget1.SetBounds(gfx::Rect(300, 300));
1726
1727 Widget widget2;
1728 Widget::InitParams params2 =
1729 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1730 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1731 widget2.Init(std::move(params2));
1732 widget2.Show();
1733 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1734
1735 ui::test::EventGenerator generator(GetRootWindow(&widget1));
1736 generator.set_current_screen_location(gfx::Point(100, 100));
1737 generator.MoveMouseBy(0, 0);
1738
1739 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1740 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1741
1742 widget2.SetCapture(nullptr);
1743 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1744 // On Windows, Chrome doesn't synthesize a separate mouse exited event.
1745 // Instead, it uses ::TrackMouseEvent to get notified of the mouse leaving.
1746 // Calling SetCapture does not cause Windows to generate a WM_MOUSELEAVE
1747 // event. See WindowEventDispatcher::OnOtherRootGotCapture() for more info.
1748 #if defined(OS_WIN)
1749 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1750 #else
1751 EXPECT_EQ(1, mouse_view1->ExitedCalls());
1752 #endif // OS_WIN
1753 }
1754
1755 namespace {
1756
1757 // Widget observer which grabs capture when the widget is activated.
1758 class CaptureOnActivationObserver : public WidgetObserver {
1759 public:
1760 CaptureOnActivationObserver() = default;
1761 ~CaptureOnActivationObserver() override = default;
1762
1763 // WidgetObserver:
1764 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1765 if (active) {
1766 widget->SetCapture(nullptr);
1767 activation_observed_ = true;
1768 }
1769 }
1770
1771 bool activation_observed() const { return activation_observed_; }
1772
1773 private:
1774 bool activation_observed_ = false;
1775
1776 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1777 };
1778
1779 } // namespace
1780
1781 // Test that setting capture on widget activation of a non-toplevel widget
1782 // (e.g. a bubble on Linux) succeeds.
1783 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1784 Widget toplevel;
1785 Widget::InitParams toplevel_params =
1786 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1787 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1788 toplevel.Init(std::move(toplevel_params));
1789 toplevel.Show();
1790
1791 Widget* child = new Widget;
1792 Widget::InitParams child_params =
1793 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1794 child_params.parent = toplevel.GetNativeView();
1795 child_params.context = toplevel.GetNativeWindow();
1796 child->Init(std::move(child_params));
1797
1798 CaptureOnActivationObserver observer;
1799 child->AddObserver(&observer);
1800 child->Show();
1801
1802 #if defined(OS_APPLE)
1803 // On Mac, activation is asynchronous. A single trip to the runloop should be
1804 // sufficient. On Aura platforms, note that since the child widget isn't top-
1805 // level, the aura window manager gets asked whether the widget is active, not
1806 // the OS.
1807 base::RunLoop().RunUntilIdle();
1808 #endif
1809
1810 EXPECT_TRUE(observer.activation_observed());
1811 EXPECT_TRUE(child->HasCapture());
1812
1813 child->RemoveObserver(&observer);
1814 }
1815
1816 #if defined(OS_WIN)
1817 namespace {
1818
1819 // Used to verify OnMouseEvent() has been invoked.
1820 class MouseEventTrackingWidget : public Widget {
1821 public:
1822 MouseEventTrackingWidget() = default;
1823 ~MouseEventTrackingWidget() override = default;
1824
1825 bool GetAndClearGotMouseEvent() {
1826 bool value = got_mouse_event_;
1827 got_mouse_event_ = false;
1828 return value;
1829 }
1830
1831 // Widget:
1832 void OnMouseEvent(ui::MouseEvent* event) override {
1833 got_mouse_event_ = true;
1834 Widget::OnMouseEvent(event);
1835 }
1836
1837 private:
1838 bool got_mouse_event_ = false;
1839
1840 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1841 };
1842
1843 } // namespace
1844
1845 // Verifies if a mouse event is received on a widget that doesn't have capture
1846 // on Windows that it is correctly processed by the widget that doesn't have
1847 // capture. This behavior is not desired on OSes other than Windows.
1848 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1849 MouseEventTrackingWidget widget1;
1850 Widget::InitParams params1 =
1851 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1852 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1853 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1854 widget1.Init(std::move(params1));
1855 widget1.Show();
1856
1857 MouseEventTrackingWidget widget2;
1858 Widget::InitParams params2 =
1859 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1860 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1861 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1862 widget2.Init(std::move(params2));
1863 widget2.Show();
1864
1865 // Set capture to widget2 and verity it gets it.
1866 widget2.SetCapture(widget2.GetRootView());
1867 EXPECT_FALSE(widget1.HasCapture());
1868 EXPECT_TRUE(widget2.HasCapture());
1869
1870 widget1.GetAndClearGotMouseEvent();
1871 widget2.GetAndClearGotMouseEvent();
1872 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1873 // |widget2| has capture, |widget1| should still get the event.
1874 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1875 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
1876 ui::EventDispatchDetails details =
1877 widget1.GetNativeWindow()->GetHost()->event_sink()->OnEventFromSource(
1878 &mouse_event);
1879 ASSERT_FALSE(details.dispatcher_destroyed);
1880 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1881 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1882 }
1883 #endif // defined(OS_WIN)
1884
1885 class WidgetInputMethodInteractiveTest : public DesktopWidgetTestInteractive {
1886 public:
1887 WidgetInputMethodInteractiveTest() = default;
1888
1889 // testing::Test:
1890 void SetUp() override {
1891 DesktopWidgetTestInteractive::SetUp();
1892 #if defined(OS_WIN)
1893 // On Windows, Widget::Deactivate() works by activating the next topmost
1894 // window on the z-order stack. This only works if there is at least one
1895 // other window, so make sure that is the case.
1896 deactivate_widget_ = CreateTopLevelNativeWidget();
1897 deactivate_widget_->Show();
1898 #endif
1899 }
1900
1901 void TearDown() override {
1902 if (deactivate_widget_)
1903 deactivate_widget_->CloseNow();
1904 DesktopWidgetTestInteractive::TearDown();
1905 }
1906
1907 private:
1908 Widget* deactivate_widget_ = nullptr;
1909
1910 DISALLOW_COPY_AND_ASSIGN(WidgetInputMethodInteractiveTest);
1911 };
1912
1913 #if defined(OS_APPLE)
1914 #define MAYBE_Activation DISABLED_Activation
1915 #else
1916 #define MAYBE_Activation Activation
1917 #endif
1918 // Test input method focus changes affected by top window activaction.
1919 TEST_F(WidgetInputMethodInteractiveTest, MAYBE_Activation) {
1920 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1921 Textfield* textfield = new Textfield;
1922 widget->GetRootView()->AddChildView(textfield);
1923 textfield->RequestFocus();
1924
1925 ShowSync(widget.get());
1926
1927 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1928 widget->GetInputMethod()->GetTextInputType());
1929
1930 DeactivateSync(widget.get());
1931
1932 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1933 widget->GetInputMethod()->GetTextInputType());
1934 }
1935
1936 // Test input method focus changes affected by focus changes within 1 window.
1937 TEST_F(WidgetInputMethodInteractiveTest, OneWindow) {
1938 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
1939 Textfield* textfield1 = new Textfield;
1940 Textfield* textfield2 = new Textfield;
1941 textfield2->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1942 widget->GetRootView()->AddChildView(textfield1);
1943 widget->GetRootView()->AddChildView(textfield2);
1944
1945 ShowSync(widget.get());
1946
1947 textfield1->RequestFocus();
1948 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1949 widget->GetInputMethod()->GetTextInputType());
1950
1951 textfield2->RequestFocus();
1952 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1953 widget->GetInputMethod()->GetTextInputType());
1954
1955 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
1956 // DNWA (which just activates the last active window) and involves the
1957 // AuraTestHelper which sets the input method as DummyInputMethod.
1958 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
1959 DeactivateSync(widget.get());
1960 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
1961 widget->GetInputMethod()->GetTextInputType());
1962
1963 ActivateSync(widget.get());
1964 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1965 widget->GetInputMethod()->GetTextInputType());
1966
1967 DeactivateSync(widget.get());
1968 textfield1->RequestFocus();
1969 ActivateSync(widget.get());
1970 EXPECT_TRUE(widget->IsActive());
1971 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
1972 widget->GetInputMethod()->GetTextInputType());
1973 #endif
1974 }
1975
1976 // Test input method focus changes affected by focus changes cross 2 windows
1977 // which shares the same top window.
1978 TEST_F(WidgetInputMethodInteractiveTest, TwoWindows) {
1979 WidgetAutoclosePtr parent(CreateTopLevelNativeWidget());
1980 parent->SetBounds(gfx::Rect(100, 100, 100, 100));
1981
1982 Widget* child = CreateChildNativeWidgetWithParent(parent.get());
1983 child->SetBounds(gfx::Rect(0, 0, 50, 50));
1984 child->Show();
1985
1986 Textfield* textfield_parent = new Textfield;
1987 Textfield* textfield_child = new Textfield;
1988 textfield_parent->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
1989 parent->GetRootView()->AddChildView(textfield_parent);
1990 child->GetRootView()->AddChildView(textfield_child);
1991 ShowSync(parent.get());
1992
1993 EXPECT_EQ(parent->GetInputMethod(), child->GetInputMethod());
1994
1995 textfield_parent->RequestFocus();
1996 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
1997 parent->GetInputMethod()->GetTextInputType());
1998
1999 textfield_child->RequestFocus();
2000 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
2001 parent->GetInputMethod()->GetTextInputType());
2002
2003 // Widget::Deactivate() doesn't work for CrOS, because it uses NWA instead of
2004 // DNWA (which just activates the last active window) and involves the
2005 // AuraTestHelper which sets the input method as DummyInputMethod.
2006 #if BUILDFLAG(ENABLE_DESKTOP_AURA) || defined(OS_APPLE)
2007 DeactivateSync(parent.get());
2008 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
2009 parent->GetInputMethod()->GetTextInputType());
2010
2011 ActivateSync(parent.get());
2012 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
2013 parent->GetInputMethod()->GetTextInputType());
2014
2015 textfield_parent->RequestFocus();
2016 DeactivateSync(parent.get());
2017 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
2018 parent->GetInputMethod()->GetTextInputType());
2019
2020 ActivateSync(parent.get());
2021 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
2022 parent->GetInputMethod()->GetTextInputType());
2023 #endif
2024 }
2025
2026 // Test input method focus changes affected by textfield's state changes.
2027 TEST_F(WidgetInputMethodInteractiveTest, TextField) {
2028 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
2029 Textfield* textfield = new Textfield;
2030 widget->GetRootView()->AddChildView(textfield);
2031 ShowSync(widget.get());
2032 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
2033 widget->GetInputMethod()->GetTextInputType());
2034
2035 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
2036 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
2037 widget->GetInputMethod()->GetTextInputType());
2038
2039 textfield->RequestFocus();
2040 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD,
2041 widget->GetInputMethod()->GetTextInputType());
2042
2043 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
2044 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT,
2045 widget->GetInputMethod()->GetTextInputType());
2046
2047 textfield->SetReadOnly(true);
2048 EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE,
2049 widget->GetInputMethod()->GetTextInputType());
2050 }
2051
2052 // Test input method should not work for accelerator.
2053 TEST_F(WidgetInputMethodInteractiveTest, AcceleratorInTextfield) {
2054 WidgetAutoclosePtr widget(CreateTopLevelNativeWidget());
2055 Textfield* textfield = new Textfield;
2056 widget->GetRootView()->AddChildView(textfield);
2057 ShowSync(widget.get());
2058 textfield->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
2059 textfield->RequestFocus();
2060
2061 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_F, ui::EF_ALT_DOWN);
2062 ui::Accelerator accelerator(key_event);
2063 widget->GetFocusManager()->RegisterAccelerator(
2064 accelerator, ui::AcceleratorManager::kNormalPriority, textfield);
2065
2066 widget->OnKeyEvent(&key_event);
2067 EXPECT_TRUE(key_event.stopped_propagation());
2068
2069 widget->GetFocusManager()->UnregisterAccelerators(textfield);
2070
2071 ui::KeyEvent key_event2(key_event);
2072 widget->OnKeyEvent(&key_event2);
2073 EXPECT_FALSE(key_event2.stopped_propagation());
2074 }
2075
2076 } // namespace test
2077 } // namespace views
2078