• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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