shaped_app_window_targeter_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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
16class 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  apps::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
60TEST_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
99TEST_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.
164TEST_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