Home | History | Annotate | Download | only in desktop_aura
      1 // Copyright 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 "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
      6 
      7 #include "base/bind.h"
      8 #include "ui/aura/client/aura_constants.h"
      9 #include "ui/aura/client/cursor_client.h"
     10 #include "ui/aura/client/window_tree_client.h"
     11 #include "ui/aura/test/event_generator.h"
     12 #include "ui/aura/test/test_window_delegate.h"
     13 #include "ui/aura/window.h"
     14 #include "ui/aura/window_tree_host.h"
     15 #include "ui/views/test/views_test_base.h"
     16 #include "ui/views/test/widget_test.h"
     17 #include "ui/views/widget/widget.h"
     18 #include "ui/wm/public/dispatcher_client.h"
     19 
     20 namespace views {
     21 namespace test {
     22 
     23 typedef ViewsTestBase DesktopNativeWidgetAuraTest;
     24 
     25 // Verifies creating a Widget with a parent that is not in a RootWindow doesn't
     26 // crash.
     27 TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) {
     28   scoped_ptr<aura::Window> window(new aura::Window(NULL));
     29   Widget widget;
     30   Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
     31   params.bounds = gfx::Rect(0, 0, 200, 200);
     32   params.parent = window.get();
     33   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     34   params.native_widget = new DesktopNativeWidgetAura(&widget);
     35   widget.Init(params);
     36 }
     37 
     38 // Verifies that the Aura windows making up a widget instance have the correct
     39 // bounds after the widget is resized.
     40 TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowSizeTest) {
     41   Widget widget;
     42 
     43   // On Linux we test this with popup windows because the WM may ignore the size
     44   // suggestion for normal windows.
     45 #if defined(OS_LINUX)
     46   Widget::InitParams init_params =
     47       CreateParams(Widget::InitParams::TYPE_POPUP);
     48 #else
     49   Widget::InitParams init_params =
     50       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
     51 #endif
     52 
     53   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     54   init_params.native_widget = new DesktopNativeWidgetAura(&widget);
     55   widget.Init(init_params);
     56 
     57   gfx::Rect bounds(0, 0, 100, 100);
     58   widget.SetBounds(bounds);
     59   widget.Show();
     60 
     61   EXPECT_EQ(bounds.ToString(),
     62             widget.GetNativeView()->GetRootWindow()->bounds().ToString());
     63   EXPECT_EQ(bounds.ToString(), widget.GetNativeView()->bounds().ToString());
     64   EXPECT_EQ(bounds.ToString(),
     65             widget.GetNativeView()->parent()->bounds().ToString());
     66 
     67   gfx::Rect new_bounds(0, 0, 200, 200);
     68   widget.SetBounds(new_bounds);
     69   EXPECT_EQ(new_bounds.ToString(),
     70             widget.GetNativeView()->GetRootWindow()->bounds().ToString());
     71   EXPECT_EQ(new_bounds.ToString(), widget.GetNativeView()->bounds().ToString());
     72   EXPECT_EQ(new_bounds.ToString(),
     73             widget.GetNativeView()->parent()->bounds().ToString());
     74 }
     75 
     76 // Verifies GetNativeView() is initially hidden. If the native view is initially
     77 // shown then animations can not be disabled.
     78 TEST_F(DesktopNativeWidgetAuraTest, NativeViewInitiallyHidden) {
     79   Widget widget;
     80   Widget::InitParams init_params =
     81       CreateParams(Widget::InitParams::TYPE_WINDOW);
     82   init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     83   init_params.native_widget = new DesktopNativeWidgetAura(&widget);
     84   widget.Init(init_params);
     85   EXPECT_FALSE(widget.GetNativeView()->IsVisible());
     86 }
     87 
     88 // Verify that the cursor state is shared between two native widgets.
     89 TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) {
     90   // Create two native widgets, each owning different root windows.
     91   Widget widget_a;
     92   Widget::InitParams init_params_a =
     93       CreateParams(Widget::InitParams::TYPE_WINDOW);
     94   init_params_a.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     95   DesktopNativeWidgetAura* desktop_native_widget_aura_a =
     96       new DesktopNativeWidgetAura(&widget_a);
     97   init_params_a.native_widget = desktop_native_widget_aura_a;
     98   widget_a.Init(init_params_a);
     99 
    100   Widget widget_b;
    101   Widget::InitParams init_params_b =
    102       CreateParams(Widget::InitParams::TYPE_WINDOW);
    103   init_params_b.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    104   DesktopNativeWidgetAura* desktop_native_widget_aura_b =
    105       new DesktopNativeWidgetAura(&widget_b);
    106   init_params_b.native_widget = desktop_native_widget_aura_b;
    107   widget_b.Init(init_params_b);
    108 
    109   aura::client::CursorClient* cursor_client_a = aura::client::GetCursorClient(
    110       desktop_native_widget_aura_a->host()->window());
    111   aura::client::CursorClient* cursor_client_b = aura::client::GetCursorClient(
    112       desktop_native_widget_aura_b->host()->window());
    113 
    114   // Verify the cursor can be locked using one client and unlocked using
    115   // another.
    116   EXPECT_FALSE(cursor_client_a->IsCursorLocked());
    117   EXPECT_FALSE(cursor_client_b->IsCursorLocked());
    118 
    119   cursor_client_a->LockCursor();
    120   EXPECT_TRUE(cursor_client_a->IsCursorLocked());
    121   EXPECT_TRUE(cursor_client_b->IsCursorLocked());
    122 
    123   cursor_client_b->UnlockCursor();
    124   EXPECT_FALSE(cursor_client_a->IsCursorLocked());
    125   EXPECT_FALSE(cursor_client_b->IsCursorLocked());
    126 
    127   // Verify that mouse events can be disabled using one client and then
    128   // re-enabled using another. Note that disabling mouse events should also
    129   // have the side effect of making the cursor invisible.
    130   EXPECT_TRUE(cursor_client_a->IsCursorVisible());
    131   EXPECT_TRUE(cursor_client_b->IsCursorVisible());
    132   EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
    133   EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
    134 
    135   cursor_client_b->DisableMouseEvents();
    136   EXPECT_FALSE(cursor_client_a->IsCursorVisible());
    137   EXPECT_FALSE(cursor_client_b->IsCursorVisible());
    138   EXPECT_FALSE(cursor_client_a->IsMouseEventsEnabled());
    139   EXPECT_FALSE(cursor_client_b->IsMouseEventsEnabled());
    140 
    141   cursor_client_a->EnableMouseEvents();
    142   EXPECT_TRUE(cursor_client_a->IsCursorVisible());
    143   EXPECT_TRUE(cursor_client_b->IsCursorVisible());
    144   EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
    145   EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
    146 
    147   // Verify that setting the cursor using one cursor client
    148   // will set it for all root windows.
    149   EXPECT_EQ(ui::kCursorNone, cursor_client_a->GetCursor().native_type());
    150   EXPECT_EQ(ui::kCursorNone, cursor_client_b->GetCursor().native_type());
    151 
    152   cursor_client_b->SetCursor(ui::kCursorPointer);
    153   EXPECT_EQ(ui::kCursorPointer, cursor_client_a->GetCursor().native_type());
    154   EXPECT_EQ(ui::kCursorPointer, cursor_client_b->GetCursor().native_type());
    155 
    156   // Verify that hiding the cursor using one cursor client will
    157   // hide it for all root windows. Note that hiding the cursor
    158   // should not disable mouse events.
    159   cursor_client_a->HideCursor();
    160   EXPECT_FALSE(cursor_client_a->IsCursorVisible());
    161   EXPECT_FALSE(cursor_client_b->IsCursorVisible());
    162   EXPECT_TRUE(cursor_client_a->IsMouseEventsEnabled());
    163   EXPECT_TRUE(cursor_client_b->IsMouseEventsEnabled());
    164 
    165   // Verify that the visibility state cannot be changed using one
    166   // cursor client when the cursor was locked using another.
    167   cursor_client_b->LockCursor();
    168   cursor_client_a->ShowCursor();
    169   EXPECT_FALSE(cursor_client_a->IsCursorVisible());
    170   EXPECT_FALSE(cursor_client_b->IsCursorVisible());
    171 
    172   // Verify the cursor becomes visible on unlock (since a request
    173   // to make it visible was queued up while the cursor was locked).
    174   cursor_client_b->UnlockCursor();
    175   EXPECT_TRUE(cursor_client_a->IsCursorVisible());
    176   EXPECT_TRUE(cursor_client_b->IsCursorVisible());
    177 }
    178 
    179 // Verifies FocusController doesn't attempt to access |content_window_| during
    180 // destruction. Previously the FocusController was destroyed after the window.
    181 // This could be problematic as FocusController references |content_window_| and
    182 // could attempt to use it after |content_window_| was destroyed. This test
    183 // verifies this doesn't happen. Note that this test only failed under ASAN.
    184 TEST_F(DesktopNativeWidgetAuraTest, DontAccessContentWindowDuringDestruction) {
    185   aura::test::TestWindowDelegate delegate;
    186   {
    187     Widget widget;
    188     Widget::InitParams init_params =
    189         CreateParams(Widget::InitParams::TYPE_WINDOW);
    190     init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    191     DesktopNativeWidgetAura* desktop_native_widget_aura =
    192         new DesktopNativeWidgetAura(&widget);
    193     init_params.native_widget = desktop_native_widget_aura;
    194     widget.Init(init_params);
    195 
    196     // Owned by |widget|.
    197     aura::Window* window = new aura::Window(&delegate);
    198     window->Show();
    199     widget.GetNativeWindow()->parent()->AddChild(window);
    200 
    201     widget.Show();
    202   }
    203 }
    204 
    205 void QuitNestedLoopAndCloseWidget(scoped_ptr<Widget> widget,
    206                                   base::Closure* quit_runloop) {
    207   quit_runloop->Run();
    208 }
    209 
    210 // Verifies that a widget can be destroyed when running a nested message-loop.
    211 TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) {
    212   scoped_ptr<Widget> widget(new Widget);
    213   Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
    214   params.bounds = gfx::Rect(0, 0, 200, 200);
    215   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    216   params.native_widget = new DesktopNativeWidgetAura(widget.get());
    217   widget->Init(params);
    218   widget->Show();
    219 
    220   aura::Window* window = widget->GetNativeView();
    221   aura::Window* root = window->GetRootWindow();
    222   aura::client::DispatcherClient* client =
    223       aura::client::GetDispatcherClient(root);
    224 
    225   // Post a task that terminates the nested loop and destroyes the widget. This
    226   // task will be executed from the nested loop initiated with the call to
    227   // |RunWithDispatcher()| below.
    228   aura::client::DispatcherRunLoop run_loop(client, NULL);
    229   base::Closure quit_runloop = run_loop.QuitClosure();
    230   message_loop()->PostTask(FROM_HERE,
    231                            base::Bind(&QuitNestedLoopAndCloseWidget,
    232                                       base::Passed(&widget),
    233                                       base::Unretained(&quit_runloop)));
    234   run_loop.Run();
    235 }
    236 
    237 // This class provides functionality to create fullscreen and top level popup
    238 // windows. It additionally tests whether the destruction of these windows
    239 // occurs correctly in desktop AURA without crashing.
    240 // It provides facilities to test the following cases:-
    241 // 1. Child window destroyed which should lead to the destruction of the
    242 //    parent.
    243 // 2. Parent window destroyed which should lead to the child being destroyed.
    244 class DesktopAuraTopLevelWindowTest
    245     : public views::TestViewsDelegate,
    246       public aura::WindowObserver {
    247  public:
    248   DesktopAuraTopLevelWindowTest()
    249       : top_level_widget_(NULL),
    250         owned_window_(NULL),
    251         owner_destroyed_(false),
    252         owned_window_destroyed_(false) {}
    253 
    254   virtual ~DesktopAuraTopLevelWindowTest() {
    255     EXPECT_TRUE(owner_destroyed_);
    256     EXPECT_TRUE(owned_window_destroyed_);
    257     top_level_widget_ = NULL;
    258     owned_window_ = NULL;
    259   }
    260 
    261   // views::TestViewsDelegate overrides.
    262   virtual void OnBeforeWidgetInit(
    263       Widget::InitParams* params,
    264       internal::NativeWidgetDelegate* delegate) OVERRIDE {
    265     if (!params->native_widget)
    266       params->native_widget = new views::DesktopNativeWidgetAura(delegate);
    267   }
    268 
    269   void CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) {
    270     Widget::InitParams init_params;
    271     init_params.type = Widget::InitParams::TYPE_WINDOW;
    272     init_params.bounds = bounds;
    273     init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    274     init_params.layer_type = aura::WINDOW_LAYER_NOT_DRAWN;
    275     init_params.accept_events = fullscreen;
    276 
    277     widget_.Init(init_params);
    278 
    279     owned_window_ = new aura::Window(&child_window_delegate_);
    280     owned_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
    281     owned_window_->SetName("TestTopLevelWindow");
    282     if (fullscreen) {
    283       owned_window_->SetProperty(aura::client::kShowStateKey,
    284                                  ui::SHOW_STATE_FULLSCREEN);
    285     } else {
    286       owned_window_->SetType(ui::wm::WINDOW_TYPE_MENU);
    287     }
    288     owned_window_->Init(aura::WINDOW_LAYER_TEXTURED);
    289     aura::client::ParentWindowWithContext(
    290         owned_window_,
    291         widget_.GetNativeView()->GetRootWindow(),
    292         gfx::Rect(0, 0, 1900, 1600));
    293     owned_window_->Show();
    294     owned_window_->AddObserver(this);
    295 
    296     ASSERT_TRUE(owned_window_->parent() != NULL);
    297     owned_window_->parent()->AddObserver(this);
    298 
    299     top_level_widget_ =
    300         views::Widget::GetWidgetForNativeView(owned_window_->parent());
    301     ASSERT_TRUE(top_level_widget_ != NULL);
    302   }
    303 
    304   void DestroyOwnedWindow() {
    305     ASSERT_TRUE(owned_window_ != NULL);
    306     delete owned_window_;
    307   }
    308 
    309   void DestroyOwnerWindow() {
    310     ASSERT_TRUE(top_level_widget_ != NULL);
    311     top_level_widget_->CloseNow();
    312   }
    313 
    314   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
    315     window->RemoveObserver(this);
    316     if (window == owned_window_) {
    317       owned_window_destroyed_ = true;
    318     } else if (window == top_level_widget_->GetNativeView()) {
    319       owner_destroyed_ = true;
    320     } else {
    321       ADD_FAILURE() << "Unexpected window destroyed callback: " << window;
    322     }
    323   }
    324 
    325   aura::Window* owned_window() {
    326     return owned_window_;
    327   }
    328 
    329   views::Widget* top_level_widget() {
    330     return top_level_widget_;
    331   }
    332 
    333  private:
    334   views::Widget widget_;
    335   views::Widget* top_level_widget_;
    336   aura::Window* owned_window_;
    337   bool owner_destroyed_;
    338   bool owned_window_destroyed_;
    339   aura::test::TestWindowDelegate child_window_delegate_;
    340 
    341   DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest);
    342 };
    343 
    344 typedef WidgetTest DesktopAuraWidgetTest;
    345 
    346 TEST_F(DesktopAuraWidgetTest, FullscreenWindowDestroyedBeforeOwnerTest) {
    347   ViewsDelegate::views_delegate = NULL;
    348   DesktopAuraTopLevelWindowTest fullscreen_window;
    349   ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
    350       gfx::Rect(0, 0, 200, 200), true));
    351 
    352   RunPendingMessages();
    353   ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow());
    354   RunPendingMessages();
    355 }
    356 
    357 TEST_F(DesktopAuraWidgetTest, FullscreenWindowOwnerDestroyed) {
    358   ViewsDelegate::views_delegate = NULL;
    359 
    360   DesktopAuraTopLevelWindowTest fullscreen_window;
    361   ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
    362       gfx::Rect(0, 0, 200, 200), true));
    363 
    364   RunPendingMessages();
    365   ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow());
    366   RunPendingMessages();
    367 }
    368 
    369 TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupTest) {
    370   ViewsDelegate::views_delegate = NULL;
    371   DesktopAuraTopLevelWindowTest popup_window;
    372   ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
    373       gfx::Rect(0, 0, 200, 200), false));
    374 
    375   RunPendingMessages();
    376   ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
    377   RunPendingMessages();
    378 }
    379 
    380 // This test validates that when a top level owned popup Aura window is
    381 // resized, the widget is resized as well.
    382 TEST_F(DesktopAuraWidgetTest, TopLevelOwnedPopupResizeTest) {
    383   ViewsDelegate::views_delegate = NULL;
    384   DesktopAuraTopLevelWindowTest popup_window;
    385   ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
    386       gfx::Rect(0, 0, 200, 200), false));
    387 
    388   gfx::Rect new_size(0, 0, 400, 400);
    389   popup_window.owned_window()->SetBounds(new_size);
    390 
    391   EXPECT_EQ(popup_window.top_level_widget()->GetNativeView()->bounds().size(),
    392             new_size.size());
    393   RunPendingMessages();
    394   ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
    395   RunPendingMessages();
    396 }
    397 
    398 }  // namespace test
    399 }  // namespace views
    400