Home | History | Annotate | Download | only in widget
      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:
     38   ExitLoopOnRelease() {}
     39   virtual ~ExitLoopOnRelease() {}
     40 
     41  private:
     42   // Overridden from View:
     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:
     54   GestureCaptureView() {}
     55   virtual ~GestureCaptureView() {}
     56 
     57  private:
     58   // Overridden from View:
     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:
     72   MouseView()
     73       : View(),
     74         entered_(0),
     75         exited_(0),
     76         pressed_(0) {
     77   }
     78   virtual ~MouseView() {}
     79 
     80   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
     81     pressed_++;
     82     return true;
     83   }
     84 
     85   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
     86     entered_++;
     87   }
     88 
     89   virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
     90     exited_++;
     91   }
     92 
     93   // Return the number of OnMouseEntered calls and reset the counter.
     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.
    101   int ExitedCalls() {
    102     int i = exited_;
    103     exited_ = 0;
    104     return i;
    105   }
    106 
    107   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:
    122   explicit NestedLoopCaptureView(Widget* widget) : widget_(widget) {}
    123   virtual ~NestedLoopCaptureView() {}
    124 
    125  private:
    126   // Overridden from View:
    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.
    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 
    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 
    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.
    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?
    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..
    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:
    457   WidgetActivationTest()
    458       : active_(false) {}
    459 
    460   virtual ~WidgetActivationTest() {}
    461 
    462   virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE {
    463     active_ = active;
    464   }
    465 
    466   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.
    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:
    515   explicit ModalDialogDelegate(ui::ModalType type) : type_(type) {}
    516   virtual ~ModalDialogDelegate() {}
    517 
    518   // WidgetDelegate overrides.
    519   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.
    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.
    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:
    612   CaptureLostTrackingWidget() : got_capture_lost_(false) {}
    613   virtual ~CaptureLostTrackingWidget() {}
    614 
    615   bool GetAndClearGotCaptureLost() {
    616     bool value = got_capture_lost_;
    617     got_capture_lost_ = false;
    618     return value;
    619   }
    620 
    621   // Widget:
    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:
    637   WidgetCaptureTest() {
    638   }
    639 
    640   virtual ~WidgetCaptureTest() {
    641   }
    642 
    643   // Verifies Widget::SetCapture() results in updating native capture along with
    644   // invoking the right Widget function.
    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:
    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().
    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.
    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:
    717   MouseEventTrackingWidget() : got_mouse_event_(false) {}
    718   virtual ~MouseEventTrackingWidget() {}
    719 
    720   bool GetAndClearGotMouseEvent() {
    721     bool value = got_mouse_event_;
    722     got_mouse_event_ = false;
    723     return value;
    724   }
    725 
    726   // Widget:
    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.
    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