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.
4
5 #include "ui/aura/window_targeter.h"
6
7 #include "ui/aura/client/capture_client.h"
8 #include "ui/aura/client/event_client.h"
9 #include "ui/aura/client/focus_client.h"
10 #include "ui/aura/client/screen_position_client.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_delegate.h"
14 #include "ui/aura/window_event_dispatcher.h"
15 #include "ui/aura/window_tree_host.h"
16 #include "ui/display/display.h"
17 #include "ui/display/screen.h"
18 #include "ui/events/event_target.h"
19 #include "ui/events/event_target_iterator.h"
20
21 namespace aura {
22
23 WindowTargeter::WindowTargeter() = default;
24
25 WindowTargeter::~WindowTargeter() = default;
26
SubtreeShouldBeExploredForEvent(Window * window,const ui::LocatedEvent & event)27 bool WindowTargeter::SubtreeShouldBeExploredForEvent(
28 Window* window,
29 const ui::LocatedEvent& event) {
30 return SubtreeCanAcceptEvent(window, event) &&
31 EventLocationInsideBounds(window, event);
32 }
33
GetHitTestRects(Window * window,gfx::Rect * hit_test_rect_mouse,gfx::Rect * hit_test_rect_touch) const34 bool WindowTargeter::GetHitTestRects(Window* window,
35 gfx::Rect* hit_test_rect_mouse,
36 gfx::Rect* hit_test_rect_touch) const {
37 DCHECK(hit_test_rect_mouse);
38 DCHECK(hit_test_rect_touch);
39 *hit_test_rect_mouse = *hit_test_rect_touch = window->bounds();
40
41 if (ShouldUseExtendedBounds(window)) {
42 hit_test_rect_mouse->Inset(mouse_extend_);
43 hit_test_rect_touch->Inset(touch_extend_);
44 }
45
46 return true;
47 }
48
49 std::unique_ptr<WindowTargeter::HitTestRects>
GetExtraHitTestShapeRects(Window * target) const50 WindowTargeter::GetExtraHitTestShapeRects(Window* target) const {
51 return nullptr;
52 }
53
SetInsets(const gfx::Insets & mouse_and_touch_extend)54 void WindowTargeter::SetInsets(const gfx::Insets& mouse_and_touch_extend) {
55 SetInsets(mouse_and_touch_extend, mouse_and_touch_extend);
56 }
57
SetInsets(const gfx::Insets & mouse_extend,const gfx::Insets & touch_extend)58 void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend,
59 const gfx::Insets& touch_extend) {
60 if (mouse_extend_ == mouse_extend && touch_extend_ == touch_extend)
61 return;
62
63 mouse_extend_ = mouse_extend;
64 touch_extend_ = touch_extend;
65 }
66
GetPriorityTargetInRootWindow(Window * root_window,const ui::LocatedEvent & event)67 Window* WindowTargeter::GetPriorityTargetInRootWindow(
68 Window* root_window,
69 const ui::LocatedEvent& event) {
70 DCHECK_EQ(root_window, root_window->GetRootWindow());
71
72 // Mouse events should be dispatched to the window that processed the
73 // mouse-press events (if any).
74 if (event.IsScrollEvent() || event.IsMouseEvent()) {
75 WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher();
76 if (dispatcher->mouse_pressed_handler())
77 return dispatcher->mouse_pressed_handler();
78 }
79
80 // All events should be directed towards the capture window (if any).
81 Window* capture_window = client::GetCaptureWindow(root_window);
82 if (capture_window)
83 return capture_window;
84
85 if (event.IsPinchEvent()) {
86 DCHECK_EQ(event.AsGestureEvent()->details().device_type(),
87 ui::GestureDeviceType::DEVICE_TOUCHPAD);
88 WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher();
89 if (dispatcher->touchpad_pinch_handler())
90 return dispatcher->touchpad_pinch_handler();
91 }
92
93 if (event.IsTouchEvent()) {
94 // Query the gesture-recognizer to find targets for touch events.
95 const ui::TouchEvent& touch = *event.AsTouchEvent();
96 ui::GestureConsumer* consumer =
97 Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(touch);
98 if (consumer)
99 return static_cast<Window*>(consumer);
100 }
101
102 return nullptr;
103 }
104
FindTargetInRootWindow(Window * root_window,const ui::LocatedEvent & event)105 Window* WindowTargeter::FindTargetInRootWindow(Window* root_window,
106 const ui::LocatedEvent& event) {
107 DCHECK_EQ(root_window, root_window->GetRootWindow());
108
109 Window* priority_target = GetPriorityTargetInRootWindow(root_window, event);
110 if (priority_target)
111 return priority_target;
112
113 if (event.IsTouchEvent()) {
114 // Query the gesture-recognizer to find targets for touch events.
115 const ui::TouchEvent& touch = *event.AsTouchEvent();
116 // GetTouchLockedTarget() is handled in GetPriorityTargetInRootWindow().
117 ui::GestureRecognizer* gesture_recognizer =
118 Env::GetInstance()->gesture_recognizer();
119 DCHECK(!gesture_recognizer->GetTouchLockedTarget(touch));
120 ui::GestureConsumer* consumer = gesture_recognizer->GetTargetForLocation(
121 event.location_f(), touch.source_device_id());
122 if (consumer)
123 return static_cast<Window*>(consumer);
124
125 #if defined(OS_CHROMEOS)
126 // If the initial touch is outside the window's display, target the root.
127 // This is used for bezel gesture events (eg. swiping in from screen edge).
128 display::Display display =
129 display::Screen::GetScreen()->GetDisplayNearestWindow(root_window);
130 // The window target may be null, so use the root's ScreenPositionClient.
131 gfx::Point screen_location = event.root_location();
132 if (client::GetScreenPositionClient(root_window)) {
133 client::GetScreenPositionClient(root_window)
134 ->ConvertPointToScreen(root_window, &screen_location);
135 }
136 if (!display.bounds().Contains(screen_location))
137 return root_window;
138 #else
139 // If the initial touch is outside the root window, target the root.
140 // TODO: this code is likely not necessarily and will be removed.
141 if (!root_window->bounds().Contains(event.location()))
142 return root_window;
143 #endif
144 }
145
146 return nullptr;
147 }
148
ProcessEventIfTargetsDifferentRootWindow(Window * root_window,Window * target,ui::Event * event)149 bool WindowTargeter::ProcessEventIfTargetsDifferentRootWindow(
150 Window* root_window,
151 Window* target,
152 ui::Event* event) {
153 if (root_window->Contains(target))
154 return false;
155
156 // |window| is the root window, but |target| is not a descendent of
157 // |window|. So do not allow dispatching from here. Instead, dispatch the
158 // event through the WindowEventDispatcher that owns |target|.
159 Window* new_root = target->GetRootWindow();
160 DCHECK(new_root);
161 if (event->IsLocatedEvent()) {
162 // The event has been transformed to be in |target|'s coordinate system.
163 // But dispatching the event through the EventProcessor requires the event
164 // to be in the host's coordinate system. So, convert the event to be in
165 // the root's coordinate space, and then to the host's coordinate space by
166 // applying the host's transform.
167 ui::LocatedEvent* located_event = event->AsLocatedEvent();
168 located_event->ConvertLocationToTarget(target, new_root);
169 WindowTreeHost* window_tree_host = new_root->GetHost();
170 located_event->UpdateForRootTransform(
171 window_tree_host->GetRootTransform(),
172 window_tree_host->GetRootTransformForLocalEventCoordinates());
173 }
174 ignore_result(new_root->GetHost()->event_sink()->OnEventFromSource(event));
175 return true;
176 }
177
FindTargetForEvent(ui::EventTarget * root,ui::Event * event)178 ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root,
179 ui::Event* event) {
180 Window* window = static_cast<Window*>(root);
181 Window* target = event->IsKeyEvent()
182 ? FindTargetForKeyEvent(window)
183 : FindTargetForNonKeyEvent(window, event);
184 if (target && !window->parent() &&
185 ProcessEventIfTargetsDifferentRootWindow(window, target, event)) {
186 return nullptr;
187 }
188 return target;
189 }
190
FindNextBestTarget(ui::EventTarget * previous_target,ui::Event * event)191 ui::EventTarget* WindowTargeter::FindNextBestTarget(
192 ui::EventTarget* previous_target,
193 ui::Event* event) {
194 return nullptr;
195 }
196
FindTargetForKeyEvent(Window * window)197 Window* WindowTargeter::FindTargetForKeyEvent(Window* window) {
198 Window* root_window = window->GetRootWindow();
199 client::FocusClient* focus_client = client::GetFocusClient(root_window);
200 if (!focus_client)
201 return window;
202 Window* focused_window = focus_client->GetFocusedWindow();
203 if (!focused_window)
204 return window;
205
206 client::EventClient* event_client = client::GetEventClient(root_window);
207 if (event_client &&
208 !event_client->CanProcessEventsWithinSubtree(focused_window)) {
209 focus_client->FocusWindow(nullptr);
210 return nullptr;
211 }
212 return focused_window ? focused_window : window;
213 }
214
OnInstalled(Window * window)215 void WindowTargeter::OnInstalled(Window* window) {
216 window_ = window;
217 }
218
FindTargetForLocatedEvent(Window * window,ui::LocatedEvent * event)219 Window* WindowTargeter::FindTargetForLocatedEvent(Window* window,
220 ui::LocatedEvent* event) {
221 if (!window->parent()) {
222 Window* target = FindTargetInRootWindow(window, *event);
223 if (target) {
224 window->ConvertEventToTarget(target, event);
225
226 #if defined(OS_CHROMEOS)
227 if (window->IsRootWindow() && event->HasNativeEvent()) {
228 // If window is root, and the target is in a different host, we need to
229 // convert the native event to the target's host as well. This happens
230 // while a widget is being dragged and when the majority of its bounds
231 // reside in a different display. Setting the widget's bounds at this
232 // point changes the window's root, and the event's target's root, but
233 // the events are still being generated relative to the original
234 // display. crbug.com/714578.
235 ui::LocatedEvent* e =
236 static_cast<ui::LocatedEvent*>(event->native_event());
237 gfx::PointF native_point = e->location_f();
238 aura::Window::ConvertNativePointToTargetHost(window, target,
239 &native_point);
240 e->set_location_f(native_point);
241 }
242 #endif
243
244 return target;
245 }
246 }
247 return FindTargetForLocatedEventRecursively(window, event);
248 }
249
SubtreeCanAcceptEvent(Window * window,const ui::LocatedEvent & event) const250 bool WindowTargeter::SubtreeCanAcceptEvent(
251 Window* window,
252 const ui::LocatedEvent& event) const {
253 if (!window->IsVisible())
254 return false;
255 if (window->event_targeting_policy() == EventTargetingPolicy::kNone ||
256 window->event_targeting_policy() == EventTargetingPolicy::kTargetOnly) {
257 return false;
258 }
259 client::EventClient* client = client::GetEventClient(window->GetRootWindow());
260 if (client && !client->CanProcessEventsWithinSubtree(window))
261 return false;
262
263 Window* parent = window->parent();
264 if (parent && parent->delegate_ &&
265 !parent->delegate_->ShouldDescendIntoChildForEventHandling(
266 window, event.location())) {
267 return false;
268 }
269 return true;
270 }
271
EventLocationInsideBounds(Window * window,const ui::LocatedEvent & event) const272 bool WindowTargeter::EventLocationInsideBounds(
273 Window* window,
274 const ui::LocatedEvent& event) const {
275 gfx::Rect mouse_rect;
276 gfx::Rect touch_rect;
277 if (!GetHitTestRects(window, &mouse_rect, &touch_rect))
278 return false;
279
280 const gfx::Vector2d offset = -window->bounds().OffsetFromOrigin();
281 mouse_rect.Offset(offset);
282 touch_rect.Offset(offset);
283 gfx::Point point = event.location();
284 if (window->parent())
285 Window::ConvertPointToTarget(window->parent(), window, &point);
286
287 const bool point_in_rect = event.IsTouchEvent() || event.IsGestureEvent()
288 ? touch_rect.Contains(point)
289 : mouse_rect.Contains(point);
290 if (!point_in_rect)
291 return false;
292
293 auto shape_rects = GetExtraHitTestShapeRects(window);
294 if (!shape_rects)
295 return true;
296
297 for (const gfx::Rect& shape_rect : *shape_rects) {
298 if (shape_rect.Contains(point)) {
299 return true;
300 }
301 }
302
303 return false;
304 }
305
ShouldUseExtendedBounds(const aura::Window * w) const306 bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* w) const {
307 // window() is null when this is used as the default targeter (by
308 // WindowEventDispatcher). Insets should never be set in this case, so the
309 // return should not matter.
310 if (!window()) {
311 DCHECK(mouse_extend_.IsEmpty());
312 DCHECK(touch_extend_.IsEmpty());
313 return false;
314 }
315
316 // Insets should only apply to the window. Subclasses may enforce other
317 // policies.
318 return window() == w;
319 }
320
FindTargetForNonKeyEvent(Window * root_window,ui::Event * event)321 Window* WindowTargeter::FindTargetForNonKeyEvent(Window* root_window,
322 ui::Event* event) {
323 if (!event->IsLocatedEvent())
324 return root_window;
325 return FindTargetForLocatedEvent(root_window,
326 static_cast<ui::LocatedEvent*>(event));
327 }
328
FindTargetForLocatedEventRecursively(Window * root_window,ui::LocatedEvent * event)329 Window* WindowTargeter::FindTargetForLocatedEventRecursively(
330 Window* root_window,
331 ui::LocatedEvent* event) {
332 std::unique_ptr<ui::EventTargetIterator> iter =
333 root_window->GetChildIterator();
334 if (iter) {
335 ui::EventTarget* target = root_window;
336 for (ui::EventTarget* child = iter->GetNextTarget(); child;
337 child = iter->GetNextTarget()) {
338 WindowTargeter* targeter =
339 static_cast<WindowTargeter*>(child->GetEventTargeter());
340 if (!targeter)
341 targeter = this;
342 if (!targeter->SubtreeShouldBeExploredForEvent(
343 static_cast<Window*>(child), *event)) {
344 continue;
345 }
346 target->ConvertEventToTarget(child, event);
347 target = child;
348 Window* child_target_window =
349 static_cast<Window*>(targeter->FindTargetForEvent(child, event));
350 if (child_target_window)
351 return child_target_window;
352 }
353 target->ConvertEventToTarget(root_window, event);
354 }
355 return root_window->CanAcceptEvent(*event) ? root_window : nullptr;
356 }
357
358 } // namespace aura
359