Home | History | Annotate | Download | only in apps
      1 // Copyright 2014 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 "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
      6 
      7 #include "apps/ui/views/app_window_frame_view.h"
      8 #include "chrome/browser/ui/views/apps/chrome_native_app_window_views.h"
      9 #include "ui/aura/test/aura_test_base.h"
     10 #include "ui/aura/window.h"
     11 #include "ui/aura/window_event_dispatcher.h"
     12 #include "ui/views/controls/webview/webview.h"
     13 #include "ui/wm/core/default_activation_client.h"
     14 #include "ui/wm/core/easy_resize_window_targeter.h"
     15 
     16 class ShapedAppWindowTargeterTest : public aura::test::AuraTestBase {
     17  public:
     18   ShapedAppWindowTargeterTest()
     19       : web_view_(NULL) {
     20   }
     21 
     22   virtual ~ShapedAppWindowTargeterTest() {}
     23 
     24   views::Widget* widget() { return widget_.get(); }
     25 
     26   extensions::NativeAppWindow* app_window() { return &app_window_; }
     27   ChromeNativeAppWindowViews* app_window_views() { return &app_window_; }
     28 
     29  protected:
     30   virtual void SetUp() OVERRIDE {
     31     aura::test::AuraTestBase::SetUp();
     32     new wm::DefaultActivationClient(root_window());
     33     widget_.reset(new views::Widget);
     34     views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
     35     params.remove_standard_frame = true;
     36     params.bounds = gfx::Rect(30, 30, 100, 100);
     37     params.context = root_window();
     38     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     39     widget_->Init(params);
     40 
     41     app_window_.set_web_view_for_testing(&web_view_);
     42     app_window_.set_window_for_testing(widget_.get());
     43 
     44     widget_->Show();
     45   }
     46 
     47   virtual void TearDown() OVERRIDE {
     48     widget_.reset();
     49     aura::test::AuraTestBase::TearDown();
     50   }
     51 
     52  private:
     53   views::WebView web_view_;
     54   scoped_ptr<views::Widget> widget_;
     55   ChromeNativeAppWindowViews app_window_;
     56 
     57   DISALLOW_COPY_AND_ASSIGN(ShapedAppWindowTargeterTest);
     58 };
     59 
     60 TEST_F(ShapedAppWindowTargeterTest, HitTestBasic) {
     61   aura::Window* window = widget()->GetNativeWindow();
     62   {
     63     // Without any custom shapes, the event should be targeted correctly to the
     64     // window.
     65     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
     66                         gfx::Point(40, 40), gfx::Point(40, 40),
     67                         ui::EF_NONE, ui::EF_NONE);
     68     ui::EventDispatchDetails details =
     69         event_processor()->OnEventFromSource(&move);
     70     ASSERT_FALSE(details.dispatcher_destroyed);
     71     EXPECT_EQ(window, move.target());
     72   }
     73 
     74   scoped_ptr<SkRegion> region(new SkRegion);
     75   region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
     76   region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
     77   app_window()->UpdateShape(region.Pass());
     78   {
     79     // With the custom shape, the events that don't fall within the custom shape
     80     // will go through to the root window.
     81     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
     82                         gfx::Point(40, 40), gfx::Point(40, 40),
     83                         ui::EF_NONE, ui::EF_NONE);
     84     ui::EventDispatchDetails details =
     85         event_processor()->OnEventFromSource(&move);
     86     ASSERT_FALSE(details.dispatcher_destroyed);
     87     EXPECT_EQ(root_window(), move.target());
     88 
     89     // But events within the shape will still reach the window.
     90     ui::MouseEvent move2(ui::ET_MOUSE_MOVED,
     91                          gfx::Point(80, 80), gfx::Point(80, 80),
     92                          ui::EF_NONE, ui::EF_NONE);
     93     details = event_processor()->OnEventFromSource(&move2);
     94     ASSERT_FALSE(details.dispatcher_destroyed);
     95     EXPECT_EQ(window, move2.target());
     96   }
     97 }
     98 
     99 TEST_F(ShapedAppWindowTargeterTest, HitTestOnlyForShapedWindow) {
    100   // Install a window-targeter on the root window that allows a window to
    101   // receive events outside of its bounds. Verify that this window-targeter is
    102   // active unless the window has a custom shape.
    103   gfx::Insets inset(-30, -30, -30, -30);
    104   root_window()->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
    105       new wm::EasyResizeWindowTargeter(root_window(), inset, inset)));
    106 
    107   aura::Window* window = widget()->GetNativeWindow();
    108   {
    109     // Without any custom shapes, an event within the window bounds should be
    110     // targeted correctly to the window.
    111     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    112                         gfx::Point(40, 40), gfx::Point(40, 40),
    113                         ui::EF_NONE, ui::EF_NONE);
    114     ui::EventDispatchDetails details =
    115         event_processor()->OnEventFromSource(&move);
    116     ASSERT_FALSE(details.dispatcher_destroyed);
    117     EXPECT_EQ(window, move.target());
    118   }
    119   {
    120     // Without any custom shapes, an event that falls just outside the window
    121     // bounds should also be targeted correctly to the window, because of the
    122     // targeter installed on the root-window.
    123     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    124                         gfx::Point(10, 10), gfx::Point(10, 10),
    125                         ui::EF_NONE, ui::EF_NONE);
    126     ui::EventDispatchDetails details =
    127         event_processor()->OnEventFromSource(&move);
    128     ASSERT_FALSE(details.dispatcher_destroyed);
    129     EXPECT_EQ(window, move.target());
    130   }
    131 
    132   scoped_ptr<SkRegion> region(new SkRegion);
    133   region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
    134   region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
    135   app_window()->UpdateShape(region.Pass());
    136   {
    137     // With the custom shape, the events that don't fall within the custom shape
    138     // will go through to the root window.
    139     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    140                         gfx::Point(10, 10), gfx::Point(10, 10),
    141                         ui::EF_NONE, ui::EF_NONE);
    142     ui::EventDispatchDetails details =
    143         event_processor()->OnEventFromSource(&move);
    144     ASSERT_FALSE(details.dispatcher_destroyed);
    145     EXPECT_EQ(root_window(), move.target());
    146   }
    147 
    148   // Remove the custom shape. This should restore the behaviour of targeting the
    149   // app window for events just outside its bounds.
    150   app_window()->UpdateShape(scoped_ptr<SkRegion>());
    151   {
    152     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    153                         gfx::Point(10, 10), gfx::Point(10, 10),
    154                         ui::EF_NONE, ui::EF_NONE);
    155     ui::EventDispatchDetails details =
    156         event_processor()->OnEventFromSource(&move);
    157     ASSERT_FALSE(details.dispatcher_destroyed);
    158     EXPECT_EQ(window, move.target());
    159   }
    160 }
    161 
    162 // Tests targeting of events on a window with an EasyResizeWindowTargeter
    163 // installed on its container.
    164 TEST_F(ShapedAppWindowTargeterTest, ResizeInsetsWithinBounds) {
    165   aura::Window* window = widget()->GetNativeWindow();
    166   {
    167     // An event in the center of the window should always have
    168     // |window| as its target.
    169     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    170                         gfx::Point(80, 80), gfx::Point(80, 80),
    171                         ui::EF_NONE, ui::EF_NONE);
    172     ui::EventDispatchDetails details =
    173         event_processor()->OnEventFromSource(&move);
    174     ASSERT_FALSE(details.dispatcher_destroyed);
    175     EXPECT_EQ(window, move.target());
    176   }
    177   {
    178     // Without an EasyResizeTargeter on the container, an event
    179     // inside the window and within 5px of an edge should have
    180     // |window| as its target.
    181     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    182                         gfx::Point(32, 37), gfx::Point(32, 37),
    183                         ui::EF_NONE, ui::EF_NONE);
    184     ui::EventDispatchDetails details =
    185         event_processor()->OnEventFromSource(&move);
    186     ASSERT_FALSE(details.dispatcher_destroyed);
    187     EXPECT_EQ(window, move.target());
    188   }
    189 
    190 #if !defined(OS_CHROMEOS)
    191   // The non standard app frame has a easy resize targetter installed.
    192   scoped_ptr<apps::AppWindowFrameView> frame(
    193       app_window_views()->CreateNonStandardAppFrame());
    194   {
    195     // Ensure that the window has an event targeter (there should be an
    196     // EasyResizeWindowTargeter installed).
    197     EXPECT_TRUE(static_cast<ui::EventTarget*>(window)->GetEventTargeter());
    198   }
    199   {
    200     // An event in the center of the window should always have
    201     // |window| as its target.
    202     // TODO(mgiuca): This isn't really testing anything (note that it has the
    203     // same expectation as the border case below). In the real environment, the
    204     // target will actually be the RenderWidgetHostViewAura's window that is the
    205     // child of the child of |window|, whereas in the border case it *will* be
    206     // |window|. However, since this test environment does not have a
    207     // RenderWidgetHostViewAura, we cannot differentiate the two cases. Fix
    208     // the test environment so that the test can assert that non-border events
    209     // bubble down to a child of |window|.
    210     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    211                         gfx::Point(80, 80), gfx::Point(80, 80),
    212                         ui::EF_NONE, ui::EF_NONE);
    213     ui::EventDispatchDetails details =
    214         event_processor()->OnEventFromSource(&move);
    215     ASSERT_FALSE(details.dispatcher_destroyed);
    216     EXPECT_EQ(window, move.target());
    217   }
    218   {
    219     // With an EasyResizeTargeter on the container, an event
    220     // inside the window and within 5px of an edge should have
    221     // |window| as its target.
    222     ui::MouseEvent move(ui::ET_MOUSE_MOVED,
    223                         gfx::Point(32, 37), gfx::Point(32, 37),
    224                         ui::EF_NONE, ui::EF_NONE);
    225     ui::EventDispatchDetails details =
    226         event_processor()->OnEventFromSource(&move);
    227     ASSERT_FALSE(details.dispatcher_destroyed);
    228     EXPECT_EQ(window, move.target());
    229   }
    230 #endif  // defined (OS_CHROMEOS)
    231 }
    232