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 "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/run_loop.h"
8 #include "ui/gfx/native_widget_types.h"
9 #include "ui/views/test/widget_test.h"
10 #include "ui/views/widget/widget.h"
11 #include "ui/views/window/dialog_delegate.h"
12
13 #if defined(USE_AURA)
14 #include "ui/aura/client/activation_client.h"
15 #include "ui/aura/client/focus_client.h"
16 #include "ui/aura/env.h"
17 #include "ui/aura/root_window.h"
18 #include "ui/aura/window.h"
19 #endif
20
21 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
22 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
23 #endif
24
25 #if defined(OS_WIN)
26 #include "ui/views/win/hwnd_util.h"
27 #endif
28
29 namespace views {
30 namespace test {
31
32 namespace {
33
34 // A View that closes the Widget and exits the current message-loop when it
35 // receives a mouse-release event.
36 class ExitLoopOnRelease : public View {
37 public:
ExitLoopOnRelease()38 ExitLoopOnRelease() {}
~ExitLoopOnRelease()39 virtual ~ExitLoopOnRelease() {}
40
41 private:
42 // Overridden from View:
OnMouseReleased(const ui::MouseEvent & event)43 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
44 GetWidget()->Close();
45 base::MessageLoop::current()->QuitNow();
46 }
47
48 DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease);
49 };
50
51 // A view that does a capture on gesture-begin events.
52 class GestureCaptureView : public View {
53 public:
GestureCaptureView()54 GestureCaptureView() {}
~GestureCaptureView()55 virtual ~GestureCaptureView() {}
56
57 private:
58 // Overridden from View:
OnGestureEvent(ui::GestureEvent * event)59 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
60 if (event->type() == ui::ET_GESTURE_BEGIN) {
61 GetWidget()->SetCapture(this);
62 event->StopPropagation();
63 }
64 }
65
66 DISALLOW_COPY_AND_ASSIGN(GestureCaptureView);
67 };
68
69 // A view that always processes all mouse events.
70 class MouseView : public View {
71 public:
MouseView()72 MouseView()
73 : View(),
74 entered_(0),
75 exited_(0),
76 pressed_(0) {
77 }
~MouseView()78 virtual ~MouseView() {}
79
OnMousePressed(const ui::MouseEvent & event)80 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
81 pressed_++;
82 return true;
83 }
84
OnMouseEntered(const ui::MouseEvent & event)85 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
86 entered_++;
87 }
88
OnMouseExited(const ui::MouseEvent & event)89 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
90 exited_++;
91 }
92
93 // Return the number of OnMouseEntered calls and reset the counter.
EnteredCalls()94 int EnteredCalls() {
95 int i = entered_;
96 entered_ = 0;
97 return i;
98 }
99
100 // Return the number of OnMouseExited calls and reset the counter.
ExitedCalls()101 int ExitedCalls() {
102 int i = exited_;
103 exited_ = 0;
104 return i;
105 }
106
pressed() const107 int pressed() const { return pressed_; }
108
109 private:
110 int entered_;
111 int exited_;
112
113 int pressed_;
114
115 DISALLOW_COPY_AND_ASSIGN(MouseView);
116 };
117
118 // A View that shows a different widget, sets capture on that widget, and
119 // initiates a nested message-loop when it receives a mouse-press event.
120 class NestedLoopCaptureView : public View {
121 public:
NestedLoopCaptureView(Widget * widget)122 explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
~NestedLoopCaptureView()123 virtual ~NestedLoopCaptureView() {}
124
125 private:
126 // Overridden from View:
OnMousePressed(const ui::MouseEvent & event)127 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
128 // Start a nested loop.
129 widget_->Show();
130 widget_->SetCapture(widget_->GetContentsView());
131 EXPECT_TRUE(widget_->HasCapture());
132
133 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
134 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
135
136 base::RunLoop run_loop;
137 #if defined(USE_AURA)
138 run_loop.set_dispatcher(aura::Env::GetInstance()->GetDispatcher());
139 #endif
140 run_loop.Run();
141 return true;
142 }
143
144 Widget* widget_;
145
146 DISALLOW_COPY_AND_ASSIGN(NestedLoopCaptureView);
147 };
148
149 } // namespace
150
151 #if defined(OS_WIN) && defined(USE_AURA)
152 // Tests whether activation and focus change works correctly in Windows AURA.
153 // We test the following:-
154 // 1. If the active aura window is correctly set when a top level widget is
155 // created.
156 // 2. If the active aura window in widget 1 created above, is set to NULL when
157 // another top level widget is created and focused.
158 // 3. On focusing the native platform window for widget 1, the active aura
159 // window for widget 1 should be set and that for widget 2 should reset.
160 // TODO(ananta)
161 // Discuss with erg on how to write this test for linux x11 aura.
TEST_F(WidgetTest,DesktopNativeWidgetAuraActivationAndFocusTest)162 TEST_F(WidgetTest, DesktopNativeWidgetAuraActivationAndFocusTest) {
163 // Create widget 1 and expect the active window to be its window.
164 View* contents_view1 = new View;
165 contents_view1->SetFocusable(true);
166 Widget widget1;
167 Widget::InitParams init_params =
168 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
169 init_params.bounds = gfx::Rect(0, 0, 200, 200);
170 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
171 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
172 widget1.Init(init_params);
173 widget1.SetContentsView(contents_view1);
174 widget1.Show();
175 aura::Window* root_window1= widget1.GetNativeView()->GetRootWindow();
176 contents_view1->RequestFocus();
177
178 EXPECT_TRUE(root_window1 != NULL);
179 aura::client::ActivationClient* activation_client1 =
180 aura::client::GetActivationClient(root_window1);
181 EXPECT_TRUE(activation_client1 != NULL);
182 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
183
184 // Create widget 2 and expect the active window to be its window.
185 View* contents_view2 = new View;
186 Widget widget2;
187 Widget::InitParams init_params2 =
188 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
189 init_params2.bounds = gfx::Rect(0, 0, 200, 200);
190 init_params2.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
191 init_params2.native_widget = new DesktopNativeWidgetAura(&widget2);
192 widget2.Init(init_params2);
193 widget2.SetContentsView(contents_view2);
194 widget2.Show();
195 aura::Window* root_window2 = widget2.GetNativeView()->GetRootWindow();
196 contents_view2->RequestFocus();
197 ::SetActiveWindow(
198 root_window2->GetDispatcher()->host()->GetAcceleratedWidget());
199
200 aura::client::ActivationClient* activation_client2 =
201 aura::client::GetActivationClient(root_window2);
202 EXPECT_TRUE(activation_client2 != NULL);
203 EXPECT_EQ(activation_client2->GetActiveWindow(), widget2.GetNativeView());
204 EXPECT_EQ(activation_client1->GetActiveWindow(),
205 reinterpret_cast<aura::Window*>(NULL));
206
207 // Now set focus back to widget 1 and expect the active window to be its
208 // window.
209 contents_view1->RequestFocus();
210 ::SetActiveWindow(
211 root_window1->GetDispatcher()->host()->GetAcceleratedWidget());
212 EXPECT_EQ(activation_client2->GetActiveWindow(),
213 reinterpret_cast<aura::Window*>(NULL));
214 EXPECT_EQ(activation_client1->GetActiveWindow(), widget1.GetNativeView());
215 }
216 #endif
217
TEST_F(WidgetTest,CaptureAutoReset)218 TEST_F(WidgetTest, CaptureAutoReset) {
219 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
220 View* container = new View;
221 toplevel->SetContentsView(container);
222
223 EXPECT_FALSE(toplevel->HasCapture());
224 toplevel->SetCapture(NULL);
225 EXPECT_TRUE(toplevel->HasCapture());
226
227 // By default, mouse release removes capture.
228 gfx::Point click_location(45, 15);
229 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
230 ui::EF_LEFT_MOUSE_BUTTON);
231 toplevel->OnMouseEvent(&release);
232 EXPECT_FALSE(toplevel->HasCapture());
233
234 // Now a mouse release shouldn't remove capture.
235 toplevel->set_auto_release_capture(false);
236 toplevel->SetCapture(NULL);
237 EXPECT_TRUE(toplevel->HasCapture());
238 toplevel->OnMouseEvent(&release);
239 EXPECT_TRUE(toplevel->HasCapture());
240 toplevel->ReleaseCapture();
241 EXPECT_FALSE(toplevel->HasCapture());
242
243 toplevel->Close();
244 RunPendingMessages();
245 }
246
TEST_F(WidgetTest,ResetCaptureOnGestureEnd)247 TEST_F(WidgetTest, ResetCaptureOnGestureEnd) {
248 Widget* toplevel = CreateTopLevelFramelessPlatformWidget();
249 View* container = new View;
250 toplevel->SetContentsView(container);
251
252 View* gesture = new GestureCaptureView;
253 gesture->SetBounds(0, 0, 30, 30);
254 container->AddChildView(gesture);
255
256 MouseView* mouse = new MouseView;
257 mouse->SetBounds(30, 0, 30, 30);
258 container->AddChildView(mouse);
259
260 toplevel->SetSize(gfx::Size(100, 100));
261 toplevel->Show();
262
263 // Start a gesture on |gesture|.
264 ui::GestureEvent begin(ui::ET_GESTURE_BEGIN,
265 15, 15, 0, base::TimeDelta(),
266 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), 1);
267 ui::GestureEvent end(ui::ET_GESTURE_END,
268 15, 15, 0, base::TimeDelta(),
269 ui::GestureEventDetails(ui::ET_GESTURE_END, 0, 0), 1);
270 toplevel->OnGestureEvent(&begin);
271
272 // Now try to click on |mouse|. Since |gesture| will have capture, |mouse|
273 // will not receive the event.
274 gfx::Point click_location(45, 15);
275
276 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
277 ui::EF_LEFT_MOUSE_BUTTON);
278 ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
279 ui::EF_LEFT_MOUSE_BUTTON);
280
281 EXPECT_TRUE(toplevel->HasCapture());
282
283 toplevel->OnMouseEvent(&press);
284 toplevel->OnMouseEvent(&release);
285 EXPECT_EQ(0, mouse->pressed());
286
287 EXPECT_FALSE(toplevel->HasCapture());
288
289 // The end of the gesture should release the capture, and pressing on |mouse|
290 // should now reach |mouse|.
291 toplevel->OnGestureEvent(&end);
292 toplevel->OnMouseEvent(&press);
293 toplevel->OnMouseEvent(&release);
294 EXPECT_EQ(1, mouse->pressed());
295
296 toplevel->Close();
297 RunPendingMessages();
298 }
299
300 // Checks that if a mouse-press triggers a capture on a different widget (which
301 // consumes the mouse-release event), then the target of the press does not have
302 // capture.
TEST_F(WidgetTest,DisableCaptureWidgetFromMousePress)303 TEST_F(WidgetTest, DisableCaptureWidgetFromMousePress) {
304 // The test creates two widgets: |first| and |second|.
305 // The View in |first| makes |second| visible, sets capture on it, and starts
306 // a nested loop (like a menu does). The View in |second| terminates the
307 // nested loop and closes the widget.
308 // The test sends a mouse-press event to |first|, and posts a task to send a
309 // release event to |second|, to make sure that the release event is
310 // dispatched after the nested loop starts.
311
312 Widget* first = CreateTopLevelFramelessPlatformWidget();
313 Widget* second = CreateTopLevelFramelessPlatformWidget();
314
315 View* container = new NestedLoopCaptureView(second);
316 first->SetContentsView(container);
317
318 second->SetContentsView(new ExitLoopOnRelease());
319
320 first->SetSize(gfx::Size(100, 100));
321 first->Show();
322
323 gfx::Point location(20, 20);
324 base::MessageLoop::current()->PostTask(FROM_HERE,
325 base::Bind(&Widget::OnMouseEvent,
326 base::Unretained(second),
327 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
328 location,
329 location,
330 ui::EF_LEFT_MOUSE_BUTTON))));
331 ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
332 ui::EF_LEFT_MOUSE_BUTTON);
333 first->OnMouseEvent(&press);
334 EXPECT_FALSE(first->HasCapture());
335 first->Close();
336 RunPendingMessages();
337 }
338
339 // Tests some grab/ungrab events.
340 // TODO(estade): can this be enabled now that this is an interactive ui test?
TEST_F(WidgetTest,DISABLED_GrabUngrab)341 TEST_F(WidgetTest, DISABLED_GrabUngrab) {
342 Widget* toplevel = CreateTopLevelPlatformWidget();
343 Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
344 Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
345
346 toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
347
348 child1->SetBounds(gfx::Rect(10, 10, 300, 300));
349 View* view = new MouseView();
350 view->SetBounds(0, 0, 300, 300);
351 child1->GetRootView()->AddChildView(view);
352
353 child2->SetBounds(gfx::Rect(200, 10, 200, 200));
354 view = new MouseView();
355 view->SetBounds(0, 0, 200, 200);
356 child2->GetRootView()->AddChildView(view);
357
358 toplevel->Show();
359 RunPendingMessages();
360
361 // Click on child1
362 gfx::Point p1(45, 45);
363 ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
364 ui::EF_LEFT_MOUSE_BUTTON);
365 toplevel->OnMouseEvent(&pressed);
366
367 EXPECT_TRUE(toplevel->HasCapture());
368 EXPECT_TRUE(child1->HasCapture());
369 EXPECT_FALSE(child2->HasCapture());
370
371 ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
372 ui::EF_LEFT_MOUSE_BUTTON);
373 toplevel->OnMouseEvent(&released);
374
375 EXPECT_FALSE(toplevel->HasCapture());
376 EXPECT_FALSE(child1->HasCapture());
377 EXPECT_FALSE(child2->HasCapture());
378
379 RunPendingMessages();
380
381 // Click on child2
382 gfx::Point p2(315, 45);
383 ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
384 ui::EF_LEFT_MOUSE_BUTTON);
385 toplevel->OnMouseEvent(&pressed2);
386 EXPECT_TRUE(pressed2.handled());
387 EXPECT_TRUE(toplevel->HasCapture());
388 EXPECT_TRUE(child2->HasCapture());
389 EXPECT_FALSE(child1->HasCapture());
390
391 ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
392 ui::EF_LEFT_MOUSE_BUTTON);
393 toplevel->OnMouseEvent(&released2);
394 EXPECT_FALSE(toplevel->HasCapture());
395 EXPECT_FALSE(child1->HasCapture());
396 EXPECT_FALSE(child2->HasCapture());
397
398 toplevel->CloseNow();
399 }
400
401 // Tests mouse move outside of the window into the "resize controller" and back
402 // will still generate an OnMouseEntered and OnMouseExited event..
TEST_F(WidgetTest,CheckResizeControllerEvents)403 TEST_F(WidgetTest, CheckResizeControllerEvents) {
404 Widget* toplevel = CreateTopLevelPlatformWidget();
405
406 toplevel->SetBounds(gfx::Rect(0, 0, 100, 100));
407
408 MouseView* view = new MouseView();
409 view->SetBounds(90, 90, 10, 10);
410 toplevel->GetRootView()->AddChildView(view);
411
412 toplevel->Show();
413 RunPendingMessages();
414
415 // Move to an outside position.
416 gfx::Point p1(200, 200);
417 ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE);
418 toplevel->OnMouseEvent(&moved_out);
419 EXPECT_EQ(0, view->EnteredCalls());
420 EXPECT_EQ(0, view->ExitedCalls());
421
422 // Move onto the active view.
423 gfx::Point p2(95, 95);
424 ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE);
425 toplevel->OnMouseEvent(&moved_over);
426 EXPECT_EQ(1, view->EnteredCalls());
427 EXPECT_EQ(0, view->ExitedCalls());
428
429 // Move onto the outer resizing border.
430 gfx::Point p3(102, 95);
431 ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE);
432 toplevel->OnMouseEvent(&moved_resizer);
433 EXPECT_EQ(0, view->EnteredCalls());
434 EXPECT_EQ(1, view->ExitedCalls());
435
436 // Move onto the view again.
437 toplevel->OnMouseEvent(&moved_over);
438 EXPECT_EQ(1, view->EnteredCalls());
439 EXPECT_EQ(0, view->ExitedCalls());
440
441 RunPendingMessages();
442
443 toplevel->CloseNow();
444 }
445
446 #if defined(OS_WIN)
447
448 // This class subclasses the Widget class to listen for activation change
449 // notifications and provides accessors to return information as to whether
450 // the widget is active. We need this to ensure that users of the widget
451 // class activate the widget only when the underlying window becomes really
452 // active. Previously we would activate the widget in the WM_NCACTIVATE
453 // message which is incorrect because APIs like FlashWindowEx flash the
454 // window caption by sending fake WM_NCACTIVATE messages.
455 class WidgetActivationTest : public Widget {
456 public:
WidgetActivationTest()457 WidgetActivationTest()
458 : active_(false) {}
459
~WidgetActivationTest()460 virtual ~WidgetActivationTest() {}
461
OnNativeWidgetActivationChanged(bool active)462 virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE {
463 active_ = active;
464 }
465
active() const466 bool active() const { return active_; }
467
468 private:
469 bool active_;
470
471 DISALLOW_COPY_AND_ASSIGN(WidgetActivationTest);
472 };
473
474 // Tests whether the widget only becomes active when the underlying window
475 // is really active.
TEST_F(WidgetTest,WidgetNotActivatedOnFakeActivationMessages)476 TEST_F(WidgetTest, WidgetNotActivatedOnFakeActivationMessages) {
477 WidgetActivationTest widget1;
478 Widget::InitParams init_params =
479 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
480 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
481 #if defined(USE_AURA)
482 init_params.native_widget = new DesktopNativeWidgetAura(&widget1);
483 #endif
484 init_params.bounds = gfx::Rect(0, 0, 200, 200);
485 widget1.Init(init_params);
486 widget1.Show();
487 EXPECT_EQ(true, widget1.active());
488
489 WidgetActivationTest widget2;
490 #if defined(USE_AURA)
491 init_params.native_widget = new DesktopNativeWidgetAura(&widget2);
492 #endif
493 widget2.Init(init_params);
494 widget2.Show();
495 EXPECT_EQ(true, widget2.active());
496 EXPECT_EQ(false, widget1.active());
497
498 HWND win32_native_window1 = HWNDForWidget(&widget1);
499 EXPECT_TRUE(::IsWindow(win32_native_window1));
500
501 ::SendMessage(win32_native_window1, WM_NCACTIVATE, 1, 0);
502 EXPECT_EQ(false, widget1.active());
503 EXPECT_EQ(true, widget2.active());
504
505 ::SetActiveWindow(win32_native_window1);
506 EXPECT_EQ(true, widget1.active());
507 EXPECT_EQ(false, widget2.active());
508 }
509 #endif
510
511 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
512 // Provides functionality to create a window modal dialog.
513 class ModalDialogDelegate : public DialogDelegateView {
514 public:
ModalDialogDelegate(ui::ModalType type)515 explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
~ModalDialogDelegate()516 virtual ~ModalDialogDelegate() {}
517
518 // WidgetDelegate overrides.
GetModalType() const519 virtual ui::ModalType GetModalType() const OVERRIDE {
520 return type_;
521 }
522
523 private:
524 ui::ModalType type_;
525
526 DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate);
527 };
528
529 // Tests whether the focused window is set correctly when a modal window is
530 // created and destroyed. When it is destroyed it should focus the owner
531 // window.
TEST_F(WidgetTest,WindowModalWindowDestroyedActivationTest)532 TEST_F(WidgetTest, WindowModalWindowDestroyedActivationTest) {
533 // Create a top level widget.
534 Widget top_level_widget;
535 Widget::InitParams init_params =
536 CreateParams(Widget::InitParams::TYPE_WINDOW);
537 init_params.show_state = ui::SHOW_STATE_NORMAL;
538 gfx::Rect initial_bounds(0, 0, 500, 500);
539 init_params.bounds = initial_bounds;
540 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
541 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
542 top_level_widget.Init(init_params);
543 top_level_widget.Show();
544
545 aura::Window* top_level_window = top_level_widget.GetNativeWindow();
546 EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
547 top_level_window)->GetFocusedWindow());
548
549 // Create a modal dialog.
550 // This instance will be destroyed when the dialog is destroyed.
551 ModalDialogDelegate* dialog_delegate =
552 new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
553
554 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
555 dialog_delegate, NULL, top_level_widget.GetNativeWindow());
556 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
557 modal_dialog_widget->Show();
558 aura::Window* dialog_window = modal_dialog_widget->GetNativeWindow();
559 EXPECT_EQ(dialog_window, aura::client::GetFocusClient(
560 top_level_window)->GetFocusedWindow());
561
562 modal_dialog_widget->CloseNow();
563 EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
564 top_level_window)->GetFocusedWindow());
565 top_level_widget.CloseNow();
566 }
567
568 // Test that when opening a system-modal window, capture is released.
TEST_F(WidgetTest,SystemModalWindowReleasesCapture)569 TEST_F(WidgetTest, SystemModalWindowReleasesCapture) {
570 // Create a top level widget.
571 Widget top_level_widget;
572 Widget::InitParams init_params =
573 CreateParams(Widget::InitParams::TYPE_WINDOW);
574 init_params.show_state = ui::SHOW_STATE_NORMAL;
575 gfx::Rect initial_bounds(0, 0, 500, 500);
576 init_params.bounds = initial_bounds;
577 init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
578 init_params.native_widget = new DesktopNativeWidgetAura(&top_level_widget);
579 top_level_widget.Init(init_params);
580 top_level_widget.Show();
581
582 aura::Window* top_level_window = top_level_widget.GetNativeWindow();
583 EXPECT_EQ(top_level_window, aura::client::GetFocusClient(
584 top_level_window)->GetFocusedWindow());
585
586 EXPECT_FALSE(top_level_window->HasCapture());
587 top_level_window->SetCapture();
588 EXPECT_TRUE(top_level_window->HasCapture());
589
590 // Create a modal dialog.
591 ModalDialogDelegate* dialog_delegate =
592 new ModalDialogDelegate(ui::MODAL_TYPE_SYSTEM);
593
594 Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget(
595 dialog_delegate, NULL, top_level_widget.GetNativeWindow());
596 modal_dialog_widget->SetBounds(gfx::Rect(100, 100, 200, 200));
597 modal_dialog_widget->Show();
598
599 EXPECT_FALSE(top_level_window->HasCapture());
600
601 modal_dialog_widget->CloseNow();
602 top_level_widget.CloseNow();
603 }
604
605 #endif
606
607 namespace {
608
609 // Used to veirfy OnMouseCaptureLost() has been invoked.
610 class CaptureLostTrackingWidget : public Widget {
611 public:
CaptureLostTrackingWidget()612 CaptureLostTrackingWidget() : got_capture_lost_(false) {}
~CaptureLostTrackingWidget()613 virtual ~CaptureLostTrackingWidget() {}
614
GetAndClearGotCaptureLost()615 bool GetAndClearGotCaptureLost() {
616 bool value = got_capture_lost_;
617 got_capture_lost_ = false;
618 return value;
619 }
620
621 // Widget:
OnMouseCaptureLost()622 virtual void OnMouseCaptureLost() OVERRIDE {
623 got_capture_lost_ = true;
624 Widget::OnMouseCaptureLost();
625 }
626
627 private:
628 bool got_capture_lost_;
629
630 DISALLOW_COPY_AND_ASSIGN(CaptureLostTrackingWidget);
631 };
632
633 } // namespace
634
635 class WidgetCaptureTest : public ViewsTestBase {
636 public:
WidgetCaptureTest()637 WidgetCaptureTest() {
638 }
639
~WidgetCaptureTest()640 virtual ~WidgetCaptureTest() {
641 }
642
643 // Verifies Widget::SetCapture() results in updating native capture along with
644 // invoking the right Widget function.
TestCapture(bool use_desktop_native_widget)645 void TestCapture(bool use_desktop_native_widget) {
646 CaptureLostTrackingWidget widget1;
647 Widget::InitParams params1 =
648 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
649 params1.native_widget = CreateNativeWidget(use_desktop_native_widget,
650 &widget1);
651 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
652 widget1.Init(params1);
653 widget1.Show();
654
655 CaptureLostTrackingWidget widget2;
656 Widget::InitParams params2 =
657 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
658 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
659 params2.native_widget = CreateNativeWidget(use_desktop_native_widget,
660 &widget2);
661 widget2.Init(params2);
662 widget2.Show();
663
664 // Set capture to widget2 and verity it gets it.
665 widget2.SetCapture(widget2.GetRootView());
666 EXPECT_FALSE(widget1.HasCapture());
667 EXPECT_TRUE(widget2.HasCapture());
668 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
669 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
670
671 // Set capture to widget1 and verify it gets it.
672 widget1.SetCapture(widget1.GetRootView());
673 EXPECT_TRUE(widget1.HasCapture());
674 EXPECT_FALSE(widget2.HasCapture());
675 EXPECT_FALSE(widget1.GetAndClearGotCaptureLost());
676 EXPECT_TRUE(widget2.GetAndClearGotCaptureLost());
677
678 // Release and verify no one has it.
679 widget1.ReleaseCapture();
680 EXPECT_FALSE(widget1.HasCapture());
681 EXPECT_FALSE(widget2.HasCapture());
682 EXPECT_TRUE(widget1.GetAndClearGotCaptureLost());
683 EXPECT_FALSE(widget2.GetAndClearGotCaptureLost());
684 }
685
686 private:
CreateNativeWidget(bool create_desktop_native_widget,Widget * widget)687 NativeWidget* CreateNativeWidget(bool create_desktop_native_widget,
688 Widget* widget) {
689 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
690 if (create_desktop_native_widget)
691 return new DesktopNativeWidgetAura(widget);
692 #endif
693 return NULL;
694 }
695
696 DISALLOW_COPY_AND_ASSIGN(WidgetCaptureTest);
697 };
698
699 // See description in TestCapture().
TEST_F(WidgetCaptureTest,Capture)700 TEST_F(WidgetCaptureTest, Capture) {
701 TestCapture(false);
702 }
703
704 #if defined(USE_AURA) && !defined(OS_LINUX)
705 // See description in TestCapture(). Creates DesktopNativeWidget.
TEST_F(WidgetCaptureTest,CaptureDesktopNativeWidget)706 TEST_F(WidgetCaptureTest, CaptureDesktopNativeWidget) {
707 TestCapture(true);
708 }
709 #endif
710
711 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
712 namespace {
713
714 // Used to veirfy OnMouseEvent() has been invoked.
715 class MouseEventTrackingWidget : public Widget {
716 public:
MouseEventTrackingWidget()717 MouseEventTrackingWidget() : got_mouse_event_(false) {}
~MouseEventTrackingWidget()718 virtual ~MouseEventTrackingWidget() {}
719
GetAndClearGotMouseEvent()720 bool GetAndClearGotMouseEvent() {
721 bool value = got_mouse_event_;
722 got_mouse_event_ = false;
723 return value;
724 }
725
726 // Widget:
OnMouseEvent(ui::MouseEvent * event)727 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
728 got_mouse_event_ = true;
729 Widget::OnMouseEvent(event);
730 }
731
732 private:
733 bool got_mouse_event_;
734
735 DISALLOW_COPY_AND_ASSIGN(MouseEventTrackingWidget);
736 };
737
738 } // namespace
739
740 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
741 // TODO(erg): linux_aura bringup: http://crbug.com/163931
742 #define MAYBE_MouseEventDispatchedToRightWindow \
743 DISABLED_MouseEventDispatchedToRightWindow
744 #else
745 #define MAYBE_MouseEventDispatchedToRightWindow \
746 MouseEventDispatchedToRightWindow
747 #endif
748
749 // Verifies if a mouse event is received on a widget that doesn't have capture
750 // it is correctly processed by the widget that doesn't have capture.
TEST_F(WidgetCaptureTest,MAYBE_MouseEventDispatchedToRightWindow)751 TEST_F(WidgetCaptureTest, MAYBE_MouseEventDispatchedToRightWindow) {
752 MouseEventTrackingWidget widget1;
753 Widget::InitParams params1 =
754 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
755 params1.native_widget = new DesktopNativeWidgetAura(&widget1);
756 params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
757 widget1.Init(params1);
758 widget1.Show();
759
760 MouseEventTrackingWidget widget2;
761 Widget::InitParams params2 =
762 CreateParams(views::Widget::InitParams::TYPE_WINDOW);
763 params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
764 params2.native_widget = new DesktopNativeWidgetAura(&widget2);
765 widget2.Init(params2);
766 widget2.Show();
767
768 // Set capture to widget2 and verity it gets it.
769 widget2.SetCapture(widget2.GetRootView());
770 EXPECT_FALSE(widget1.HasCapture());
771 EXPECT_TRUE(widget2.HasCapture());
772
773 widget1.GetAndClearGotMouseEvent();
774 widget2.GetAndClearGotMouseEvent();
775 // Send a mouse event to the RootWindow associated with |widget1|. Even though
776 // |widget2| has capture, |widget1| should still get the event.
777 ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
778 ui::EF_NONE);
779 widget1.GetNativeWindow()->GetDispatcher()->AsRootWindowHostDelegate()->
780 OnHostMouseEvent(&mouse_event);
781 EXPECT_TRUE(widget1.GetAndClearGotMouseEvent());
782 EXPECT_FALSE(widget2.GetAndClearGotMouseEvent());
783 }
784 #endif
785
786 } // namespace test
787 } // namespace views
788