1 // Copyright (c) 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.
5 #include "ui/aura/window_targeter.h"
7 #include "ui/aura/scoped_window_targeter.h"
8 #include "ui/aura/test/aura_test_base.h"
9 #include "ui/aura/test/test_window_delegate.h"
10 #include "ui/aura/window.h"
11 #include "ui/events/event_utils.h"
12 #include "ui/events/test/test_event_handler.h"
16 // Always returns the same window.
17 class StaticWindowTargeter
: public WindowTargeter
{
19 explicit StaticWindowTargeter(aura::Window
* window
)
21 ~StaticWindowTargeter() override
{}
24 // aura::WindowTargeter:
25 Window
* FindTargetForLocatedEvent(Window
* window
,
26 ui::LocatedEvent
* event
) override
{
32 DISALLOW_COPY_AND_ASSIGN(StaticWindowTargeter
);
35 class WindowTargeterTest
: public test::AuraTestBase
{
37 WindowTargeterTest() {}
38 ~WindowTargeterTest() override
{}
40 Window
* root_window() { return AuraTestBase::root_window(); }
43 gfx::RectF
GetEffectiveVisibleBoundsInRootWindow(Window
* window
) {
44 gfx::RectF bounds
= gfx::Rect(window
->bounds().size());
45 Window
* root
= window
->GetRootWindow();
46 CHECK(window
->layer());
48 gfx::Transform transform
;
49 if (!window
->layer()->GetTargetTransformRelativeTo(root
->layer(), &transform
))
51 transform
.TransformRect(&bounds
);
55 TEST_F(WindowTargeterTest
, Basic
) {
56 test::TestWindowDelegate delegate
;
57 scoped_ptr
<Window
> window(CreateNormalWindow(1, root_window(), &delegate
));
58 Window
* one
= CreateNormalWindow(2, window
.get(), &delegate
);
59 Window
* two
= CreateNormalWindow(3, window
.get(), &delegate
);
61 window
->SetBounds(gfx::Rect(0, 0, 100, 100));
62 one
->SetBounds(gfx::Rect(0, 0, 500, 100));
63 two
->SetBounds(gfx::Rect(501, 0, 500, 1000));
65 root_window()->Show();
67 ui::test::TestEventHandler handler
;
68 one
->AddPreTargetHandler(&handler
);
70 ui::MouseEvent
press(ui::ET_MOUSE_PRESSED
, gfx::Point(20, 20),
71 gfx::Point(20, 20), ui::EventTimeForNow(), ui::EF_NONE
,
73 DispatchEventUsingWindowDispatcher(&press
);
74 EXPECT_EQ(1, handler
.num_mouse_events());
77 DispatchEventUsingWindowDispatcher(&press
);
78 EXPECT_EQ(1, handler
.num_mouse_events());
80 one
->RemovePreTargetHandler(&handler
);
83 TEST_F(WindowTargeterTest
, ScopedWindowTargeter
) {
84 test::TestWindowDelegate delegate
;
85 scoped_ptr
<Window
> window(CreateNormalWindow(1, root_window(), &delegate
));
86 Window
* child
= CreateNormalWindow(2, window
.get(), &delegate
);
88 window
->SetBounds(gfx::Rect(30, 30, 100, 100));
89 child
->SetBounds(gfx::Rect(20, 20, 50, 50));
90 root_window()->Show();
92 ui::EventTarget
* root
= root_window();
93 ui::EventTargeter
* targeter
= root
->GetEventTargeter();
95 gfx::Point
event_location(60, 60);
97 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
98 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
99 EXPECT_EQ(child
, targeter
->FindTargetForEvent(root
, &mouse
));
102 // Install a targeter on |window| so that the events never reach the child.
103 scoped_ptr
<ScopedWindowTargeter
> scoped_targeter(
104 new ScopedWindowTargeter(window
.get(), scoped_ptr
<ui::EventTargeter
>(
105 new StaticWindowTargeter(window
.get()))));
107 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
108 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
109 EXPECT_EQ(window
.get(), targeter
->FindTargetForEvent(root
, &mouse
));
111 scoped_targeter
.reset();
113 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
114 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
115 EXPECT_EQ(child
, targeter
->FindTargetForEvent(root
, &mouse
));
119 // Test that ScopedWindowTargeter does not crash if the window for which it
120 // replaces the targeter gets destroyed before it does.
121 TEST_F(WindowTargeterTest
, ScopedWindowTargeterWindowDestroyed
) {
122 test::TestWindowDelegate delegate
;
123 scoped_ptr
<Window
> window(CreateNormalWindow(1, root_window(), &delegate
));
124 scoped_ptr
<ScopedWindowTargeter
> scoped_targeter(
125 new ScopedWindowTargeter(window
.get(), scoped_ptr
<ui::EventTargeter
>(
126 new StaticWindowTargeter(window
.get()))));
129 scoped_targeter
.reset();
134 TEST_F(WindowTargeterTest
, TargetTransformedWindow
) {
135 root_window()->Show();
137 test::TestWindowDelegate delegate
;
138 scoped_ptr
<Window
> window(CreateNormalWindow(2, root_window(), &delegate
));
140 const gfx::Rect
window_bounds(100, 20, 400, 80);
141 window
->SetBounds(window_bounds
);
143 ui::EventTarget
* root_target
= root_window();
144 ui::EventTargeter
* targeter
= root_target
->GetEventTargeter();
145 gfx::Point
event_location(490, 50);
147 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
148 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
149 EXPECT_EQ(window
.get(), targeter
->FindTargetForEvent(root_target
, &mouse
));
152 // Scale |window| by 50%. This should move it away from underneath
153 // |event_location|, so an event in that location will not be targeted to it.
154 gfx::Transform transform
;
155 transform
.Scale(0.5, 0.5);
156 window
->SetTransform(transform
);
157 EXPECT_EQ(gfx::RectF(100, 20, 200, 40).ToString(),
158 GetEffectiveVisibleBoundsInRootWindow(window
.get()).ToString());
160 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
161 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
162 EXPECT_EQ(root_window(), targeter
->FindTargetForEvent(root_target
, &mouse
));
165 transform
= gfx::Transform();
166 transform
.Translate(200, 10);
167 transform
.Scale(0.5, 0.5);
168 window
->SetTransform(transform
);
169 EXPECT_EQ(gfx::RectF(300, 30, 200, 40).ToString(),
170 GetEffectiveVisibleBoundsInRootWindow(window
.get()).ToString());
172 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, event_location
, event_location
,
173 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
174 EXPECT_EQ(window
.get(), targeter
->FindTargetForEvent(root_target
, &mouse
));
178 class IdCheckingEventTargeter
: public WindowTargeter
{
180 IdCheckingEventTargeter(int id
) : id_(id
) {}
181 ~IdCheckingEventTargeter() override
{}
185 bool SubtreeShouldBeExploredForEvent(Window
* window
,
186 const ui::LocatedEvent
& event
) override
{
187 return (window
->id() == id_
&&
188 WindowTargeter::SubtreeShouldBeExploredForEvent(window
, event
));
195 TEST_F(WindowTargeterTest
, Bounds
) {
196 test::TestWindowDelegate delegate
;
197 scoped_ptr
<Window
> parent(CreateNormalWindow(1, root_window(), &delegate
));
198 scoped_ptr
<Window
> child(CreateNormalWindow(1, parent
.get(), &delegate
));
199 scoped_ptr
<Window
> grandchild(CreateNormalWindow(1, child
.get(), &delegate
));
201 parent
->SetBounds(gfx::Rect(0, 0, 30, 30));
202 child
->SetBounds(gfx::Rect(5, 5, 20, 20));
203 grandchild
->SetBounds(gfx::Rect(5, 5, 5, 5));
205 ASSERT_EQ(1u, root_window()->children().size());
206 ASSERT_EQ(1u, root_window()->children()[0]->children().size());
207 ASSERT_EQ(1u, root_window()->children()[0]->children()[0]->children().size());
209 Window
* parent_r
= root_window()->children()[0];
210 Window
* child_r
= parent_r
->children()[0];
211 Window
* grandchild_r
= child_r
->children()[0];
213 ui::EventTarget
* root_target
= root_window();
214 ui::EventTargeter
* targeter
= root_target
->GetEventTargeter();
216 // Dispatch a mouse event that falls on the parent, but not on the child. When
217 // the default event-targeter used, the event will still reach |grandchild|,
218 // because the default targeter does not look at the bounds.
219 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, gfx::Point(1, 1), gfx::Point(1, 1),
220 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
221 EXPECT_EQ(parent_r
, targeter
->FindTargetForEvent(root_target
, &mouse
));
223 // Install a targeter on the |child| that looks at the window id as well
224 // as the bounds and makes sure the event reaches the target only if the id of
225 // the window is equal to 2 (incorrect). This causes the event to get handled
227 ui::MouseEvent
mouse2(ui::ET_MOUSE_MOVED
, gfx::Point(8, 8), gfx::Point(8, 8),
228 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
229 scoped_ptr
<ui::EventTargeter
> original_targeter
= child_r
->SetEventTargeter(
230 scoped_ptr
<ui::EventTargeter
>(new IdCheckingEventTargeter(2)));
231 EXPECT_EQ(parent_r
, targeter
->FindTargetForEvent(root_target
, &mouse2
));
233 // Now install a targeter on the |child| that looks at the window id as well
234 // as the bounds and makes sure the event reaches the target only if the id of
235 // the window is equal to 1 (correct).
236 ui::MouseEvent
mouse3(ui::ET_MOUSE_MOVED
, gfx::Point(8, 8), gfx::Point(8, 8),
237 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
238 child_r
->SetEventTargeter(
239 scoped_ptr
<ui::EventTargeter
>(new IdCheckingEventTargeter(1)));
240 EXPECT_EQ(child_r
, targeter
->FindTargetForEvent(root_target
, &mouse3
));
242 // restore original WindowTargeter for |child|.
243 child_r
->SetEventTargeter(original_targeter
.Pass());
245 // Target |grandchild| location.
246 ui::MouseEvent
second(ui::ET_MOUSE_MOVED
, gfx::Point(12, 12),
247 gfx::Point(12, 12), ui::EventTimeForNow(), ui::EF_NONE
,
249 EXPECT_EQ(grandchild_r
, targeter
->FindTargetForEvent(root_target
, &second
));
251 // Target |child| location.
252 ui::MouseEvent
third(ui::ET_MOUSE_MOVED
, gfx::Point(8, 8), gfx::Point(8, 8),
253 ui::EventTimeForNow(), ui::EF_NONE
, ui::EF_NONE
);
254 EXPECT_EQ(child_r
, targeter
->FindTargetForEvent(root_target
, &third
));
257 class IgnoreWindowTargeter
: public WindowTargeter
{
259 IgnoreWindowTargeter() {}
260 ~IgnoreWindowTargeter() override
{}
264 bool SubtreeShouldBeExploredForEvent(Window
* window
,
265 const ui::LocatedEvent
& event
) override
{
270 // Verifies that an EventTargeter installed on an EventTarget can dictate
271 // whether the target itself can process an event.
272 TEST_F(WindowTargeterTest
, TargeterChecksOwningEventTarget
) {
273 test::TestWindowDelegate delegate
;
274 scoped_ptr
<Window
> child(CreateNormalWindow(1, root_window(), &delegate
));
276 ui::EventTarget
* root_target
= root_window();
277 ui::EventTargeter
* targeter
= root_target
->GetEventTargeter();
279 ui::MouseEvent
mouse(ui::ET_MOUSE_MOVED
, gfx::Point(10, 10),
280 gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE
,
282 EXPECT_EQ(child
.get(), targeter
->FindTargetForEvent(root_target
, &mouse
));
284 // Install an event targeter on |child| which always prevents the target from
286 child
->SetEventTargeter(
287 scoped_ptr
<ui::EventTargeter
>(new IgnoreWindowTargeter()));
289 ui::MouseEvent
mouse2(ui::ET_MOUSE_MOVED
, gfx::Point(10, 10),
290 gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE
,
292 EXPECT_EQ(root_window(), targeter
->FindTargetForEvent(root_target
, &mouse2
));