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