Home | History | Annotate | Download | only in widget
      1 // Copyright (c) 2012 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/native_widget_aura.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/command_line.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "testing/gtest/include/gtest/gtest.h"
     12 #include "ui/aura/client/aura_constants.h"
     13 #include "ui/aura/env.h"
     14 #include "ui/aura/layout_manager.h"
     15 #include "ui/aura/test/aura_test_base.h"
     16 #include "ui/aura/window.h"
     17 #include "ui/aura/window_tree_host.h"
     18 #include "ui/events/event.h"
     19 #include "ui/events/event_utils.h"
     20 #include "ui/gfx/screen.h"
     21 #include "ui/views/layout/fill_layout.h"
     22 #include "ui/views/widget/root_view.h"
     23 #include "ui/views/widget/widget_delegate.h"
     24 #include "ui/wm/core/default_activation_client.h"
     25 
     26 namespace views {
     27 namespace {
     28 
     29 NativeWidgetAura* Init(aura::Window* parent, Widget* widget) {
     30   Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
     31   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     32   params.parent = parent;
     33   widget->Init(params);
     34   return static_cast<NativeWidgetAura*>(widget->native_widget());
     35 }
     36 
     37 class NativeWidgetAuraTest : public aura::test::AuraTestBase {
     38  public:
     39   NativeWidgetAuraTest() {}
     40   virtual ~NativeWidgetAuraTest() {}
     41 
     42   // testing::Test overrides:
     43   virtual void SetUp() OVERRIDE {
     44     AuraTestBase::SetUp();
     45     new wm::DefaultActivationClient(root_window());
     46     host()->SetBounds(gfx::Rect(640, 480));
     47   }
     48 
     49  private:
     50   DISALLOW_COPY_AND_ASSIGN(NativeWidgetAuraTest);
     51 };
     52 
     53 TEST_F(NativeWidgetAuraTest, CenterWindowLargeParent) {
     54   // Make a parent window larger than the host represented by
     55   // WindowEventDispatcher.
     56   scoped_ptr<aura::Window> parent(new aura::Window(NULL));
     57   parent->Init(aura::WINDOW_LAYER_NOT_DRAWN);
     58   parent->SetBounds(gfx::Rect(0, 0, 1024, 800));
     59   scoped_ptr<Widget> widget(new Widget());
     60   NativeWidgetAura* window = Init(parent.get(), widget.get());
     61 
     62   window->CenterWindow(gfx::Size(100, 100));
     63   EXPECT_EQ(gfx::Rect( (640 - 100) / 2,
     64                        (480 - 100) / 2,
     65                        100, 100),
     66             window->GetNativeWindow()->bounds());
     67   widget->CloseNow();
     68 }
     69 
     70 TEST_F(NativeWidgetAuraTest, CenterWindowSmallParent) {
     71   // Make a parent window smaller than the host represented by
     72   // WindowEventDispatcher.
     73   scoped_ptr<aura::Window> parent(new aura::Window(NULL));
     74   parent->Init(aura::WINDOW_LAYER_NOT_DRAWN);
     75   parent->SetBounds(gfx::Rect(0, 0, 480, 320));
     76   scoped_ptr<Widget> widget(new Widget());
     77   NativeWidgetAura* window = Init(parent.get(), widget.get());
     78 
     79   window->CenterWindow(gfx::Size(100, 100));
     80   EXPECT_EQ(gfx::Rect( (480 - 100) / 2,
     81                        (320 - 100) / 2,
     82                        100, 100),
     83             window->GetNativeWindow()->bounds());
     84   widget->CloseNow();
     85 }
     86 
     87 // Verifies CenterWindow() constrains to parent size.
     88 TEST_F(NativeWidgetAuraTest, CenterWindowSmallParentNotAtOrigin) {
     89   // Make a parent window smaller than the host represented by
     90   // WindowEventDispatcher and offset it slightly from the origin.
     91   scoped_ptr<aura::Window> parent(new aura::Window(NULL));
     92   parent->Init(aura::WINDOW_LAYER_NOT_DRAWN);
     93   parent->SetBounds(gfx::Rect(20, 40, 480, 320));
     94   scoped_ptr<Widget> widget(new Widget());
     95   NativeWidgetAura* window = Init(parent.get(), widget.get());
     96   window->CenterWindow(gfx::Size(500, 600));
     97 
     98   // |window| should be no bigger than |parent|.
     99   EXPECT_EQ("20,40 480x320", window->GetNativeWindow()->bounds().ToString());
    100   widget->CloseNow();
    101 }
    102 
    103 class TestLayoutManagerBase : public aura::LayoutManager {
    104  public:
    105   TestLayoutManagerBase() {}
    106   virtual ~TestLayoutManagerBase() {}
    107 
    108   // aura::LayoutManager:
    109   virtual void OnWindowResized() OVERRIDE {}
    110   virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {}
    111   virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
    112   virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
    113   virtual void OnChildWindowVisibilityChanged(aura::Window* child,
    114                                               bool visible) OVERRIDE {}
    115   virtual void SetChildBounds(aura::Window* child,
    116                               const gfx::Rect& requested_bounds) OVERRIDE {}
    117 
    118  private:
    119   DISALLOW_COPY_AND_ASSIGN(TestLayoutManagerBase);
    120 };
    121 
    122 // Used by ShowMaximizedDoesntBounceAround. See it for details.
    123 class MaximizeLayoutManager : public TestLayoutManagerBase {
    124  public:
    125   MaximizeLayoutManager() {}
    126   virtual ~MaximizeLayoutManager() {}
    127 
    128  private:
    129   // aura::LayoutManager:
    130   virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
    131     // This simulates what happens when adding a maximized window.
    132     SetChildBoundsDirect(child, gfx::Rect(0, 0, 300, 300));
    133   }
    134 
    135   DISALLOW_COPY_AND_ASSIGN(MaximizeLayoutManager);
    136 };
    137 
    138 // This simulates BrowserView, which creates a custom RootView so that
    139 // OnNativeWidgetSizeChanged that is invoked during Init matters.
    140 class TestWidget : public views::Widget {
    141  public:
    142   TestWidget() : did_size_change_more_than_once_(false) {
    143   }
    144 
    145   // Returns true if the size changes to a non-empty size, and then to another
    146   // size.
    147   bool did_size_change_more_than_once() const {
    148     return did_size_change_more_than_once_;
    149   }
    150 
    151   virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) OVERRIDE {
    152     if (last_size_.IsEmpty())
    153       last_size_ = new_size;
    154     else if (!did_size_change_more_than_once_ && new_size != last_size_)
    155       did_size_change_more_than_once_ = true;
    156     Widget::OnNativeWidgetSizeChanged(new_size);
    157   }
    158 
    159  private:
    160   bool did_size_change_more_than_once_;
    161   gfx::Size last_size_;
    162 
    163   DISALLOW_COPY_AND_ASSIGN(TestWidget);
    164 };
    165 
    166 // Verifies the size of the widget doesn't change more than once during Init if
    167 // the window ends up maximized. This is important as otherwise
    168 // RenderWidgetHostViewAura ends up getting resized during construction, which
    169 // leads to noticable flashes.
    170 TEST_F(NativeWidgetAuraTest, ShowMaximizedDoesntBounceAround) {
    171   root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
    172   root_window()->SetLayoutManager(new MaximizeLayoutManager);
    173   scoped_ptr<TestWidget> widget(new TestWidget());
    174   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
    175   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    176   params.parent = NULL;
    177   params.context = root_window();
    178   params.show_state = ui::SHOW_STATE_MAXIMIZED;
    179   params.bounds = gfx::Rect(10, 10, 100, 200);
    180   widget->Init(params);
    181   EXPECT_FALSE(widget->did_size_change_more_than_once());
    182   widget->CloseNow();
    183 }
    184 
    185 class PropertyTestLayoutManager : public TestLayoutManagerBase {
    186  public:
    187   PropertyTestLayoutManager() : added_(false) {}
    188   virtual ~PropertyTestLayoutManager() {}
    189 
    190   bool added() const { return added_; }
    191 
    192  private:
    193   // aura::LayoutManager:
    194   virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
    195     EXPECT_TRUE(child->GetProperty(aura::client::kCanMaximizeKey));
    196     EXPECT_TRUE(child->GetProperty(aura::client::kCanResizeKey));
    197     added_ = true;
    198   }
    199 
    200   bool added_;
    201 
    202   DISALLOW_COPY_AND_ASSIGN(PropertyTestLayoutManager);
    203 };
    204 
    205 class PropertyTestWidgetDelegate : public views::WidgetDelegate {
    206  public:
    207   explicit PropertyTestWidgetDelegate(Widget* widget) : widget_(widget) {}
    208   virtual ~PropertyTestWidgetDelegate() {}
    209 
    210  private:
    211   // views::WidgetDelegate:
    212   virtual bool CanMaximize() const OVERRIDE {
    213     return true;
    214   }
    215   virtual bool CanMinimize() const OVERRIDE {
    216     return true;
    217   }
    218   virtual bool CanResize() const OVERRIDE {
    219     return true;
    220   }
    221   virtual void DeleteDelegate() OVERRIDE {
    222     delete this;
    223   }
    224   virtual Widget* GetWidget() OVERRIDE {
    225     return widget_;
    226   }
    227   virtual const Widget* GetWidget() const OVERRIDE {
    228     return widget_;
    229   }
    230 
    231   Widget* widget_;
    232   DISALLOW_COPY_AND_ASSIGN(PropertyTestWidgetDelegate);
    233 };
    234 
    235 // Verifies that the kCanMaximizeKey/kCanReizeKey have the correct
    236 // value when added to the layout manager.
    237 TEST_F(NativeWidgetAuraTest, TestPropertiesWhenAddedToLayout) {
    238   root_window()->SetBounds(gfx::Rect(0, 0, 640, 480));
    239   PropertyTestLayoutManager* layout_manager = new PropertyTestLayoutManager();
    240   root_window()->SetLayoutManager(layout_manager);
    241   scoped_ptr<TestWidget> widget(new TestWidget());
    242   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
    243   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    244   params.delegate = new PropertyTestWidgetDelegate(widget.get());
    245   params.parent = NULL;
    246   params.context = root_window();
    247   widget->Init(params);
    248   EXPECT_TRUE(layout_manager->added());
    249   widget->CloseNow();
    250 }
    251 
    252 TEST_F(NativeWidgetAuraTest, GetClientAreaScreenBounds) {
    253   // Create a widget.
    254   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
    255   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    256   params.context = root_window();
    257   params.bounds.SetRect(10, 20, 300, 400);
    258   scoped_ptr<Widget> widget(new Widget());
    259   widget->Init(params);
    260 
    261   // For Aura, client area bounds match window bounds.
    262   gfx::Rect client_bounds = widget->GetClientAreaBoundsInScreen();
    263   EXPECT_EQ(10, client_bounds.x());
    264   EXPECT_EQ(20, client_bounds.y());
    265   EXPECT_EQ(300, client_bounds.width());
    266   EXPECT_EQ(400, client_bounds.height());
    267 }
    268 
    269 // View subclass that tracks whether it has gotten a gesture event.
    270 class GestureTrackingView : public views::View {
    271  public:
    272   GestureTrackingView()
    273       : got_gesture_event_(false),
    274         consume_gesture_event_(true) {}
    275 
    276   void set_consume_gesture_event(bool value) {
    277     consume_gesture_event_ = value;
    278   }
    279 
    280   void clear_got_gesture_event() {
    281     got_gesture_event_ = false;
    282   }
    283   bool got_gesture_event() const {
    284     return got_gesture_event_;
    285   }
    286 
    287   // View overrides:
    288   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
    289     got_gesture_event_ = true;
    290     if (consume_gesture_event_)
    291       event->StopPropagation();
    292   }
    293 
    294  private:
    295   // Was OnGestureEvent() invoked?
    296   bool got_gesture_event_;
    297 
    298   // Dictates what OnGestureEvent() returns.
    299   bool consume_gesture_event_;
    300 
    301   DISALLOW_COPY_AND_ASSIGN(GestureTrackingView);
    302 };
    303 
    304 // Verifies a capture isn't set on touch press and that the view that gets
    305 // the press gets the release.
    306 TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) {
    307   // Create two views (both sized the same). |child| is configured not to
    308   // consume the gesture event.
    309   GestureTrackingView* view = new GestureTrackingView();
    310   GestureTrackingView* child = new GestureTrackingView();
    311   child->set_consume_gesture_event(false);
    312   view->SetLayoutManager(new FillLayout);
    313   view->AddChildView(child);
    314   scoped_ptr<TestWidget> widget(new TestWidget());
    315   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    316   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    317   params.context = root_window();
    318   params.bounds = gfx::Rect(0, 0, 100, 200);
    319   widget->Init(params);
    320   widget->SetContentsView(view);
    321   widget->Show();
    322 
    323   ui::TouchEvent press(
    324       ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), 1, ui::EventTimeForNow());
    325   ui::EventDispatchDetails details =
    326       event_processor()->OnEventFromSource(&press);
    327   ASSERT_FALSE(details.dispatcher_destroyed);
    328   // Both views should get the press.
    329   EXPECT_TRUE(view->got_gesture_event());
    330   EXPECT_TRUE(child->got_gesture_event());
    331   view->clear_got_gesture_event();
    332   child->clear_got_gesture_event();
    333   // Touch events should not automatically grab capture.
    334   EXPECT_FALSE(widget->HasCapture());
    335 
    336   // Release touch. Only |view| should get the release since that it consumed
    337   // the press.
    338   ui::TouchEvent release(
    339       ui::ET_TOUCH_RELEASED, gfx::Point(250, 251), 1, ui::EventTimeForNow());
    340   details = event_processor()->OnEventFromSource(&release);
    341   ASSERT_FALSE(details.dispatcher_destroyed);
    342   EXPECT_TRUE(view->got_gesture_event());
    343   EXPECT_FALSE(child->got_gesture_event());
    344   view->clear_got_gesture_event();
    345 
    346   // Work around for bug in NativeWidgetAura.
    347   // TODO: fix bug and remove this.
    348   widget->Close();
    349 }
    350 
    351 // Verifies views with layers are targeted for events properly.
    352 TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) {
    353   // Create two widgets: |parent| and |child|. |child| is a child of |parent|.
    354   views::View* parent_root = new views::View;
    355   scoped_ptr<Widget> parent(new Widget());
    356   Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    357   parent_params.ownership =
    358       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    359   parent_params.context = root_window();
    360   parent->Init(parent_params);
    361   parent->SetContentsView(parent_root);
    362   parent->SetBounds(gfx::Rect(0, 0, 400, 400));
    363   parent->Show();
    364 
    365   scoped_ptr<Widget> child(new Widget());
    366   Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
    367   child_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    368   child_params.parent = parent->GetNativeWindow();
    369   child->Init(child_params);
    370   child->SetBounds(gfx::Rect(0, 0, 200, 200));
    371   child->Show();
    372 
    373   // Point is over |child|.
    374   EXPECT_EQ(child->GetNativeWindow(),
    375             parent->GetNativeWindow()->GetEventHandlerForPoint(
    376                 gfx::Point(50, 50)));
    377 
    378   // Create a view with a layer and stack it at the bottom (below |child|).
    379   views::View* view_with_layer = new views::View;
    380   parent_root->AddChildView(view_with_layer);
    381   view_with_layer->SetBounds(0, 0, 50, 50);
    382   view_with_layer->SetPaintToLayer(true);
    383 
    384   // Make sure that |child| still gets the event.
    385   EXPECT_EQ(child->GetNativeWindow(),
    386             parent->GetNativeWindow()->GetEventHandlerForPoint(
    387                 gfx::Point(20, 20)));
    388 
    389   // Move |view_with_layer| to the top and make sure it gets the
    390   // event when the point is within |view_with_layer|'s bounds.
    391   view_with_layer->layer()->parent()->StackAtTop(
    392       view_with_layer->layer());
    393   EXPECT_EQ(parent->GetNativeWindow(),
    394             parent->GetNativeWindow()->GetEventHandlerForPoint(
    395                 gfx::Point(20, 20)));
    396 
    397   // Point is over |child|, it should get the event.
    398   EXPECT_EQ(child->GetNativeWindow(),
    399             parent->GetNativeWindow()->GetEventHandlerForPoint(
    400                 gfx::Point(70, 70)));
    401 
    402   delete view_with_layer;
    403   view_with_layer = NULL;
    404 
    405   EXPECT_EQ(child->GetNativeWindow(),
    406             parent->GetNativeWindow()->GetEventHandlerForPoint(
    407                 gfx::Point(20, 20)));
    408 
    409   // Work around for bug in NativeWidgetAura.
    410   // TODO: fix bug and remove this.
    411   parent->Close();
    412 }
    413 
    414 // Verifies that widget->FlashFrame() sets aura::client::kDrawAttentionKey,
    415 // and activating the window clears it.
    416 TEST_F(NativeWidgetAuraTest, FlashFrame) {
    417   scoped_ptr<Widget> widget(new Widget());
    418   Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
    419   params.context = root_window();
    420   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    421   widget->Init(params);
    422   aura::Window* window = widget->GetNativeWindow();
    423   EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
    424   widget->FlashFrame(true);
    425   EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey));
    426   widget->FlashFrame(false);
    427   EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
    428   widget->FlashFrame(true);
    429   EXPECT_TRUE(window->GetProperty(aura::client::kDrawAttentionKey));
    430   widget->Activate();
    431   EXPECT_FALSE(window->GetProperty(aura::client::kDrawAttentionKey));
    432 }
    433 
    434 TEST_F(NativeWidgetAuraTest, NoCrashOnThemeAfterClose) {
    435   scoped_ptr<aura::Window> parent(new aura::Window(NULL));
    436   parent->Init(aura::WINDOW_LAYER_NOT_DRAWN);
    437   parent->SetBounds(gfx::Rect(0, 0, 480, 320));
    438   scoped_ptr<Widget> widget(new Widget());
    439   Init(parent.get(), widget.get());
    440   widget->Show();
    441   widget->Close();
    442   base::MessageLoop::current()->RunUntilIdle();
    443   widget->GetNativeTheme();  // Shouldn't crash.
    444 }
    445 
    446 // Used to track calls to WidgetDelegate::OnWidgetMove().
    447 class MoveTestWidgetDelegate : public WidgetDelegateView {
    448  public:
    449   MoveTestWidgetDelegate() : got_move_(false) {}
    450   virtual ~MoveTestWidgetDelegate() {}
    451 
    452   void ClearGotMove() { got_move_ = false; }
    453   bool got_move() const { return got_move_; }
    454 
    455   // WidgetDelegate overrides:
    456   virtual void OnWidgetMove() OVERRIDE { got_move_ = true; }
    457 
    458  private:
    459   bool got_move_;
    460 
    461   DISALLOW_COPY_AND_ASSIGN(MoveTestWidgetDelegate);
    462 };
    463 
    464 // This test simulates what happens when a window is normally maximized. That
    465 // is, it's layer is acquired for animation then the window is maximized.
    466 // Acquiring the layer resets the bounds of the window. This test verifies the
    467 // Widget is still notified correctly of a move in this case.
    468 TEST_F(NativeWidgetAuraTest, OnWidgetMovedInvokedAfterAcquireLayer) {
    469   // |delegate| deletes itself when the widget is destroyed.
    470   MoveTestWidgetDelegate* delegate = new MoveTestWidgetDelegate;
    471   Widget* widget =
    472       Widget::CreateWindowWithContextAndBounds(delegate,
    473                                                root_window(),
    474                                                gfx::Rect(10, 10, 100, 200));
    475   widget->Show();
    476   delegate->ClearGotMove();
    477   // Simulate a maximize with animation.
    478   delete widget->GetNativeView()->RecreateLayer().release();
    479   widget->SetBounds(gfx::Rect(0, 0, 500, 500));
    480   EXPECT_TRUE(delegate->got_move());
    481   widget->CloseNow();
    482 }
    483 
    484 }  // namespace
    485 }  // namespace views
    486