Add ability for NetLogLogger to gather data from more than just NetLog
[chromium-blink-merge.git] / ui / views / widget / widget_interactive_uitest.cc
blob6ac9451c124230c589fd5f0c8006babf0cf74f5a
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/path_service.h"
9 #include "base/run_loop.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/base/ui_base_paths.h"
14 #include "ui/base/ui_base_switches.h"
15 #include "ui/events/event_processor.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/events/test/event_generator.h"
18 #include "ui/gfx/native_widget_types.h"
19 #include "ui/gl/gl_surface.h"
20 #include "ui/views/controls/textfield/textfield.h"
21 #include "ui/views/controls/textfield/textfield_test_api.h"
22 #include "ui/views/focus/focus_manager.h"
23 #include "ui/views/test/focus_manager_test.h"
24 #include "ui/views/test/widget_test.h"
25 #include "ui/views/touchui/touch_selection_controller_impl.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/window/dialog_delegate.h"
28 #include "ui/wm/public/activation_client.h"
30 #if defined(OS_WIN)
31 #include "ui/aura/window.h"
32 #include "ui/aura/window_tree_host.h"
33 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
34 #include "ui/views/win/hwnd_util.h"
35 #endif
37 namespace views {
38 namespace test {
40 namespace {
42 // A View that closes the Widget and exits the current message-loop when it
43 // receives a mouse-release event.
44 class ExitLoopOnRelease : public View {
45 public:
46 ExitLoopOnRelease() {}
47 ~ExitLoopOnRelease() override {}
49 private:
50 // Overridden from View:
51 void OnMouseReleased(const ui::MouseEvent& event) override {
52 GetWidget()->Close();
53 base::MessageLoop::current()->QuitNow();
56 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
59 // A view that does a capture on ui::ET_GESTURE_TAP_DOWN events.
60 class GestureCaptureView : public View {
61 public:
62 GestureCaptureView() {}
63 ~GestureCaptureView() override {}
65 private:
66 // Overridden from View:
67 void OnGestureEvent(ui::GestureEvent* event) override {
68 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
69 GetWidget()->SetCapture(this);
70 event->StopPropagation();
74 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
77 // A view that always processes all mouse events.
78 class MouseView : public View {
79 public:
80 MouseView()
81 : View(),
82 entered_(0),
83 exited_(0),
84 pressed_(0) {
86 ~MouseView() override {}
88 bool OnMousePressed(const ui::MouseEvent& event) override {
89 pressed_++;
90 return true;
93 void OnMouseEntered(const ui::MouseEvent& event) override { entered_++; }
95 void OnMouseExited(const ui::MouseEvent& event) override { exited_++; }
97 // Return the number of OnMouseEntered calls and reset the counter.
98 int EnteredCalls() {
99 int i = entered_;
100 entered_ = 0;
101 return i;
104 // Return the number of OnMouseExited calls and reset the counter.
105 int ExitedCalls() {
106 int i = exited_;
107 exited_ = 0;
108 return i;
111 int pressed() const { return pressed_; }
113 private:
114 int entered_;
115 int exited_;
117 int pressed_;
119 DISALLOW_COPY_AND_ASSIGN(MouseView);
122 // A View that shows a different widget, sets capture on that widget, and
123 // initiates a nested message-loop when it receives a mouse-press event.
124 class NestedLoopCaptureView : public View {
125 public:
126 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
127 ~NestedLoopCaptureView() override {}
129 private:
130 // Overridden from View:
131 bool OnMousePressed(const ui::MouseEvent& event) override {
132 // Start a nested loop.
133 widget_->Show();
134 widget_->SetCapture(widget_->GetContentsView());
135 EXPECT_TRUE(widget_->HasCapture());
137 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
138 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
140 base::RunLoop run_loop;
141 run_loop.Run();
142 return true;
145 Widget* widget_;
147 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
150 // Spins a run loop until a Widget's active state matches a desired state.
151 class WidgetActivationWaiter : public WidgetObserver {
152 public:
153 WidgetActivationWaiter(Widget* widget, bool active) : observed_(false) {
154 // Always expect a change for tests using this.
155 EXPECT_NE(active, widget->IsActive());
156 widget->AddObserver(this);
159 void Wait() {
160 if (!observed_)
161 run_loop_.Run();
164 void OnWidgetActivationChanged(Widget* widget, bool active) override {
165 observed_ = true;
166 widget->RemoveObserver(this);
167 if (run_loop_.running())
168 run_loop_.Quit();
171 private:
172 base::RunLoop run_loop_;
173 bool observed_;
175 DISALLOW_COPY_AND_ASSIGN(WidgetActivationWaiter);
178 ui::WindowShowState GetWidgetShowState(const Widget* widget) {
179 // Use IsMaximized/IsMinimized/IsFullScreen instead of GetWindowPlacement
180 // because the former is implemented on all platforms but the latter is not.
181 return widget->IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN :
182 widget->IsMaximized() ? ui::SHOW_STATE_MAXIMIZED :
183 widget->IsMinimized() ? ui::SHOW_STATE_MINIMIZED :
184 widget->IsActive() ? ui::SHOW_STATE_NORMAL :
185 ui::SHOW_STATE_INACTIVE;
188 // Give the OS an opportunity to process messages for an activation change, when
189 // there is actually no change expected (e.g. ShowInactive()).
190 void RunPendingMessagesForActiveStatusChange() {
191 #if defined(OS_MACOSX)
192 // On Mac, a single spin is *usually* enough. It isn't when a widget is shown
193 // and made active in two steps, so tests should follow up with a ShowSync()
194 // or ActivateSync to ensure a consistent state.
195 base::RunLoop().RunUntilIdle();
196 #endif
197 // TODO(tapted): Check for desktop aura widgets.
200 // Activate a widget, and wait for it to become active. On non-desktop Aura
201 // this is just an activation. For other widgets, it means activating and then
202 // spinning the run loop until the OS has activated the window.
203 void ActivateSync(Widget* widget) {
204 WidgetActivationWaiter waiter(widget, true);
205 widget->Activate();
206 waiter.Wait();
209 // Like for ActivateSync(), wait for a widget to become active, but Show() the
210 // widget rather than calling Activate().
211 void ShowSync(Widget* widget) {
212 WidgetActivationWaiter waiter(widget, true);
213 widget->Show();
214 waiter.Wait();
217 // Calls ShowInactive() on a Widget, and spins a run loop. The goal is to give
218 // the OS a chance to activate a widget. However, for this case, the test
219 // doesn't expect that to happen, so there is nothing to wait for.
220 void ShowInactiveSync(Widget* widget) {
221 widget->ShowInactive();
222 RunPendingMessagesForActiveStatusChange();
225 } // namespace
227 class WidgetTestInteractive : public WidgetTest {
228 public:
229 WidgetTestInteractive() {}
230 ~WidgetTestInteractive() override {}
232 void SetUp() override {
233 gfx::GLSurface::InitializeOneOffForTests();
234 ui::RegisterPathProvider();
235 base::FilePath ui_test_pak_path;
236 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
237 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
238 WidgetTest::SetUp();
241 protected:
242 static void ShowQuickMenuImmediately(
243 TouchSelectionControllerImpl* controller) {
244 DCHECK(controller);
245 if (controller->context_menu_timer_.IsRunning()) {
246 controller->context_menu_timer_.Stop();
247 // TODO(tapted): Enable this when porting ui/views/touchui to Mac.
248 #if !defined(OS_MACOSX)
249 controller->ContextMenuTimerFired();
250 #endif
254 static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
255 DCHECK(controller);
256 return controller->context_menu_ && controller->context_menu_->visible();
260 #if defined(OS_WIN)
261 // Tests whether activation and focus change works correctly in Windows.
262 // We test the following:-
263 // 1. If the active aura window is correctly set when a top level widget is
264 // created.
265 // 2. If the active aura window in widget 1 created above, is set to NULL when
266 // another top level widget is created and focused.
267 // 3. On focusing the native platform window for widget 1, the active aura
268 // window for widget 1 should be set and that for widget 2 should reset.
269 // TODO(ananta): Discuss with erg on how to write this test for linux x11 aura.
270 TEST_F(WidgetTestInteractive, DesktopNativeWidgetAuraActivationAndFocusTest) {
271 // Create widget 1 and expect the active window to be its window.
272 View* contents_view1 = new View;
273 contents_view1->SetFocusable(true);
274 Widget widget1;
275 Widget::InitParams init_params =
276 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
277 init_params.bounds = gfx::Rect(0, 0, 200, 200);
278 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
279 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
280 widget1.Init(init_params);
281 widget1.SetContentsView(contents_view1);
282 widget1.Show();
283 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
284 contents_view1->RequestFocus();
286 EXPECT_TRUE(root_window1 != NULL);
287 aura::client::ActivationClient* activation_client1 =
288 aura::client::GetActivationClient(root_window1);
289 EXPECT_TRUE(activation_client1 != NULL);
290 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
292 // Create widget 2 and expect the active window to be its window.
293 View* contents_view2 = new View;
294 Widget widget2;
295 Widget::InitParams init_params2 =
296 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
297 init_params2.bounds = gfx::Rect(0, 0, 200, 200);
298 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
299 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
300 widget2.Init(init_params2);
301 widget2.SetContentsView(contents_view2);
302 widget2.Show();
303 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
304 contents_view2->RequestFocus();
305 ::SetActiveWindow(
306 root_window2->GetHost()->GetAcceleratedWidget());
308 aura::client::ActivationClient* activation_client2 =
309 aura::client::GetActivationClient(root_window2);
310 EXPECT_TRUE(activation_client2 != NULL);
311 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
312 EXPECT_EQ(activation_client1->GetActiveWindow(),
313 reinterpret_cast<aura::Window*>(NULL));
315 // Now set focus back to widget 1 and expect the active window to be its
316 // window.
317 contents_view1->RequestFocus();
318 ::SetActiveWindow(
319 root_window1->GetHost()->GetAcceleratedWidget());
320 EXPECT_EQ(activation_client2->GetActiveWindow(),
321 reinterpret_cast<aura::Window*>(NULL));
322 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
324 #endif // defined(OS_WIN)
326 TEST_F(WidgetTestInteractive, CaptureAutoReset) {
327 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
328 View* container = new View;
329 toplevel->SetContentsView(container);
331 EXPECT_FALSE(toplevel->HasCapture());
332 toplevel->SetCapture(NULL);
333 EXPECT_TRUE(toplevel->HasCapture());
335 // By default, mouse release removes capture.
336 gfx::Point click_location(45, 15);
337 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
338 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
339 ui::EF_LEFT_MOUSE_BUTTON);
340 toplevel->OnMouseEvent(&release);
341 EXPECT_FALSE(toplevel->HasCapture());
343 // Now a mouse release shouldn't remove capture.
344 toplevel->set_auto_release_capture(false);
345 toplevel->SetCapture(NULL);
346 EXPECT_TRUE(toplevel->HasCapture());
347 toplevel->OnMouseEvent(&release);
348 EXPECT_TRUE(toplevel->HasCapture());
349 toplevel->ReleaseCapture();
350 EXPECT_FALSE(toplevel->HasCapture());
352 toplevel->Close();
353 RunPendingMessages();
356 TEST_F(WidgetTestInteractive, ResetCaptureOnGestureEnd) {
357 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
358 View* container = new View;
359 toplevel->SetContentsView(container);
361 View* gesture = new GestureCaptureView;
362 gesture->SetBounds(0, 0, 30, 30);
363 container->AddChildView(gesture);
365 MouseView* mouse = new MouseView;
366 mouse->SetBounds(30, 0, 30, 30);
367 container->AddChildView(mouse);
369 toplevel->SetSize(gfx::Size(100, 100));
370 toplevel->Show();
372 // Start a gesture on |gesture|.
373 ui::GestureEvent tap_down(15,
376 base::TimeDelta(),
377 ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
378 ui::GestureEvent end(15,
381 base::TimeDelta(),
382 ui::GestureEventDetails(ui::ET_GESTURE_END));
383 toplevel->OnGestureEvent(&tap_down);
385 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
386 // will not receive the event.
387 gfx::Point click_location(45, 15);
389 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
390 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
391 ui::EF_LEFT_MOUSE_BUTTON);
392 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
393 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
394 ui::EF_LEFT_MOUSE_BUTTON);
396 EXPECT_TRUE(toplevel->HasCapture());
398 toplevel->OnMouseEvent(&press);
399 toplevel->OnMouseEvent(&release);
400 EXPECT_EQ(0, mouse->pressed());
402 EXPECT_FALSE(toplevel->HasCapture());
404 // The end of the gesture should release the capture, and pressing on |mouse|
405 // should now reach |mouse|.
406 toplevel->OnGestureEvent(&end);
407 toplevel->OnMouseEvent(&press);
408 toplevel->OnMouseEvent(&release);
409 EXPECT_EQ(1, mouse->pressed());
411 toplevel->Close();
412 RunPendingMessages();
415 // Checks that if a mouse-press triggers a capture on a different widget (which
416 // consumes the mouse-release event), then the target of the press does not have
417 // capture.
418 TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) {
419 // The test creates two widgets: |first| and |second|.
420 // The View in |first| makes |second| visible, sets capture on it, and starts
421 // a nested loop (like a menu does). The View in |second| terminates the
422 // nested loop and closes the widget.
423 // The test sends a mouse-press event to |first|, and posts a task to send a
424 // release event to |second|, to make sure that the release event is
425 // dispatched after the nested loop starts.
427 Widget* first = CreateTopLevelFramelessPlatformWidget();
428 Widget* second = CreateTopLevelFramelessPlatformWidget();
430 View* container = new NestedLoopCaptureView(second);
431 first->SetContentsView(container);
433 second->SetContentsView(new ExitLoopOnRelease());
435 first->SetSize(gfx::Size(100, 100));
436 first->Show();
438 gfx::Point location(20, 20);
439 base::MessageLoop::current()->PostTask(
440 FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second),
441 base::Owned(new ui::MouseEvent(
442 ui::ET_MOUSE_RELEASED, location, location,
443 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
444 ui::EF_LEFT_MOUSE_BUTTON))));
445 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
446 ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
447 ui::EF_LEFT_MOUSE_BUTTON);
448 first->OnMouseEvent(&press);
449 EXPECT_FALSE(first->HasCapture());
450 first->Close();
451 RunPendingMessages();
454 // Tests some grab/ungrab events.
455 // TODO(estade): can this be enabled now that this is an interactive ui test?
456 TEST_F(WidgetTestInteractive, DISABLED_GrabUngrab) {
457 Widget* toplevel = CreateTopLevelPlatformWidget();
458 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
459 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
461 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
463 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
464 View* view = new MouseView();
465 view->SetBounds(0, 0, 300, 300);
466 child1->GetRootView()->AddChildView(view);
468 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
469 view = new MouseView();
470 view->SetBounds(0, 0, 200, 200);
471 child2->GetRootView()->AddChildView(view);
473 toplevel->Show();
474 RunPendingMessages();
476 // Click on child1
477 gfx::Point p1(45, 45);
478 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
479 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
480 toplevel->OnMouseEvent(&pressed);
482 EXPECT_TRUE(toplevel->HasCapture());
483 EXPECT_TRUE(child1->HasCapture());
484 EXPECT_FALSE(child2->HasCapture());
486 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(),
487 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
488 toplevel->OnMouseEvent(&released);
490 EXPECT_FALSE(toplevel->HasCapture());
491 EXPECT_FALSE(child1->HasCapture());
492 EXPECT_FALSE(child2->HasCapture());
494 RunPendingMessages();
496 // Click on child2
497 gfx::Point p2(315, 45);
498 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(),
499 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
500 toplevel->OnMouseEvent(&pressed2);
501 EXPECT_TRUE(pressed2.handled());
502 EXPECT_TRUE(toplevel->HasCapture());
503 EXPECT_TRUE(child2->HasCapture());
504 EXPECT_FALSE(child1->HasCapture());
506 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(),
507 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
508 toplevel->OnMouseEvent(&released2);
509 EXPECT_FALSE(toplevel->HasCapture());
510 EXPECT_FALSE(child1->HasCapture());
511 EXPECT_FALSE(child2->HasCapture());
513 toplevel->CloseNow();
516 // Tests mouse move outside of the window into the "resize controller" and back
517 // will still generate an OnMouseEntered and OnMouseExited event..
518 TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) {
519 Widget* toplevel = CreateTopLevelPlatformWidget();
521 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
523 MouseView* view = new MouseView();
524 view->SetBounds(90, 90, 10, 10);
525 toplevel->GetRootView()->AddChildView(view);
527 toplevel->Show();
528 RunPendingMessages();
530 // Move to an outside position.
531 gfx::Point p1(200, 200);
532 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
533 ui::EF_NONE, ui::EF_NONE);
534 toplevel->OnMouseEvent(&moved_out);
535 EXPECT_EQ(0, view->EnteredCalls());
536 EXPECT_EQ(0, view->ExitedCalls());
538 // Move onto the active view.
539 gfx::Point p2(95, 95);
540 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
541 ui::EF_NONE, ui::EF_NONE);
542 toplevel->OnMouseEvent(&moved_over);
543 EXPECT_EQ(1, view->EnteredCalls());
544 EXPECT_EQ(0, view->ExitedCalls());
546 // Move onto the outer resizing border.
547 gfx::Point p3(102, 95);
548 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
549 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
550 toplevel->OnMouseEvent(&moved_resizer);
551 EXPECT_EQ(0, view->EnteredCalls());
552 EXPECT_EQ(1, view->ExitedCalls());
554 // Move onto the view again.
555 toplevel->OnMouseEvent(&moved_over);
556 EXPECT_EQ(1, view->EnteredCalls());
557 EXPECT_EQ(0, view->ExitedCalls());
559 RunPendingMessages();
561 toplevel->CloseNow();
564 // Test view focus restoration when a widget is deactivated and re-activated.
565 TEST_F(WidgetTestInteractive, ViewFocusOnWidgetActivationChanges) {
566 Widget* widget1 = CreateTopLevelPlatformWidget();
567 View* view1 = new View;
568 view1->SetFocusable(true);
569 widget1->GetContentsView()->AddChildView(view1);
571 Widget* widget2 = CreateTopLevelPlatformWidget();
572 View* view2a = new View;
573 View* view2b = new View;
574 view2a->SetFocusable(true);
575 view2b->SetFocusable(true);
576 widget2->GetContentsView()->AddChildView(view2a);
577 widget2->GetContentsView()->AddChildView(view2b);
579 ShowSync(widget1);
580 EXPECT_TRUE(widget1->IsActive());
581 view1->RequestFocus();
582 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
584 ShowSync(widget2);
585 EXPECT_TRUE(widget2->IsActive());
586 EXPECT_FALSE(widget1->IsActive());
587 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
588 view2a->RequestFocus();
589 EXPECT_EQ(view2a, widget2->GetFocusManager()->GetFocusedView());
590 view2b->RequestFocus();
591 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
593 ActivateSync(widget1);
594 EXPECT_TRUE(widget1->IsActive());
595 EXPECT_EQ(view1, widget1->GetFocusManager()->GetFocusedView());
596 EXPECT_FALSE(widget2->IsActive());
597 EXPECT_EQ(NULL, widget2->GetFocusManager()->GetFocusedView());
599 ActivateSync(widget2);
600 EXPECT_TRUE(widget2->IsActive());
601 EXPECT_EQ(view2b, widget2->GetFocusManager()->GetFocusedView());
602 EXPECT_FALSE(widget1->IsActive());
603 EXPECT_EQ(NULL, widget1->GetFocusManager()->GetFocusedView());
605 widget1->CloseNow();
606 widget2->CloseNow();
609 #if defined(OS_WIN)
611 // Test view focus retention when a widget's HWND is disabled and re-enabled.
612 TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) {
613 Widget* widget = CreateTopLevelFramelessPlatformWidget();
614 widget->SetContentsView(new View);
615 for (size_t i = 0; i < 2; ++i) {
616 widget->GetContentsView()->AddChildView(new View);
617 widget->GetContentsView()->child_at(i)->SetFocusable(true);
620 widget->Show();
621 const HWND hwnd = HWNDForWidget(widget);
622 EXPECT_TRUE(::IsWindow(hwnd));
623 EXPECT_TRUE(::IsWindowEnabled(hwnd));
624 EXPECT_EQ(hwnd, ::GetActiveWindow());
626 for (int i = 0; i < widget->GetContentsView()->child_count(); ++i) {
627 SCOPED_TRACE(base::StringPrintf("Child view %d", i));
628 View* view = widget->GetContentsView()->child_at(i);
630 view->RequestFocus();
631 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
632 EXPECT_FALSE(::EnableWindow(hwnd, FALSE));
633 EXPECT_FALSE(::IsWindowEnabled(hwnd));
635 // Oddly, disabling the HWND leaves it active with the focus unchanged.
636 EXPECT_EQ(hwnd, ::GetActiveWindow());
637 EXPECT_TRUE(widget->IsActive());
638 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
640 EXPECT_TRUE(::EnableWindow(hwnd, TRUE));
641 EXPECT_TRUE(::IsWindowEnabled(hwnd));
642 EXPECT_EQ(hwnd, ::GetActiveWindow());
643 EXPECT_TRUE(widget->IsActive());
644 EXPECT_EQ(view, widget->GetFocusManager()->GetFocusedView());
647 widget->CloseNow();
650 // This class subclasses the Widget class to listen for activation change
651 // notifications and provides accessors to return information as to whether
652 // the widget is active. We need this to ensure that users of the widget
653 // class activate the widget only when the underlying window becomes really
654 // active. Previously we would activate the widget in the WM_NCACTIVATE
655 // message which is incorrect because APIs like FlashWindowEx flash the
656 // window caption by sending fake WM_NCACTIVATE messages.
657 class WidgetActivationTest : public Widget {
658 public:
659 WidgetActivationTest()
660 : active_(false) {}
662 virtual ~WidgetActivationTest() {}
664 virtual void OnNativeWidgetActivationChanged(bool active) override {
665 active_ = active;
668 bool active() const { return active_; }
670 private:
671 bool active_;
673 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
676 // Tests whether the widget only becomes active when the underlying window
677 // is really active.
678 TEST_F(WidgetTestInteractive, WidgetNotActivatedOnFakeActivationMessages) {
679 WidgetActivationTest widget1;
680 Widget::InitParams init_params =
681 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
682 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
683 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
684 init_params.bounds = gfx::Rect(0, 0, 200, 200);
685 widget1.Init(init_params);
686 widget1.Show();
687 EXPECT_EQ(true, widget1.active());
689 WidgetActivationTest widget2;
690 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
691 widget2.Init(init_params);
692 widget2.Show();
693 EXPECT_EQ(true, widget2.active());
694 EXPECT_EQ(false, widget1.active());
696 HWND win32_native_window1 = HWNDForWidget(&widget1);
697 EXPECT_TRUE(::IsWindow(win32_native_window1));
699 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
700 EXPECT_EQ(false, widget1.active());
701 EXPECT_EQ(true, widget2.active());
703 ::SetActiveWindow(win32_native_window1);
704 EXPECT_EQ(true, widget1.active());
705 EXPECT_EQ(false, widget2.active());
707 #endif // defined(OS_WIN)
709 #if !defined(OS_CHROMEOS)
710 // Provides functionality to create a window modal dialog.
711 class ModalDialogDelegate : public DialogDelegateView {
712 public:
713 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
714 ~ModalDialogDelegate() override {}
716 // WidgetDelegate overrides.
717 ui::ModalType GetModalType() const override { return type_; }
719 private:
720 ui::ModalType type_;
722 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
725 // Tests whether the focused window is set correctly when a modal window is
726 // created and destroyed. When it is destroyed it should focus the owner window.
727 TEST_F(WidgetTestInteractive, WindowModalWindowDestroyedActivationTest) {
728 TestWidgetFocusChangeListener focus_listener;
729 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
730 const std::vector<gfx::NativeView>& focus_changes =
731 focus_listener.focus_changes();
733 // Create a top level widget.
734 Widget top_level_widget;
735 Widget::InitParams init_params =
736 CreateParams(Widget::InitParams::TYPE_WINDOW);
737 init_params.show_state = ui::SHOW_STATE_NORMAL;
738 gfx::Rect initial_bounds(0, 0, 500, 500);
739 init_params.bounds = initial_bounds;
740 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
741 init_params.native_widget =
742 new PlatformDesktopNativeWidget(&top_level_widget);
743 top_level_widget.Init(init_params);
744 top_level_widget.Show();
746 gfx::NativeView top_level_native_view = top_level_widget.GetNativeView();
747 ASSERT_FALSE(focus_listener.focus_changes().empty());
748 EXPECT_EQ(1u, focus_changes.size());
749 EXPECT_EQ(top_level_native_view, focus_changes[0]);
751 // Create a modal dialog.
752 // This instance will be destroyed when the dialog is destroyed.
753 ModalDialogDelegate* dialog_delegate =
754 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
756 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
757 dialog_delegate, NULL, top_level_widget.GetNativeView());
758 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
759 modal_dialog_widget->Show();
761 gfx::NativeView modal_native_view = modal_dialog_widget->GetNativeView();
762 EXPECT_EQ(3u, focus_changes.size());
763 EXPECT_EQ(nullptr, focus_changes[1]);
764 EXPECT_EQ(modal_native_view, focus_changes[2]);
766 modal_dialog_widget->CloseNow();
768 EXPECT_EQ(5u, focus_changes.size());
769 EXPECT_EQ(nullptr, focus_changes[3]);
770 EXPECT_EQ(top_level_native_view, focus_changes[4]);
772 top_level_widget.CloseNow();
773 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
776 // Test that when opening a system-modal window, capture is released.
777 TEST_F(WidgetTestInteractive, SystemModalWindowReleasesCapture) {
778 TestWidgetFocusChangeListener focus_listener;
779 WidgetFocusManager::GetInstance()->AddFocusChangeListener(&focus_listener);
781 // Create a top level widget.
782 Widget top_level_widget;
783 Widget::InitParams init_params =
784 CreateParams(Widget::InitParams::TYPE_WINDOW);
785 init_params.show_state = ui::SHOW_STATE_NORMAL;
786 gfx::Rect initial_bounds(0, 0, 500, 500);
787 init_params.bounds = initial_bounds;
788 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
789 init_params.native_widget =
790 new PlatformDesktopNativeWidget(&top_level_widget);
791 top_level_widget.Init(init_params);
792 ShowSync(&top_level_widget);
794 ASSERT_FALSE(focus_listener.focus_changes().empty());
795 EXPECT_EQ(top_level_widget.GetNativeView(),
796 focus_listener.focus_changes().back());;
798 EXPECT_FALSE(top_level_widget.HasCapture());
799 top_level_widget.SetCapture(NULL);
800 EXPECT_TRUE(top_level_widget.HasCapture());
802 // Create a modal dialog.
803 ModalDialogDelegate* dialog_delegate =
804 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
806 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
807 dialog_delegate, NULL, top_level_widget.GetNativeView());
808 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
809 ShowSync(modal_dialog_widget);
811 EXPECT_FALSE(top_level_widget.HasCapture());
813 modal_dialog_widget->CloseNow();
814 top_level_widget.CloseNow();
815 WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(&focus_listener);
818 #endif // !defined(OS_CHROMEOS)
820 TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
821 Widget widget;
822 Widget::InitParams init_params =
823 CreateParams(Widget::InitParams::TYPE_WINDOW);
824 init_params.bounds = gfx::Rect(0, 0, 200, 200);
825 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
826 init_params.activatable = Widget::InitParams::ACTIVATABLE_NO;
827 #if !defined(OS_CHROMEOS)
828 init_params.native_widget = new PlatformDesktopNativeWidget(&widget);
829 #endif // !defined(OS_CHROMEOS)
830 widget.Init(init_params);
832 widget.Show();
833 EXPECT_FALSE(widget.IsActive());
836 // No touch on desktop Mac. Tracked in http://crbug.com/445520.
837 #if defined(OS_MACOSX) && !defined(USE_AURA)
838 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
839 DISABLED_TouchSelectionQuickMenuIsNotActivated
840 #else
841 #define MAYBE_TouchSelectionQuickMenuIsNotActivated \
842 TouchSelectionQuickMenuIsNotActivated
843 #endif
845 // Test that touch selection quick menu is not activated when opened.
846 TEST_F(WidgetTestInteractive, MAYBE_TouchSelectionQuickMenuIsNotActivated) {
847 base::CommandLine::ForCurrentProcess()->AppendSwitch(
848 switches::kEnableTouchEditing);
849 #if defined(OS_WIN)
850 views_delegate().set_use_desktop_native_widgets(true);
851 #endif // !defined(OS_WIN)
853 Widget widget;
854 Widget::InitParams init_params =
855 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
856 init_params.bounds = gfx::Rect(0, 0, 200, 200);
857 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
858 widget.Init(init_params);
860 Textfield* textfield = new Textfield;
861 textfield->SetBounds(0, 0, 200, 20);
862 textfield->SetText(base::ASCIIToUTF16("some text"));
863 widget.GetRootView()->AddChildView(textfield);
865 widget.Show();
866 textfield->RequestFocus();
867 textfield->SelectAll(true);
868 TextfieldTestApi textfield_test_api(textfield);
870 RunPendingMessages();
872 ui::test::EventGenerator generator(widget.GetNativeWindow());
873 generator.GestureTapAt(gfx::Point(10, 10));
874 ShowQuickMenuImmediately(static_cast<TouchSelectionControllerImpl*>(
875 textfield_test_api.touch_selection_controller()));
877 EXPECT_TRUE(textfield->HasFocus());
878 EXPECT_TRUE(widget.IsActive());
879 EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>(
880 textfield_test_api.touch_selection_controller())));
883 TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
884 #if defined(OS_WIN)
885 views_delegate().set_use_desktop_native_widgets(true);
886 #endif // !defined(OS_WIN)
888 // Create first widget and view, activate the widget, and focus the view.
889 Widget widget1;
890 Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_POPUP);
891 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
892 params1.activatable = Widget::InitParams::ACTIVATABLE_YES;
893 widget1.Init(params1);
895 View* view1 = new View();
896 view1->SetFocusable(true);
897 widget1.GetRootView()->AddChildView(view1);
899 ActivateSync(&widget1);
901 FocusManager* focus_manager1 = widget1.GetFocusManager();
902 ASSERT_TRUE(focus_manager1);
903 focus_manager1->SetFocusedView(view1);
904 EXPECT_EQ(view1, focus_manager1->GetFocusedView());
906 // Create second widget and view, activate the widget, and focus the view.
907 Widget widget2;
908 Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_POPUP);
909 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
910 params2.activatable = Widget::InitParams::ACTIVATABLE_YES;
911 widget2.Init(params2);
913 View* view2 = new View();
914 view2->SetFocusable(true);
915 widget2.GetRootView()->AddChildView(view2);
917 ActivateSync(&widget2);
918 EXPECT_TRUE(widget2.IsActive());
919 EXPECT_FALSE(widget1.IsActive());
921 FocusManager* focus_manager2 = widget2.GetFocusManager();
922 ASSERT_TRUE(focus_manager2);
923 focus_manager2->SetFocusedView(view2);
924 EXPECT_EQ(view2, focus_manager2->GetFocusedView());
926 // Disable the first view and make sure it loses focus, but its widget is not
927 // activated.
928 view1->SetEnabled(false);
929 EXPECT_NE(view1, focus_manager1->GetFocusedView());
930 EXPECT_FALSE(widget1.IsActive());
931 EXPECT_TRUE(widget2.IsActive());
934 TEST_F(WidgetTestInteractive, ShowCreatesActiveWindow) {
935 Widget* widget = CreateTopLevelPlatformWidget();
937 ShowSync(widget);
938 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
940 widget->CloseNow();
943 TEST_F(WidgetTestInteractive, ShowInactive) {
944 Widget* widget = CreateTopLevelPlatformWidget();
946 ShowInactiveSync(widget);
947 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_INACTIVE);
949 widget->CloseNow();
952 TEST_F(WidgetTestInteractive, InactiveBeforeShow) {
953 Widget* widget = CreateTopLevelPlatformWidget();
955 EXPECT_FALSE(widget->IsActive());
956 EXPECT_FALSE(widget->IsVisible());
958 ShowSync(widget);
960 EXPECT_TRUE(widget->IsActive());
961 EXPECT_TRUE(widget->IsVisible());
963 widget->CloseNow();
966 TEST_F(WidgetTestInteractive, ShowInactiveAfterShow) {
967 // Create 2 widgets to ensure window layering does not change.
968 Widget* widget = CreateTopLevelPlatformWidget();
969 Widget* widget2 = CreateTopLevelPlatformWidget();
971 ShowSync(widget2);
972 EXPECT_FALSE(widget->IsActive());
973 EXPECT_TRUE(widget2->IsVisible());
974 EXPECT_TRUE(widget2->IsActive());
976 ShowSync(widget);
977 EXPECT_TRUE(widget->IsActive());
978 EXPECT_FALSE(widget2->IsActive());
980 ShowInactiveSync(widget);
981 EXPECT_TRUE(widget->IsActive());
982 EXPECT_FALSE(widget2->IsActive());
983 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
985 widget2->CloseNow();
986 widget->CloseNow();
989 TEST_F(WidgetTestInteractive, ShowAfterShowInactive) {
990 Widget* widget = CreateTopLevelPlatformWidget();
991 widget->SetBounds(gfx::Rect(100, 100, 100, 100));
993 ShowInactiveSync(widget);
994 ShowSync(widget);
995 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
997 widget->CloseNow();
1000 #if !defined(OS_CHROMEOS)
1001 TEST_F(WidgetTestInteractive, InactiveWidgetDoesNotGrabActivation) {
1002 Widget* widget = CreateTopLevelPlatformWidget();
1003 ShowSync(widget);
1004 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1006 Widget widget2;
1007 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
1008 params.native_widget = new PlatformDesktopNativeWidget(&widget2);
1009 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1010 widget2.Init(params);
1011 widget2.Show();
1012 RunPendingMessagesForActiveStatusChange();
1014 EXPECT_EQ(GetWidgetShowState(&widget2), ui::SHOW_STATE_INACTIVE);
1015 EXPECT_EQ(GetWidgetShowState(widget), ui::SHOW_STATE_NORMAL);
1017 widget->CloseNow();
1018 widget2.CloseNow();
1020 #endif // !defined(OS_CHROMEOS)
1022 // ExitFullscreenRestoreState doesn't use DesktopAura widgets. On Mac, there are
1023 // currently only Desktop widgets and fullscreen changes have to coordinate with
1024 // the OS. See BridgedNativeWidgetUITest for native Mac fullscreen tests.
1025 // Maximize on mac is also (intentionally) a no-op.
1026 #if defined(OS_MACOSX) && !defined(USE_AURA)
1027 #define MAYBE_ExitFullscreenRestoreState DISABLED_ExitFullscreenRestoreState
1028 #else
1029 #define MAYBE_ExitFullscreenRestoreState ExitFullscreenRestoreState
1030 #endif
1032 // Test that window state is not changed after getting out of full screen.
1033 TEST_F(WidgetTestInteractive, MAYBE_ExitFullscreenRestoreState) {
1034 Widget* toplevel = CreateTopLevelPlatformWidget();
1036 toplevel->Show();
1037 RunPendingMessages();
1039 // This should be a normal state window.
1040 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1042 toplevel->SetFullscreen(true);
1043 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1044 toplevel->SetFullscreen(false);
1045 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1047 // And it should still be in normal state after getting out of full screen.
1048 EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
1050 // Now, make it maximized.
1051 toplevel->Maximize();
1052 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1054 toplevel->SetFullscreen(true);
1055 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1056 toplevel->SetFullscreen(false);
1057 EXPECT_NE(ui::SHOW_STATE_FULLSCREEN, GetWidgetShowState(toplevel));
1059 // And it stays maximized after getting out of full screen.
1060 EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
1062 // Clean up.
1063 toplevel->Close();
1064 RunPendingMessages();
1067 namespace {
1069 // Used to veirfy OnMouseCaptureLost() has been invoked.
1070 class CaptureLostTrackingWidget : public Widget {
1071 public:
1072 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
1073 ~CaptureLostTrackingWidget() override {}
1075 bool GetAndClearGotCaptureLost() {
1076 bool value = got_capture_lost_;
1077 got_capture_lost_ = false;
1078 return value;
1081 // Widget:
1082 void OnMouseCaptureLost() override {
1083 got_capture_lost_ = true;
1084 Widget::OnMouseCaptureLost();
1087 private:
1088 bool got_capture_lost_;
1090 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
1093 } // namespace
1095 class WidgetCaptureTest : public ViewsTestBase {
1096 public:
1097 WidgetCaptureTest() {
1100 ~WidgetCaptureTest() override {}
1102 void SetUp() override {
1103 gfx::GLSurface::InitializeOneOffForTests();
1104 ui::RegisterPathProvider();
1105 base::FilePath ui_test_pak_path;
1106 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
1107 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
1108 ViewsTestBase::SetUp();
1111 // Verifies Widget::SetCapture() results in updating native capture along with
1112 // invoking the right Widget function.
1113 void TestCapture(bool use_desktop_native_widget) {
1114 CaptureLostTrackingWidget widget1;
1115 Widget::InitParams params1 =
1116 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1117 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
1118 &widget1);
1119 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1120 widget1.Init(params1);
1121 widget1.Show();
1123 CaptureLostTrackingWidget widget2;
1124 Widget::InitParams params2 =
1125 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1126 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1127 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
1128 &widget2);
1129 widget2.Init(params2);
1130 widget2.Show();
1132 // Set capture to widget2 and verity it gets it.
1133 widget2.SetCapture(widget2.GetRootView());
1134 EXPECT_FALSE(widget1.HasCapture());
1135 EXPECT_TRUE(widget2.HasCapture());
1136 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1137 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1139 // Set capture to widget1 and verify it gets it.
1140 widget1.SetCapture(widget1.GetRootView());
1141 EXPECT_TRUE(widget1.HasCapture());
1142 EXPECT_FALSE(widget2.HasCapture());
1143 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
1144 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
1146 // Release and verify no one has it.
1147 widget1.ReleaseCapture();
1148 EXPECT_FALSE(widget1.HasCapture());
1149 EXPECT_FALSE(widget2.HasCapture());
1150 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
1151 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
1154 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
1155 Widget* widget) {
1156 #if !defined(OS_CHROMEOS)
1157 if (create_desktop_native_widget)
1158 return new PlatformDesktopNativeWidget(widget);
1159 #endif
1160 return NULL;
1163 private:
1164 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
1167 // See description in TestCapture().
1168 TEST_F(WidgetCaptureTest, Capture) {
1169 TestCapture(false);
1172 #if !defined(OS_CHROMEOS)
1173 // See description in TestCapture(). Creates DesktopNativeWidget.
1174 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
1175 TestCapture(true);
1177 #endif
1179 // Test that no state is set if capture fails.
1180 TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) {
1181 Widget widget;
1182 Widget::InitParams params =
1183 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1184 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1185 params.bounds = gfx::Rect(400, 400);
1186 widget.Init(params);
1188 MouseView* mouse_view1 = new MouseView;
1189 MouseView* mouse_view2 = new MouseView;
1190 View* contents_view = new View;
1191 contents_view->AddChildView(mouse_view1);
1192 contents_view->AddChildView(mouse_view2);
1193 widget.SetContentsView(contents_view);
1195 mouse_view1->SetBounds(0, 0, 200, 400);
1196 mouse_view2->SetBounds(200, 0, 200, 400);
1198 // Setting capture should fail because |widget| is not visible.
1199 widget.SetCapture(mouse_view1);
1200 EXPECT_FALSE(widget.HasCapture());
1202 widget.Show();
1203 ui::test::EventGenerator generator(GetContext(), widget.GetNativeWindow());
1204 generator.set_current_location(gfx::Point(300, 10));
1205 generator.PressLeftButton();
1207 EXPECT_FALSE(mouse_view1->pressed());
1208 EXPECT_TRUE(mouse_view2->pressed());
1211 // Regression test for http://crbug.com/382421 (Linux-Aura issue).
1212 // TODO(pkotwicz): Make test pass on CrOS and Windows.
1213 // TODO(tapted): Investigate for toolkit-views on Mac http;//crbug.com/441064.
1214 #if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX)
1215 #define MAYBE_MouseExitOnCaptureGrab DISABLED_MouseExitOnCaptureGrab
1216 #else
1217 #define MAYBE_MouseExitOnCaptureGrab MouseExitOnCaptureGrab
1218 #endif
1220 // Test that a synthetic mouse exit is sent to the widget which was handling
1221 // mouse events when a different widget grabs capture.
1222 TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) {
1223 Widget widget1;
1224 Widget::InitParams params1 =
1225 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1226 params1.native_widget = CreateNativeWidget(true, &widget1);
1227 params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1228 widget1.Init(params1);
1229 MouseView* mouse_view1 = new MouseView;
1230 widget1.SetContentsView(mouse_view1);
1231 widget1.Show();
1232 widget1.SetBounds(gfx::Rect(300, 300));
1234 Widget widget2;
1235 Widget::InitParams params2 =
1236 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1237 params2.native_widget = CreateNativeWidget(true, &widget2);
1238 params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1239 widget2.Init(params2);
1240 widget2.Show();
1241 widget2.SetBounds(gfx::Rect(400, 0, 300, 300));
1243 ui::test::EventGenerator generator(widget1.GetNativeWindow());
1244 generator.set_current_location(gfx::Point(100, 100));
1245 generator.MoveMouseBy(0, 0);
1247 EXPECT_EQ(1, mouse_view1->EnteredCalls());
1248 EXPECT_EQ(0, mouse_view1->ExitedCalls());
1250 widget2.SetCapture(NULL);
1251 EXPECT_EQ(0, mouse_view1->EnteredCalls());
1252 // Grabbing native capture on Windows generates a ui::ET_MOUSE_EXITED event
1253 // in addition to the one generated by Chrome.
1254 EXPECT_LT(0, mouse_view1->ExitedCalls());
1257 namespace {
1259 // Widget observer which grabs capture when the widget is activated.
1260 class CaptureOnActivationObserver : public WidgetObserver {
1261 public:
1262 CaptureOnActivationObserver() : activation_observed_(false) {}
1263 ~CaptureOnActivationObserver() override {}
1265 // WidgetObserver:
1266 void OnWidgetActivationChanged(Widget* widget, bool active) override {
1267 if (active) {
1268 widget->SetCapture(nullptr);
1269 activation_observed_ = true;
1273 bool activation_observed() const { return activation_observed_; }
1275 private:
1276 bool activation_observed_;
1278 DISALLOW_COPY_AND_ASSIGN(CaptureOnActivationObserver);
1281 } // namespace
1283 // Test that setting capture on widget activation of a non-toplevel widget
1284 // (e.g. a bubble on Linux) succeeds.
1285 TEST_F(WidgetCaptureTest, SetCaptureToNonToplevel) {
1286 Widget toplevel;
1287 Widget::InitParams toplevel_params =
1288 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1289 toplevel_params.native_widget = CreateNativeWidget(true, &toplevel);
1290 toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1291 toplevel.Init(toplevel_params);
1292 toplevel.Show();
1294 Widget* child = new Widget;
1295 Widget::InitParams child_params =
1296 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1297 child_params.parent = toplevel.GetNativeView();
1298 child_params.context = toplevel.GetNativeWindow();
1299 child->Init(child_params);
1301 CaptureOnActivationObserver observer;
1302 child->AddObserver(&observer);
1303 child->Show();
1305 #if defined(OS_MACOSX) && !defined(USE_AURA)
1306 // On Mac, activation is asynchronous. A single trip to the runloop should be
1307 // sufficient. On Aura platforms, note that since the child widget isn't top-
1308 // level, the aura window manager gets asked whether the widget is active, not
1309 // the OS.
1310 base::RunLoop().RunUntilIdle();
1311 #endif
1313 EXPECT_TRUE(observer.activation_observed());
1314 EXPECT_TRUE(child->HasCapture());
1318 #if defined(OS_WIN)
1319 namespace {
1321 // Used to verify OnMouseEvent() has been invoked.
1322 class MouseEventTrackingWidget : public Widget {
1323 public:
1324 MouseEventTrackingWidget() : got_mouse_event_(false) {}
1325 virtual ~MouseEventTrackingWidget() {}
1327 bool GetAndClearGotMouseEvent() {
1328 bool value = got_mouse_event_;
1329 got_mouse_event_ = false;
1330 return value;
1333 // Widget:
1334 virtual void OnMouseEvent(ui::MouseEvent* event) override {
1335 got_mouse_event_ = true;
1336 Widget::OnMouseEvent(event);
1339 private:
1340 bool got_mouse_event_;
1342 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
1345 } // namespace
1347 // Verifies if a mouse event is received on a widget that doesn't have capture
1348 // on Windows that it is correctly processed by the widget that doesn't have
1349 // capture. This behavior is not desired on OSes other than Windows.
1350 TEST_F(WidgetCaptureTest, MouseEventDispatchedToRightWindow) {
1351 MouseEventTrackingWidget widget1;
1352 Widget::InitParams params1 =
1353 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1354 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
1355 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1356 widget1.Init(params1);
1357 widget1.Show();
1359 MouseEventTrackingWidget widget2;
1360 Widget::InitParams params2 =
1361 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
1362 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
1363 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
1364 widget2.Init(params2);
1365 widget2.Show();
1367 // Set capture to widget2 and verity it gets it.
1368 widget2.SetCapture(widget2.GetRootView());
1369 EXPECT_FALSE(widget1.HasCapture());
1370 EXPECT_TRUE(widget2.HasCapture());
1372 widget1.GetAndClearGotMouseEvent();
1373 widget2.GetAndClearGotMouseEvent();
1374 // Send a mouse event to the RootWindow associated with |widget1|. Even though
1375 // |widget2| has capture, |widget1| should still get the event.
1376 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
1377 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
1378 ui::EventDispatchDetails details = widget1.GetNativeWindow()->
1379 GetHost()->event_processor()->OnEventFromSource(&mouse_event);
1380 ASSERT_FALSE(details.dispatcher_destroyed);
1381 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
1382 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
1384 #endif // defined(OS_WIN)
1386 } // namespace test
1387 } // namespace views