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.
5 #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
7 #include "apps/ui/views/app_window_frame_view.h"
8 #include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.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/events/event_utils.h"
13 #include "ui/views/controls/webview/webview.h"
14 #include "ui/wm/core/default_activation_client.h"
15 #include "ui/wm/core/easy_resize_window_targeter.h"
17 class ShapedAppWindowTargeterTest
: public aura::test::AuraTestBase
{
19 ShapedAppWindowTargeterTest()
23 ~ShapedAppWindowTargeterTest() override
{}
25 views::Widget
* widget() { return widget_
.get(); }
27 extensions::NativeAppWindow
* app_window() { return &app_window_
; }
28 ChromeNativeAppWindowViewsAura
* app_window_views() { return &app_window_
; }
31 void SetUp() override
{
32 aura::test::AuraTestBase::SetUp();
33 new wm::DefaultActivationClient(root_window());
34 widget_
.reset(new views::Widget
);
35 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
36 params
.remove_standard_frame
= true;
37 params
.bounds
= gfx::Rect(30, 30, 100, 100);
38 params
.context
= root_window();
39 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
40 widget_
->Init(params
);
42 app_window_
.set_web_view_for_testing(&web_view_
);
43 app_window_
.set_window_for_testing(widget_
.get());
48 void TearDown() override
{
50 aura::test::AuraTestBase::TearDown();
54 views::WebView web_view_
;
55 scoped_ptr
<views::Widget
> widget_
;
56 ChromeNativeAppWindowViewsAura app_window_
;
58 DISALLOW_COPY_AND_ASSIGN(ShapedAppWindowTargeterTest
);
61 TEST_F(ShapedAppWindowTargeterTest
, HitTestBasic
) {
62 aura::Window
* window
= widget()->GetNativeWindow();
64 // Without any custom shapes, the event should be targeted correctly to the
66 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(40, 40),
67 gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE
,
69 ui::EventDispatchDetails details
=
70 event_processor()->OnEventFromSource(&move
);
71 ASSERT_FALSE(details
.dispatcher_destroyed
);
72 EXPECT_EQ(window
, move
.target());
75 scoped_ptr
<SkRegion
> region(new SkRegion
);
76 region
->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op
);
77 app_window()->UpdateShape(region
.Pass());
79 // With an empty custom shape, all events within the window should fall
80 // through to the root window.
81 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(40, 40),
82 gfx::Point(40, 40), ui::EventTimeForNow(), 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());
90 // Window shape (global coordinates)
93 // . | | <- mouse move (40,40)
94 // 70 +--------+ +---------+
95 // | . | <- mouse move (80,80)
96 // 90 +--------+ +---------+
99 region
.reset(new SkRegion
);
100 region
->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op
);
101 region
->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op
);
102 app_window()->UpdateShape(region
.Pass());
104 // With the custom shape, the events that don't fall within the custom shape
105 // will go through to the root window.
106 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(40, 40),
107 gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE
,
109 ui::EventDispatchDetails details
=
110 event_processor()->OnEventFromSource(&move
);
111 ASSERT_FALSE(details
.dispatcher_destroyed
);
112 EXPECT_EQ(root_window(), move
.target());
114 // But events within the shape will still reach the window.
115 ui::MouseEvent
move2(ui::ET_MOUSE_MOVED
, gfx::Point(80, 80),
116 gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE
,
118 details
= event_processor()->OnEventFromSource(&move2
);
119 ASSERT_FALSE(details
.dispatcher_destroyed
);
120 EXPECT_EQ(window
, move2
.target());
124 TEST_F(ShapedAppWindowTargeterTest
, HitTestOnlyForShapedWindow
) {
125 // Install a window-targeter on the root window that allows a window to
126 // receive events outside of its bounds. Verify that this window-targeter is
127 // active unless the window has a custom shape.
128 gfx::Insets
inset(-30, -30, -30, -30);
129 root_window()->SetEventTargeter(scoped_ptr
<ui::EventTargeter
>(
130 new wm::EasyResizeWindowTargeter(root_window(), inset
, inset
)));
132 aura::Window
* window
= widget()->GetNativeWindow();
134 // Without any custom shapes, an event within the window bounds should be
135 // targeted correctly to the window.
136 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(40, 40),
137 gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE
,
139 ui::EventDispatchDetails details
=
140 event_processor()->OnEventFromSource(&move
);
141 ASSERT_FALSE(details
.dispatcher_destroyed
);
142 EXPECT_EQ(window
, move
.target());
145 // Without any custom shapes, an event that falls just outside the window
146 // bounds should also be targeted correctly to the window, because of the
147 // targeter installed on the root-window.
148 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(10, 10),
149 gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE
,
151 ui::EventDispatchDetails details
=
152 event_processor()->OnEventFromSource(&move
);
153 ASSERT_FALSE(details
.dispatcher_destroyed
);
154 EXPECT_EQ(window
, move
.target());
157 scoped_ptr
<SkRegion
> region(new SkRegion
);
158 region
->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op
);
159 region
->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op
);
160 app_window()->UpdateShape(region
.Pass());
162 // With the custom shape, the events that don't fall within the custom shape
163 // will go through to the root window.
164 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(10, 10),
165 gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE
,
167 ui::EventDispatchDetails details
=
168 event_processor()->OnEventFromSource(&move
);
169 ASSERT_FALSE(details
.dispatcher_destroyed
);
170 EXPECT_EQ(root_window(), move
.target());
173 // Remove the custom shape. This should restore the behaviour of targeting the
174 // app window for events just outside its bounds.
175 app_window()->UpdateShape(scoped_ptr
<SkRegion
>());
177 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(10, 10),
178 gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE
,
180 ui::EventDispatchDetails details
=
181 event_processor()->OnEventFromSource(&move
);
182 ASSERT_FALSE(details
.dispatcher_destroyed
);
183 EXPECT_EQ(window
, move
.target());
187 // Tests targeting of events on a window with an EasyResizeWindowTargeter
188 // installed on its container.
189 TEST_F(ShapedAppWindowTargeterTest
, ResizeInsetsWithinBounds
) {
190 aura::Window
* window
= widget()->GetNativeWindow();
192 // An event in the center of the window should always have
193 // |window| as its target.
194 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(80, 80),
195 gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE
,
197 ui::EventDispatchDetails details
=
198 event_processor()->OnEventFromSource(&move
);
199 ASSERT_FALSE(details
.dispatcher_destroyed
);
200 EXPECT_EQ(window
, move
.target());
203 // Without an EasyResizeTargeter on the container, an event
204 // inside the window and within 5px of an edge should have
205 // |window| as its target.
206 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(32, 37),
207 gfx::Point(32, 37), ui::EventTimeForNow(), ui::EF_NONE
,
209 ui::EventDispatchDetails details
=
210 event_processor()->OnEventFromSource(&move
);
211 ASSERT_FALSE(details
.dispatcher_destroyed
);
212 EXPECT_EQ(window
, move
.target());
215 #if !defined(OS_CHROMEOS)
216 // The non standard app frame has a easy resize targetter installed.
217 scoped_ptr
<views::NonClientFrameView
> frame(
218 app_window_views()->CreateNonStandardAppFrame());
220 // Ensure that the window has an event targeter (there should be an
221 // EasyResizeWindowTargeter installed).
222 EXPECT_TRUE(static_cast<ui::EventTarget
*>(window
)->GetEventTargeter());
225 // An event in the center of the window should always have
226 // |window| as its target.
227 // TODO(mgiuca): This isn't really testing anything (note that it has the
228 // same expectation as the border case below). In the real environment, the
229 // target will actually be the RenderWidgetHostViewAura's window that is the
230 // child of the child of |window|, whereas in the border case it *will* be
231 // |window|. However, since this test environment does not have a
232 // RenderWidgetHostViewAura, we cannot differentiate the two cases. Fix
233 // the test environment so that the test can assert that non-border events
234 // bubble down to a child of |window|.
235 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(80, 80),
236 gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE
,
238 ui::EventDispatchDetails details
=
239 event_processor()->OnEventFromSource(&move
);
240 ASSERT_FALSE(details
.dispatcher_destroyed
);
241 EXPECT_EQ(window
, move
.target());
244 // With an EasyResizeTargeter on the container, an event
245 // inside the window and within 5px of an edge should have
246 // |window| as its target.
247 ui::MouseEvent
move(ui::ET_MOUSE_MOVED
, gfx::Point(32, 37),
248 gfx::Point(32, 37), ui::EventTimeForNow(), ui::EF_NONE
,
250 ui::EventDispatchDetails details
=
251 event_processor()->OnEventFromSource(&move
);
252 ASSERT_FALSE(details
.dispatcher_destroyed
);
253 EXPECT_EQ(window
, move
.target());
255 #endif // defined (OS_CHROMEOS)