blob: 0e182f2d8e9a18377674ee33d119d1bd031704e1 [file] [log] [blame]
[email protected]8daeb412013-12-06 23:55:511// 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
Scott Violet2462c1e2018-08-23 21:50:527#include "services/ws/public/mojom/window_tree_constants.mojom.h"
[email protected]4c5d7c92013-12-13 17:17:398#include "ui/aura/client/capture_client.h"
[email protected]8daeb412013-12-06 23:55:519#include "ui/aura/client/event_client.h"
10#include "ui/aura/client/focus_client.h"
Mike Wasserman5613aca2018-08-09 02:11:0711#include "ui/aura/client/screen_position_client.h"
Jun Mukai0f3ae5292018-08-22 16:28:1112#include "ui/aura/env.h"
Scott Violet5f7e0b42018-09-05 18:05:1713#include "ui/aura/mus/window_port_mus.h"
14#include "ui/aura/mus/window_tree_client.h"
[email protected]8daeb412013-12-06 23:55:5115#include "ui/aura/window.h"
16#include "ui/aura/window_delegate.h"
[email protected]fcc51c952014-02-21 21:31:2617#include "ui/aura/window_event_dispatcher.h"
[email protected]7a60cd3a2014-03-20 20:54:5718#include "ui/aura/window_tree_host.h"
Mike Wasserman5613aca2018-08-09 02:11:0719#include "ui/display/display.h"
20#include "ui/display/screen.h"
[email protected]8daeb412013-12-06 23:55:5121#include "ui/events/event_target.h"
varkha9c6bcf712015-06-11 18:32:5822#include "ui/events/event_target_iterator.h"
[email protected]8daeb412013-12-06 23:55:5123
24namespace aura {
Scott Violet5f7e0b42018-09-05 18:05:1725namespace {
26
27bool AreInsetsEmptyOrPositive(const gfx::Insets& insets) {
28 return insets.left() >= 0 && insets.right() >= 0 && insets.top() >= 0 &&
29 insets.bottom() >= 0;
30}
31
Jun Mukai832af13592018-11-30 05:43:4432void UpdateMusIfNecessary(aura::Window* window,
33 const gfx::Insets& mouse_extend,
34 const gfx::Insets& touch_extend) {
35 if (!window || window->env()->mode() != Env::Mode::MUS)
36 return;
37
38 // Negative insets are used solely to extend the hit-test region of child
39 // windows, which is not needed by code using MUS (negative insets are only
40 // used in the server).
41 if (AreInsetsEmptyOrPositive(mouse_extend) &&
42 AreInsetsEmptyOrPositive(touch_extend)) {
43 WindowPortMus::Get(window)->SetHitTestInsets(mouse_extend, touch_extend);
44 }
45}
46
Scott Violet5f7e0b42018-09-05 18:05:1747} // namespace
[email protected]8daeb412013-12-06 23:55:5148
49WindowTargeter::WindowTargeter() {}
50WindowTargeter::~WindowTargeter() {}
51
Scott Violet91c9d382017-06-22 16:53:3352bool WindowTargeter::SubtreeShouldBeExploredForEvent(
53 Window* window,
54 const ui::LocatedEvent& event) {
55 return SubtreeCanAcceptEvent(window, event) &&
56 EventLocationInsideBounds(window, event);
[email protected]8daeb412013-12-06 23:55:5157}
58
Valery Arkhangorodsky94dd7ba2017-07-26 02:19:5059bool WindowTargeter::GetHitTestRects(Window* window,
60 gfx::Rect* hit_test_rect_mouse,
61 gfx::Rect* hit_test_rect_touch) const {
62 DCHECK(hit_test_rect_mouse);
63 DCHECK(hit_test_rect_touch);
Valery Arkhangorodsky34a58632017-08-16 16:25:4864 *hit_test_rect_mouse = *hit_test_rect_touch = window->bounds();
Valery Arkhangorodsky94dd7ba2017-07-26 02:19:5065
66 if (ShouldUseExtendedBounds(window)) {
67 hit_test_rect_mouse->Inset(mouse_extend_);
68 hit_test_rect_touch->Inset(touch_extend_);
69 }
70
71 return true;
72}
73
Valery Arkhangorodsky34a58632017-08-16 16:25:4874std::unique_ptr<WindowTargeter::HitTestRects>
75WindowTargeter::GetExtraHitTestShapeRects(Window* target) const {
76 return nullptr;
77}
78
Trent Apted274b2a0c2018-03-12 00:03:1779void WindowTargeter::SetInsets(const gfx::Insets& mouse_and_touch_extend) {
80 SetInsets(mouse_and_touch_extend, mouse_and_touch_extend);
81}
82
83void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend,
84 const gfx::Insets& touch_extend) {
85 if (mouse_extend_ == mouse_extend && touch_extend_ == touch_extend)
86 return;
87
Trent Apted274b2a0c2018-03-12 00:03:1788 mouse_extend_ = mouse_extend;
89 touch_extend_ = touch_extend;
Scott Violet5f7e0b42018-09-05 18:05:1790 UpdateMusIfNecessary();
Trent Apted274b2a0c2018-03-12 00:03:1791}
92
Scott Violet3b4bffa32017-09-05 20:35:1593Window* WindowTargeter::GetPriorityTargetInRootWindow(
94 Window* root_window,
95 const ui::LocatedEvent& event) {
Scott Violet91c9d382017-06-22 16:53:3396 DCHECK_EQ(root_window, root_window->GetRootWindow());
[email protected]8daeb412013-12-06 23:55:5197
Scott Violet91c9d382017-06-22 16:53:3398 // Mouse events should be dispatched to the window that processed the
99 // mouse-press events (if any).
100 if (event.IsScrollEvent() || event.IsMouseEvent()) {
101 WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher();
102 if (dispatcher->mouse_pressed_handler())
103 return dispatcher->mouse_pressed_handler();
[email protected]c981c4f2014-05-06 19:16:35104 }
[email protected]c981c4f2014-05-06 19:16:35105
Scott Violet91c9d382017-06-22 16:53:33106 // All events should be directed towards the capture window (if any).
107 Window* capture_window = client::GetCaptureWindow(root_window);
108 if (capture_window)
109 return capture_window;
110
Sean O'Brien82a47b82017-09-28 19:23:09111 if (event.IsPinchEvent()) {
112 DCHECK_EQ(event.AsGestureEvent()->details().device_type(),
113 ui::GestureDeviceType::DEVICE_TOUCHPAD);
114 WindowEventDispatcher* dispatcher = root_window->GetHost()->dispatcher();
115 if (dispatcher->touchpad_pinch_handler())
116 return dispatcher->touchpad_pinch_handler();
117 }
118
Scott Violet91c9d382017-06-22 16:53:33119 if (event.IsTouchEvent()) {
120 // Query the gesture-recognizer to find targets for touch events.
121 const ui::TouchEvent& touch = *event.AsTouchEvent();
122 ui::GestureConsumer* consumer =
Jun Mukai0f3ae5292018-08-22 16:28:11123 root_window->env()->gesture_recognizer()->GetTouchLockedTarget(touch);
Scott Violet91c9d382017-06-22 16:53:33124 if (consumer)
125 return static_cast<Window*>(consumer);
Scott Violet3b4bffa32017-09-05 20:35:15126 }
127
128 return nullptr;
129}
130
131Window* WindowTargeter::FindTargetInRootWindow(Window* root_window,
132 const ui::LocatedEvent& event) {
133 DCHECK_EQ(root_window, root_window->GetRootWindow());
134
135 Window* priority_target = GetPriorityTargetInRootWindow(root_window, event);
136 if (priority_target)
137 return priority_target;
138
139 if (event.IsTouchEvent()) {
140 // Query the gesture-recognizer to find targets for touch events.
141 const ui::TouchEvent& touch = *event.AsTouchEvent();
142 // GetTouchLockedTarget() is handled in GetPriorityTargetInRootWindow().
Jun Mukai0f3ae5292018-08-22 16:28:11143 ui::GestureRecognizer* gesture_recognizer =
144 root_window->env()->gesture_recognizer();
145 DCHECK(!gesture_recognizer->GetTouchLockedTarget(touch));
146 ui::GestureConsumer* consumer = gesture_recognizer->GetTargetForLocation(
147 event.location_f(), touch.source_device_id());
Scott Violet91c9d382017-06-22 16:53:33148 if (consumer)
149 return static_cast<Window*>(consumer);
150
lanwei5f78358db2018-11-29 01:16:33151#if defined(OS_CHROMEOS)
Mike Wasserman5613aca2018-08-09 02:11:07152 // If the initial touch is outside the window's display, target the root.
153 // This is used for bezel gesture events (eg. swiping in from screen edge).
154 display::Display display =
155 display::Screen::GetScreen()->GetDisplayNearestWindow(root_window);
Mike Wassermancc86c542018-09-28 20:47:56156 // The window target may be null, so use the root's ScreenPositionClient.
157 gfx::Point screen_location = event.root_location();
158 if (client::GetScreenPositionClient(root_window)) {
159 client::GetScreenPositionClient(root_window)
160 ->ConvertPointToScreen(root_window, &screen_location);
161 }
Mike Wasserman5613aca2018-08-09 02:11:07162 if (!display.bounds().Contains(screen_location))
Scott Violet91c9d382017-06-22 16:53:33163 return root_window;
lanwei5f78358db2018-11-29 01:16:33164#else
165 // If the initial touch is outside the root window, target the root.
166 // TODO: this code is likely not necessarily and will be removed.
167 if (!root_window->bounds().Contains(event.location()))
168 return root_window;
169#endif
Scott Violet91c9d382017-06-22 16:53:33170 }
171
172 return nullptr;
[email protected]8daeb412013-12-06 23:55:51173}
174
Scott Violet3b4bffa32017-09-05 20:35:15175bool WindowTargeter::ProcessEventIfTargetsDifferentRootWindow(
176 Window* root_window,
177 Window* target,
178 ui::Event* event) {
179 if (root_window->Contains(target))
180 return false;
181
182 // |window| is the root window, but |target| is not a descendent of
183 // |window|. So do not allow dispatching from here. Instead, dispatch the
184 // event through the WindowEventDispatcher that owns |target|.
185 Window* new_root = target->GetRootWindow();
186 DCHECK(new_root);
187 if (event->IsLocatedEvent()) {
188 // The event has been transformed to be in |target|'s coordinate system.
189 // But dispatching the event through the EventProcessor requires the event
190 // to be in the host's coordinate system. So, convert the event to be in
191 // the root's coordinate space, and then to the host's coordinate space by
192 // applying the host's transform.
193 ui::LocatedEvent* located_event = event->AsLocatedEvent();
194 located_event->ConvertLocationToTarget(target, new_root);
195 WindowTreeHost* window_tree_host = new_root->GetHost();
196 located_event->UpdateForRootTransform(
197 window_tree_host->GetRootTransform(),
198 window_tree_host->GetRootTransformForLocalEventCoordinates());
199 }
200 ignore_result(new_root->GetHost()->event_sink()->OnEventFromSource(event));
201 return true;
202}
203
varkha9c6bcf712015-06-11 18:32:58204ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root,
205 ui::Event* event) {
[email protected]4c5d7c92013-12-13 17:17:39206 Window* window = static_cast<Window*>(root);
moshayedi4e6623012016-03-02 04:53:58207 Window* target = event->IsKeyEvent()
Scott Violet6c77e2f62018-10-19 16:38:42208 ? FindTargetForKeyEvent(window)
moshayedi4e6623012016-03-02 04:53:58209 : FindTargetForNonKeyEvent(window, event);
Scott Violet3b4bffa32017-09-05 20:35:15210 if (target && !window->parent() &&
211 ProcessEventIfTargetsDifferentRootWindow(window, target, event)) {
212 return nullptr;
[email protected]4c5d7c92013-12-13 17:17:39213 }
varkha9c6bcf712015-06-11 18:32:58214 return target;
215}
216
217ui::EventTarget* WindowTargeter::FindNextBestTarget(
218 ui::EventTarget* previous_target,
219 ui::Event* event) {
220 return nullptr;
221}
222
Scott Violet6c77e2f62018-10-19 16:38:42223Window* WindowTargeter::FindTargetForKeyEvent(Window* window) {
224 Window* root_window = window->GetRootWindow();
225 client::FocusClient* focus_client = client::GetFocusClient(root_window);
226 if (!focus_client)
227 return window;
228 Window* focused_window = focus_client->GetFocusedWindow();
229 if (!focused_window)
230 return window;
231
232 client::EventClient* event_client = client::GetEventClient(root_window);
233 if (event_client &&
234 !event_client->CanProcessEventsWithinSubtree(focused_window)) {
235 focus_client->FocusWindow(nullptr);
236 return nullptr;
237 }
238 return focused_window ? focused_window : window;
239}
240
Scott Violetaca0bd92018-09-04 20:14:24241void WindowTargeter::OnInstalled(Window* window) {
Jun Mukai832af13592018-11-30 05:43:44242 // Needs to clear the existing insets when uninstalled.
243 if (!window)
244 aura::UpdateMusIfNecessary(window_, gfx::Insets(), gfx::Insets());
Scott Violetaca0bd92018-09-04 20:14:24245 window_ = window;
Scott Violet5f7e0b42018-09-05 18:05:17246 UpdateMusIfNecessary();
Scott Violetaca0bd92018-09-04 20:14:24247}
248
Scott Violet91c9d382017-06-22 16:53:33249Window* WindowTargeter::FindTargetForLocatedEvent(Window* window,
250 ui::LocatedEvent* event) {
251 if (!window->parent()) {
252 Window* target = FindTargetInRootWindow(window, *event);
253 if (target) {
254 window->ConvertEventToTarget(target, event);
Ahmed Fakhry9bd7bf42017-12-11 19:10:32255
256#if defined(OS_CHROMEOS)
257 if (window->IsRootWindow() && event->HasNativeEvent()) {
258 // If window is root, and the target is in a different host, we need to
259 // convert the native event to the target's host as well. This happens
260 // while a widget is being dragged and when the majority of its bounds
261 // reside in a different display. Setting the widget's bounds at this
262 // point changes the window's root, and the event's target's root, but
263 // the events are still being generated relative to the original
264 // display. crbug.com/714578.
265 ui::LocatedEvent* e =
266 static_cast<ui::LocatedEvent*>(event->native_event());
267 gfx::PointF native_point = e->location_f();
268 aura::Window::ConvertNativePointToTargetHost(window, target,
269 &native_point);
270 e->set_location_f(native_point);
271 }
272#endif
273
Scott Violet91c9d382017-06-22 16:53:33274 return target;
275 }
276 }
277 return FindTargetForLocatedEventRecursively(window, event);
278}
279
280bool WindowTargeter::SubtreeCanAcceptEvent(
varkha9c6bcf712015-06-11 18:32:58281 Window* window,
Scott Violet91c9d382017-06-22 16:53:33282 const ui::LocatedEvent& event) const {
283 if (!window->IsVisible())
284 return false;
Scott Violetca58856d2017-07-23 20:38:10285 if (window->event_targeting_policy() ==
Scott Violet8a3fad52018-08-24 21:38:55286 ws::mojom::EventTargetingPolicy::NONE ||
Scott Violetca58856d2017-07-23 20:38:10287 window->event_targeting_policy() ==
Scott Violet8a3fad52018-08-24 21:38:55288 ws::mojom::EventTargetingPolicy::TARGET_ONLY) {
Scott Violet91c9d382017-06-22 16:53:33289 return false;
Scott Violetca58856d2017-07-23 20:38:10290 }
Scott Violet91c9d382017-06-22 16:53:33291 client::EventClient* client = client::GetEventClient(window->GetRootWindow());
292 if (client && !client->CanProcessEventsWithinSubtree(window))
293 return false;
294
295 Window* parent = window->parent();
296 if (parent && parent->delegate_ &&
297 !parent->delegate_->ShouldDescendIntoChildForEventHandling(
298 window, event.location())) {
299 return false;
300 }
301 return true;
302}
303
304bool WindowTargeter::EventLocationInsideBounds(
305 Window* window,
306 const ui::LocatedEvent& event) const {
Valery Arkhangorodskycac5e0c2017-07-24 20:00:45307 gfx::Rect mouse_rect;
308 gfx::Rect touch_rect;
309 if (!GetHitTestRects(window, &mouse_rect, &touch_rect))
310 return false;
311
312 const gfx::Vector2d offset = -window->bounds().OffsetFromOrigin();
313 mouse_rect.Offset(offset);
314 touch_rect.Offset(offset);
Scott Violet91c9d382017-06-22 16:53:33315 gfx::Point point = event.location();
316 if (window->parent())
317 Window::ConvertPointToTarget(window->parent(), window, &point);
Valery Arkhangorodskycac5e0c2017-07-24 20:00:45318
Valery Arkhangorodsky34a58632017-08-16 16:25:48319 const bool point_in_rect = event.IsTouchEvent() || event.IsGestureEvent()
320 ? touch_rect.Contains(point)
321 : mouse_rect.Contains(point);
322 if (!point_in_rect)
323 return false;
Valery Arkhangorodskycac5e0c2017-07-24 20:00:45324
Valery Arkhangorodsky34a58632017-08-16 16:25:48325 auto shape_rects = GetExtraHitTestShapeRects(window);
326 if (!shape_rects)
327 return true;
328
329 for (const gfx::Rect& shape_rect : *shape_rects) {
330 if (shape_rect.Contains(point)) {
331 return true;
332 }
333 }
334
335 return false;
Valery Arkhangorodskycac5e0c2017-07-24 20:00:45336}
337
Scott Violet427807c72018-09-04 22:15:20338bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* w) const {
339 // window() is null when this is used as the default targeter (by
340 // WindowEventDispatcher). Insets should never be set in this case, so the
341 // return should not matter.
342 if (!window()) {
343 DCHECK(mouse_extend_.IsEmpty());
344 DCHECK(touch_extend_.IsEmpty());
345 return false;
346 }
347
348 // Insets should only apply to the window. Subclasses may enforce other
349 // policies.
350 return window() == w;
[email protected]4c5d7c92013-12-13 17:17:39351}
352
Scott Violet5f7e0b42018-09-05 18:05:17353// TODO: this function should go away once https://crbug.com/879308 is fixed.
354void WindowTargeter::UpdateMusIfNecessary() {
Jun Mukai832af13592018-11-30 05:43:44355 aura::UpdateMusIfNecessary(window_, mouse_extend_, touch_extend_);
Scott Violet5f7e0b42018-09-05 18:05:17356}
Valery Arkhangorodsky94dd7ba2017-07-26 02:19:50357
varkha9c6bcf712015-06-11 18:32:58358Window* WindowTargeter::FindTargetForNonKeyEvent(Window* root_window,
359 ui::Event* event) {
360 if (!event->IsLocatedEvent())
361 return root_window;
362 return FindTargetForLocatedEvent(root_window,
363 static_cast<ui::LocatedEvent*>(event));
364}
365
varkha9c6bcf712015-06-11 18:32:58366Window* WindowTargeter::FindTargetForLocatedEventRecursively(
367 Window* root_window,
368 ui::LocatedEvent* event) {
danakj25c52c32016-04-12 21:51:08369 std::unique_ptr<ui::EventTargetIterator> iter =
370 root_window->GetChildIterator();
varkha9c6bcf712015-06-11 18:32:58371 if (iter) {
372 ui::EventTarget* target = root_window;
373 for (ui::EventTarget* child = iter->GetNextTarget(); child;
374 child = iter->GetNextTarget()) {
375 WindowTargeter* targeter =
376 static_cast<WindowTargeter*>(child->GetEventTargeter());
377 if (!targeter)
378 targeter = this;
379 if (!targeter->SubtreeShouldBeExploredForEvent(
380 static_cast<Window*>(child), *event)) {
381 continue;
382 }
383 target->ConvertEventToTarget(child, event);
384 target = child;
385 Window* child_target_window =
386 static_cast<Window*>(targeter->FindTargetForEvent(child, event));
387 if (child_target_window)
388 return child_target_window;
389 }
390 target->ConvertEventToTarget(root_window, event);
391 }
392 return root_window->CanAcceptEvent(*event) ? root_window : nullptr;
[email protected]4c5d7c92013-12-13 17:17:39393}
394
[email protected]8daeb412013-12-06 23:55:51395} // namespace aura