1 // Copyright 2016 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 "content/browser/renderer_host/render_widget_host_view_event_handler.h"
6 
7 #include "base/command_line.h"
8 #include "base/metrics/user_metrics.h"
9 #include "base/metrics/user_metrics_action.h"
10 #include "components/viz/common/features.h"
11 #include "content/browser/renderer_host/hit_test_debug_key_event_observer.h"
12 #include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h"
13 #include "content/browser/renderer_host/overscroll_controller.h"
14 #include "content/browser/renderer_host/render_view_host_delegate.h"
15 #include "content/browser/renderer_host/render_view_host_delegate_view.h"
16 #include "content/browser/renderer_host/render_widget_host_impl.h"
17 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
18 #include "content/browser/renderer_host/render_widget_host_view_base.h"
19 #include "content/browser/renderer_host/text_input_manager.h"
20 #include "content/common/content_switches_internal.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/render_widget_host.h"
23 #include "content/public/common/content_features.h"
24 #include "ui/aura/client/cursor_client.h"
25 #include "ui/aura/client/focus_client.h"
26 #include "ui/aura/scoped_keyboard_hook.h"
27 #include "ui/aura/window.h"
28 #include "ui/aura/window_delegate.h"
29 #include "ui/aura/window_tree_host.h"
30 #include "ui/base/ime/text_input_client.h"
31 #include "ui/events/blink/blink_event_util.h"
32 #include "ui/events/blink/web_input_event.h"
33 #include "ui/events/keycodes/dom/dom_code.h"
34 #include "ui/touch_selection/touch_selection_controller.h"
35 
36 #if defined(OS_WIN)
37 #include "content/browser/frame_host/render_frame_host_impl.h"
38 #include "ui/aura/window_tree_host.h"
39 #include "ui/display/screen.h"
40 #endif  // defined(OS_WIN)
41 
42 namespace {
43 
44 // In mouse lock mode, we need to prevent the (invisible) cursor from hitting
45 // the border of the view, in order to get valid movement information. However,
46 // forcing the cursor back to the center of the view after each mouse move
47 // doesn't work well. It reduces the frequency of useful mouse move messages
48 // significantly. Therefore, we move the cursor to the center of the view only
49 // if it approaches the border. |kMouseLockBorderPercentage| specifies the width
50 // of the border area, in percentage of the corresponding dimension.
51 const int kMouseLockBorderPercentage = 15;
52 
53 #if defined(OS_WIN)
54 // A callback function for EnumThreadWindows to enumerate and dismiss
55 // any owned popup windows.
DismissOwnedPopups(HWND window,LPARAM arg)56 BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) {
57   const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg);
58 
59   if (::IsWindowVisible(window)) {
60     const HWND owner = ::GetWindow(window, GW_OWNER);
61     if (toplevel_hwnd == owner) {
62       ::PostMessageW(window, WM_CANCELMODE, 0, 0);
63     }
64   }
65 
66   return TRUE;
67 }
68 #endif  // defined(OS_WIN)
69 
IsFractionalScaleFactor(float scale_factor)70 bool IsFractionalScaleFactor(float scale_factor) {
71   return (scale_factor - static_cast<int>(scale_factor)) > 0;
72 }
73 
74 // We don't mark these as handled so that they're sent back to the
75 // DefWindowProc so it can generate WM_APPCOMMAND as necessary.
ShouldGenerateAppCommand(const ui::MouseEvent * event)76 bool ShouldGenerateAppCommand(const ui::MouseEvent* event) {
77 #if defined(OS_WIN)
78   return (event->native_event().message == WM_NCXBUTTONUP);
79 #endif
80   return false;
81 }
82 
83 // Reset unchanged touch points to StateStationary for touchmove and
84 // touchcancel.
MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent * event,int changed_touch_id)85 void MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent* event,
86                                           int changed_touch_id) {
87   if (event->GetType() == blink::WebInputEvent::kTouchMove ||
88       event->GetType() == blink::WebInputEvent::kTouchCancel) {
89     for (size_t i = 0; i < event->touches_length; ++i) {
90       if (event->touches[i].id != changed_touch_id)
91         event->touches[i].state = blink::WebTouchPoint::kStateStationary;
92     }
93   }
94 }
95 
NeedsInputGrab(content::RenderWidgetHostViewBase * view)96 bool NeedsInputGrab(content::RenderWidgetHostViewBase* view) {
97   if (!view)
98     return false;
99   return view->GetWidgetType() == content::WidgetType::kPopup;
100 }
101 
102 }  // namespace
103 
104 namespace content {
105 
106 RenderWidgetHostViewEventHandler::Delegate::Delegate() = default;
107 
~Delegate()108 RenderWidgetHostViewEventHandler::Delegate::~Delegate() {}
109 
RenderWidgetHostViewEventHandler(RenderWidgetHostImpl * host,RenderWidgetHostViewBase * host_view,Delegate * delegate)110 RenderWidgetHostViewEventHandler::RenderWidgetHostViewEventHandler(
111     RenderWidgetHostImpl* host,
112     RenderWidgetHostViewBase* host_view,
113     Delegate* delegate)
114     : pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
115       enable_consolidated_movement_(
116           base::FeatureList::IsEnabled(features::kConsolidatedMovementXY)),
117       host_(host),
118       host_view_(host_view),
119       delegate_(delegate),
120       mouse_wheel_phase_handler_(host_view),
121       debug_observer_(features::IsVizHitTestingDebugEnabled()
122                           ? std::make_unique<HitTestDebugKeyEventObserver>(host)
123                           : nullptr) {}
124 
~RenderWidgetHostViewEventHandler()125 RenderWidgetHostViewEventHandler::~RenderWidgetHostViewEventHandler() {
126   DCHECK(!mouse_locked_);
127 }
128 
SetPopupChild(RenderWidgetHostViewBase * popup_child_host_view,ui::EventHandler * popup_child_event_handler)129 void RenderWidgetHostViewEventHandler::SetPopupChild(
130     RenderWidgetHostViewBase* popup_child_host_view,
131     ui::EventHandler* popup_child_event_handler) {
132   popup_child_host_view_ = popup_child_host_view;
133   popup_child_event_handler_ = popup_child_event_handler;
134 }
135 
TrackHost(aura::Window * reference_window)136 void RenderWidgetHostViewEventHandler::TrackHost(
137     aura::Window* reference_window) {
138   if (!reference_window)
139     return;
140   DCHECK(!host_tracker_);
141   host_tracker_.reset(new aura::WindowTracker);
142   host_tracker_->Add(reference_window);
143 }
144 
145 #if defined(OS_WIN)
UpdateMouseLockRegion()146 void RenderWidgetHostViewEventHandler::UpdateMouseLockRegion() {
147   RECT window_rect =
148       display::Screen::GetScreen()
149           ->DIPToScreenRectInWindow(window_, window_->GetBoundsInScreen())
150           .ToRECT();
151   ::ClipCursor(&window_rect);
152 }
153 #endif
154 
LockMouse(bool request_unadjusted_movement)155 blink::mojom::PointerLockResult RenderWidgetHostViewEventHandler::LockMouse(
156     bool request_unadjusted_movement) {
157   aura::Window* root_window = window_->GetRootWindow();
158   if (!root_window)
159     return blink::mojom::PointerLockResult::kWrongDocument;
160 
161   if (mouse_locked_)
162     return blink::mojom::PointerLockResult::kSuccess;
163 
164   if (request_unadjusted_movement && window_->GetHost()) {
165     mouse_locked_unadjusted_movement_ =
166         window_->GetHost()->RequestUnadjustedMovement();
167     if (!mouse_locked_unadjusted_movement_)
168       return blink::mojom::PointerLockResult::kUnsupportedOptions;
169   }
170   mouse_locked_ = true;
171 
172 #if !defined(OS_WIN)
173   window_->SetCapture();
174 #else
175   UpdateMouseLockRegion();
176 #endif
177   aura::client::CursorClient* cursor_client =
178       aura::client::GetCursorClient(root_window);
179   if (cursor_client) {
180     cursor_client->HideCursor();
181     cursor_client->LockCursor();
182   }
183 
184   if (ShouldMoveToCenter(unlocked_global_mouse_position_))
185     MoveCursorToCenter(nullptr);
186 
187   delegate_->SetTooltipsEnabled(false);
188   return blink::mojom::PointerLockResult::kSuccess;
189 }
190 
191 blink::mojom::PointerLockResult
ChangeMouseLock(bool request_unadjusted_movement)192 RenderWidgetHostViewEventHandler::ChangeMouseLock(
193     bool request_unadjusted_movement) {
194   aura::Window* root_window = window_->GetRootWindow();
195   if (!root_window || !window_->GetHost())
196     return blink::mojom::PointerLockResult::kWrongDocument;
197 
198   // If lock was lost before completing this change request
199   // it was because the user hit escape or navigated away
200   // from the page.
201   if (!mouse_locked_)
202     return blink::mojom::PointerLockResult::kUserRejected;
203 
204   if (!request_unadjusted_movement) {
205     mouse_locked_unadjusted_movement_.reset();
206     return blink::mojom::PointerLockResult::kSuccess;
207   }
208 
209   if (mouse_locked_unadjusted_movement_) {
210     // Desired state already acquired.
211     return blink::mojom::PointerLockResult::kSuccess;
212   }
213 
214   mouse_locked_unadjusted_movement_ =
215       window_->GetHost()->RequestUnadjustedMovement();
216 
217   if (!mouse_locked_unadjusted_movement_)
218     return blink::mojom::PointerLockResult::kUnsupportedOptions;
219 
220   return blink::mojom::PointerLockResult::kSuccess;
221 }
222 
UnlockMouse()223 void RenderWidgetHostViewEventHandler::UnlockMouse() {
224   delegate_->SetTooltipsEnabled(true);
225 
226   aura::Window* root_window = window_->GetRootWindow();
227   if (!mouse_locked_ || !root_window)
228     return;
229 
230   mouse_locked_ = false;
231   mouse_locked_unadjusted_movement_.reset();
232 
233   if (window_->HasCapture())
234     window_->ReleaseCapture();
235 
236 #if defined(OS_WIN)
237   ::ClipCursor(NULL);
238 #endif
239 
240   // Ensure that the global mouse position is updated here to its original
241   // value. If we don't do this then the synthesized mouse move which is posted
242   // after the cursor is moved ends up getting a large movement delta which is
243   // not what sites expect. The delta is computed in the
244   // ModifyEventMovementAndCoords function.
245   global_mouse_position_ = unlocked_global_mouse_position_;
246   window_->MoveCursorTo(gfx::ToFlooredPoint(unlocked_mouse_position_));
247   synthetic_move_position_ =
248       gfx::ToFlooredPoint(unlocked_global_mouse_position_);
249 
250   aura::client::CursorClient* cursor_client =
251       aura::client::GetCursorClient(root_window);
252   if (cursor_client) {
253     cursor_client->UnlockCursor();
254     cursor_client->ShowCursor();
255   }
256   host_->LostMouseLock();
257 }
258 
LockKeyboard(base::Optional<base::flat_set<ui::DomCode>> codes)259 bool RenderWidgetHostViewEventHandler::LockKeyboard(
260     base::Optional<base::flat_set<ui::DomCode>> codes) {
261   aura::Window* root_window = window_->GetRootWindow();
262   if (!root_window)
263     return false;
264 
265   // Remove existing hook, if registered.
266   UnlockKeyboard();
267   scoped_keyboard_hook_ = root_window->CaptureSystemKeyEvents(std::move(codes));
268 
269   return IsKeyboardLocked();
270 }
271 
UnlockKeyboard()272 void RenderWidgetHostViewEventHandler::UnlockKeyboard() {
273   scoped_keyboard_hook_.reset();
274 }
275 
IsKeyboardLocked() const276 bool RenderWidgetHostViewEventHandler::IsKeyboardLocked() const {
277   return scoped_keyboard_hook_ != nullptr;
278 }
279 
OnKeyEvent(ui::KeyEvent * event)280 void RenderWidgetHostViewEventHandler::OnKeyEvent(ui::KeyEvent* event) {
281   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnKeyEvent");
282 
283   if (NeedsInputGrab(popup_child_host_view_)) {
284     popup_child_event_handler_->OnKeyEvent(event);
285     if (event->handled())
286       return;
287   }
288 
289   bool mark_event_as_handled = true;
290   // We need to handle the Escape key for Pepper Flash.
291   if (host_view_->is_fullscreen() && event->key_code() == ui::VKEY_ESCAPE) {
292     // Focus the window we were created from.
293     if (host_tracker_.get() && !host_tracker_->windows().empty()) {
294       aura::Window* host = *(host_tracker_->windows().begin());
295       aura::client::FocusClient* client = aura::client::GetFocusClient(host);
296       if (client) {
297         // Calling host->Focus() may delete |this|. We create a local observer
298         // for that. In that case we exit without further access to any members.
299         auto local_tracker = std::move(host_tracker_);
300         local_tracker->Add(window_);
301         host->Focus();
302         if (!local_tracker->Contains(window_)) {
303           event->SetHandled();
304           return;
305         }
306       }
307     }
308     delegate_->Shutdown();
309     host_tracker_.reset();
310   } else {
311     if (event->key_code() == ui::VKEY_RETURN) {
312       // Do not forward return key release events if no press event was handled.
313       if (event->type() == ui::ET_KEY_RELEASED && !accept_return_character_)
314         return;
315       // Accept return key character events between press and release events.
316       accept_return_character_ = event->type() == ui::ET_KEY_PRESSED;
317     }
318 
319     // Call SetKeyboardFocus() for not only ET_KEY_PRESSED but also
320     // ET_KEY_RELEASED. If a user closed the hotdog menu with ESC key press,
321     // we need to notify focus to Blink on ET_KEY_RELEASED for ESC key.
322     SetKeyboardFocus();
323     // We don't have to communicate with an input method here.
324     NativeWebKeyboardEvent webkit_event(*event);
325 
326     // If the key has been reserved as part of the active KeyboardLock request,
327     // then we want to mark it as such so it is not intercepted by the browser.
328     if (IsKeyLocked(*event))
329       webkit_event.skip_in_browser = true;
330 
331     delegate_->ForwardKeyboardEventWithLatencyInfo(
332         webkit_event, *event->latency(), &mark_event_as_handled);
333   }
334   if (mark_event_as_handled)
335     event->SetHandled();
336 }
337 
OnMouseEvent(ui::MouseEvent * event)338 void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) {
339   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnMouseEvent");
340 
341   // CrOS will send a mouse exit event to update hover state when mouse is
342   // hidden which we want to filter out in renderer. crbug.com/723535.
343   if (event->flags() & ui::EF_CURSOR_HIDE)
344     return;
345 
346   ForwardMouseEventToParent(event);
347   // TODO(mgiuca): Return if event->handled() returns true. This currently
348   // breaks drop-down lists which means something is incorrectly setting
349   // event->handled to true (http://crbug.com/577983).
350 
351   if (mouse_locked_) {
352     HandleMouseEventWhileLocked(event);
353     return;
354   }
355 
356   // As the overscroll is handled during scroll events from the trackpad, the
357   // RWHVA window is transformed by the overscroll controller. This transform
358   // triggers a synthetic mouse-move event to be generated (by the aura
359   // RootWindow). Also, with a touchscreen, we may get a synthetic mouse-move
360   // caused by a pointer grab. But these events interfere with the overscroll
361   // gesture. So, ignore such synthetic mouse-move events if an overscroll
362   // gesture is in progress.
363   OverscrollController* overscroll_controller =
364       delegate_->overscroll_controller();
365   if (overscroll_controller &&
366       overscroll_controller->overscroll_mode() != OVERSCROLL_NONE &&
367       event->flags() & ui::EF_IS_SYNTHESIZED &&
368       (event->type() == ui::ET_MOUSE_ENTERED ||
369        event->type() == ui::ET_MOUSE_EXITED ||
370        event->type() == ui::ET_MOUSE_MOVED)) {
371     event->StopPropagation();
372     return;
373   }
374 
375   if (event->type() == ui::ET_MOUSEWHEEL) {
376 #if defined(OS_WIN)
377     // We get mouse wheel/scroll messages even if we are not in the foreground.
378     // So here we check if we have any owned popup windows in the foreground and
379     // dismiss them.
380     aura::WindowTreeHost* host = window_->GetHost();
381     if (host) {
382       HWND parent = host->GetAcceleratedWidget();
383       HWND toplevel_hwnd = ::GetAncestor(parent, GA_ROOT);
384       EnumThreadWindows(GetCurrentThreadId(), DismissOwnedPopups,
385                         reinterpret_cast<LPARAM>(toplevel_hwnd));
386     }
387 #endif
388     blink::WebMouseWheelEvent mouse_wheel_event =
389         ui::MakeWebMouseWheelEvent(*event->AsMouseWheelEvent());
390 
391     if (mouse_wheel_event.delta_x != 0 || mouse_wheel_event.delta_y != 0) {
392       const bool should_route_event = ShouldRouteEvents();
393       // End the touchpad scrolling sequence (if such exists) before handling
394       // a ui::ET_MOUSEWHEEL event.
395       mouse_wheel_phase_handler_.SendWheelEndForTouchpadScrollingIfNeeded(
396           should_route_event);
397 
398       mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(
399           mouse_wheel_event, should_route_event);
400       if (should_route_event) {
401         host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent(
402             host_view_, &mouse_wheel_event, *event->latency());
403       } else {
404         ProcessMouseWheelEvent(mouse_wheel_event, *event->latency());
405       }
406     }
407   } else {
408     bool is_selection_popup = NeedsInputGrab(popup_child_host_view_);
409     if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) &&
410         !(event->flags() & ui::EF_FROM_TOUCH)) {
411       // Confirm existing composition text on mouse press, to make sure
412       // the input caret won't be moved with an ongoing composition text.
413       if (event->type() == ui::ET_MOUSE_PRESSED)
414         FinishImeCompositionSession();
415 
416       blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent(*event);
417       ModifyEventMovementAndCoords(*event, &mouse_event);
418       if (ShouldRouteEvents()) {
419         host_->delegate()->GetInputEventRouter()->RouteMouseEvent(
420             host_view_, &mouse_event, *event->latency());
421       } else {
422         ProcessMouseEvent(mouse_event, *event->latency());
423       }
424 
425       // Ensure that we get keyboard focus on mouse down as a plugin window may
426       // have grabbed keyboard focus.
427       if (event->type() == ui::ET_MOUSE_PRESSED)
428         SetKeyboardFocus();
429     }
430   }
431 
432   switch (event->type()) {
433     case ui::ET_MOUSE_PRESSED:
434       window_->SetCapture();
435       break;
436     case ui::ET_MOUSE_RELEASED:
437       if (!delegate_->NeedsMouseCapture())
438         window_->ReleaseCapture();
439       break;
440     default:
441       break;
442   }
443 
444   if (!ShouldGenerateAppCommand(event))
445     event->SetHandled();
446 }
447 
OnScrollEvent(ui::ScrollEvent * event)448 void RenderWidgetHostViewEventHandler::OnScrollEvent(ui::ScrollEvent* event) {
449   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnScrollEvent");
450   const bool should_route_event = ShouldRouteEvents();
451   if (event->type() == ui::ET_SCROLL) {
452 #if !defined(OS_WIN)
453     // TODO(ananta)
454     // Investigate if this is true for Windows 8 Metro ASH as well.
455     if (event->finger_count() != 2)
456       return;
457 #endif
458     blink::WebMouseWheelEvent mouse_wheel_event =
459         ui::MakeWebMouseWheelEvent(*event);
460     mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(
461         mouse_wheel_event, should_route_event);
462 
463     base::Optional<blink::WebGestureEvent> maybe_synthetic_fling_cancel;
464     if (mouse_wheel_event.phase == blink::WebMouseWheelEvent::kPhaseBegan) {
465       maybe_synthetic_fling_cancel =
466           ui::MakeWebGestureEventFlingCancel(mouse_wheel_event);
467     }
468 
469     if (should_route_event) {
470       if (maybe_synthetic_fling_cancel) {
471         host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
472             host_view_, &*maybe_synthetic_fling_cancel,
473             ui::LatencyInfo(ui::SourceEventType::WHEEL));
474       }
475       host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent(
476           host_view_, &mouse_wheel_event, *event->latency());
477     } else {
478       if (maybe_synthetic_fling_cancel) {
479         host_->ForwardGestureEvent(*maybe_synthetic_fling_cancel);
480       }
481       host_->ForwardWheelEventWithLatencyInfo(mouse_wheel_event,
482                                               *event->latency());
483     }
484   } else if (event->type() == ui::ET_SCROLL_FLING_START ||
485              event->type() == ui::ET_SCROLL_FLING_CANCEL) {
486     blink::WebGestureEvent gesture_event = ui::MakeWebGestureEvent(*event);
487     if (should_route_event) {
488       host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
489           host_view_, &gesture_event,
490           ui::LatencyInfo(ui::SourceEventType::WHEEL));
491     } else {
492       host_->ForwardGestureEvent(gesture_event);
493     }
494     if (event->type() == ui::ET_SCROLL_FLING_START) {
495       RecordAction(base::UserMetricsAction("TrackpadScrollFling"));
496       // The user has lifted their fingers.
497       mouse_wheel_phase_handler_.ResetTouchpadScrollSequence();
498     } else if (event->type() == ui::ET_SCROLL_FLING_CANCEL) {
499       // The user has put their fingers down.
500       DCHECK_EQ(blink::WebGestureDevice::kTouchpad,
501                 gesture_event.SourceDevice());
502       mouse_wheel_phase_handler_.TouchpadScrollingMayBegin();
503     }
504   }
505 
506   event->SetHandled();
507 }
508 
OnTouchEvent(ui::TouchEvent * event)509 void RenderWidgetHostViewEventHandler::OnTouchEvent(ui::TouchEvent* event) {
510   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnTouchEvent");
511 
512   bool had_no_pointer = !pointer_state_.GetPointerCount();
513 
514   // Update the touch event first.
515   if (!pointer_state_.OnTouch(*event)) {
516     event->StopPropagation();
517     return;
518   }
519 
520   blink::WebTouchEvent touch_event;
521   bool handled =
522       delegate_->selection_controller()->WillHandleTouchEvent(pointer_state_);
523   if (handled) {
524     event->SetHandled();
525   } else {
526     touch_event = ui::CreateWebTouchEventFromMotionEvent(
527         pointer_state_, event->may_cause_scrolling(), event->hovering());
528   }
529   pointer_state_.CleanupRemovedTouchPoints(*event);
530 
531   if (handled)
532     return;
533 
534   if (had_no_pointer)
535     delegate_->selection_controller_client()->OnTouchDown();
536   if (!pointer_state_.GetPointerCount())
537     delegate_->selection_controller_client()->OnTouchUp();
538 
539   // It is important to always mark events as being handled asynchronously when
540   // they are forwarded. This ensures that the current event does not get
541   // processed by the gesture recognizer before events currently awaiting
542   // dispatch in the touch queue.
543   event->DisableSynchronousHandling();
544 
545   // Set unchanged touch point to StateStationary for touchmove and
546   // touchcancel to make sure only send one ack per WebTouchEvent.
547   MarkUnchangedTouchPointsAsStationary(&touch_event,
548                                        event->pointer_details().id);
549   if (ShouldRouteEvents()) {
550     host_->delegate()->GetInputEventRouter()->RouteTouchEvent(
551         host_view_, &touch_event, *event->latency());
552   } else {
553     ProcessTouchEvent(touch_event, *event->latency());
554   }
555 }
556 
OnGestureEvent(ui::GestureEvent * event)557 void RenderWidgetHostViewEventHandler::OnGestureEvent(ui::GestureEvent* event) {
558   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnGestureEvent");
559 
560   if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN ||
561        event->type() == ui::ET_GESTURE_PINCH_UPDATE ||
562        event->type() == ui::ET_GESTURE_PINCH_END) &&
563       !pinch_zoom_enabled_) {
564     event->SetHandled();
565     return;
566   }
567 
568   HandleGestureForTouchSelection(event);
569   if (event->handled())
570     return;
571 
572   // Confirm existing composition text on TAP gesture, to make sure the input
573   // caret won't be moved with an ongoing composition text.
574   if (event->type() == ui::ET_GESTURE_TAP)
575     FinishImeCompositionSession();
576 
577   blink::WebGestureEvent gesture = ui::MakeWebGestureEvent(*event);
578   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
579     // Webkit does not stop a fling-scroll on tap-down. So explicitly send an
580     // event to stop any in-progress flings.
581     blink::WebGestureEvent fling_cancel = gesture;
582     fling_cancel.SetType(blink::WebInputEvent::kGestureFlingCancel);
583     fling_cancel.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
584     if (ShouldRouteEvents()) {
585       host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
586           host_view_, &fling_cancel,
587           ui::LatencyInfo(ui::SourceEventType::TOUCH));
588     } else {
589       host_->ForwardGestureEvent(fling_cancel);
590     }
591   }
592 
593   if (gesture.GetType() != blink::WebInputEvent::kUndefined) {
594     if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
595       // If there is a current scroll going on and a new scroll that isn't
596       // wheel based send a synthetic wheel event with kPhaseEnded to cancel
597       // the current scroll.
598       mouse_wheel_phase_handler_.DispatchPendingWheelEndEvent();
599       mouse_wheel_phase_handler_.SendWheelEndForTouchpadScrollingIfNeeded(
600           ShouldRouteEvents());
601     } else if (event->type() == ui::ET_SCROLL_FLING_START) {
602       RecordAction(base::UserMetricsAction("TouchscreenScrollFling"));
603     }
604 
605     if (event->type() == ui::ET_GESTURE_SCROLL_END ||
606         event->type() == ui::ET_SCROLL_FLING_START) {
607       // Scrolling with touchscreen has finished. Make sure that the next wheel
608       // event will have phase = |kPhaseBegan|. This is for maintaining the
609       // correct phase info when some of the wheel events get ignored while a
610       // touchscreen scroll is going on.
611       mouse_wheel_phase_handler_.IgnorePendingWheelEndEvent();
612       mouse_wheel_phase_handler_.ResetTouchpadScrollSequence();
613     }
614 
615     if (ShouldRouteEvents()) {
616       host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
617           host_view_, &gesture, *event->latency());
618     } else {
619       host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency());
620     }
621   }
622 
623   // If a gesture is not processed by the webpage, then WebKit processes it
624   // (e.g. generates synthetic mouse events).
625   event->SetHandled();
626 }
627 
GestureEventAck(const blink::WebGestureEvent & event,InputEventAckState ack_result)628 void RenderWidgetHostViewEventHandler::GestureEventAck(
629     const blink::WebGestureEvent& event,
630     InputEventAckState ack_result) {
631   mouse_wheel_phase_handler_.GestureEventAck(event, ack_result);
632 }
633 
CanRendererHandleEvent(const ui::MouseEvent * event,bool mouse_locked,bool selection_popup) const634 bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent(
635     const ui::MouseEvent* event,
636     bool mouse_locked,
637     bool selection_popup) const {
638   if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED)
639     return false;
640 
641   if (event->type() == ui::ET_MOUSE_EXITED) {
642     if (mouse_locked || selection_popup)
643       return false;
644 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_BSD)
645     // Don't forward the mouse leave message which is received when the context
646     // menu is displayed by the page. This confuses the page and causes state
647     // changes.
648     if (host_->delegate() && host_->delegate()->IsShowingContextMenuOnPage())
649       return false;
650 #endif
651     return true;
652   }
653 
654 #if defined(OS_WIN)
655   // Renderer cannot handle WM_XBUTTON or NC events.
656   switch (event->native_event().message) {
657     case WM_XBUTTONDOWN:
658     case WM_XBUTTONUP:
659     case WM_XBUTTONDBLCLK:
660       return true;
661     case WM_NCMOUSELEAVE:
662     case WM_NCMOUSEMOVE:
663     case WM_NCLBUTTONDOWN:
664     case WM_NCLBUTTONUP:
665     case WM_NCLBUTTONDBLCLK:
666     case WM_NCRBUTTONDOWN:
667     case WM_NCRBUTTONUP:
668     case WM_NCRBUTTONDBLCLK:
669     case WM_NCMBUTTONDOWN:
670     case WM_NCMBUTTONUP:
671     case WM_NCMBUTTONDBLCLK:
672     case WM_NCXBUTTONDOWN:
673     case WM_NCXBUTTONUP:
674     case WM_NCXBUTTONDBLCLK:
675       return false;
676     default:
677       break;
678   }
679 #endif
680   return true;
681 }
682 
FinishImeCompositionSession()683 void RenderWidgetHostViewEventHandler::FinishImeCompositionSession() {
684   // RenderWidgetHostViewAura keeps track of existing composition texts. The
685   // call to finish composition text should be made through the RWHVA itself
686   // otherwise the following call to cancel composition will lead to an extra
687   // IPC for finishing the ongoing composition (see https://crbug.com/723024).
688   host_view_->GetTextInputClient()->ConfirmCompositionText(
689       /* keep_selection */ false);
690   host_view_->ImeCancelComposition();
691 }
692 
ForwardMouseEventToParent(ui::MouseEvent * event)693 void RenderWidgetHostViewEventHandler::ForwardMouseEventToParent(
694     ui::MouseEvent* event) {
695   // Needed to propagate mouse event to |window_->parent()->delegate()|, but
696   // note that it might be something other than a WebContentsViewAura instance.
697   // TODO(pkotwicz): Find a better way of doing this.
698   // In fullscreen mode which is typically used by flash, don't forward
699   // the mouse events to the parent. The renderer and the plugin process
700   // handle these events.
701   if (host_view_->is_fullscreen())
702     return;
703 
704   if (event->flags() & ui::EF_FROM_TOUCH)
705     return;
706 
707   if (!window_->parent() || !window_->parent()->delegate())
708     return;
709 
710   // Take a copy of |event|, to avoid ConvertLocationToTarget mutating the
711   // event.
712   std::unique_ptr<ui::Event> event_copy = ui::Event::Clone(*event);
713   ui::MouseEvent* mouse_event = static_cast<ui::MouseEvent*>(event_copy.get());
714   mouse_event->ConvertLocationToTarget(window_, window_->parent());
715   window_->parent()->delegate()->OnMouseEvent(mouse_event);
716   if (mouse_event->handled())
717     event->SetHandled();
718 }
719 
HandleGestureForTouchSelection(ui::GestureEvent * event)720 void RenderWidgetHostViewEventHandler::HandleGestureForTouchSelection(
721     ui::GestureEvent* event) {
722   switch (event->type()) {
723     case ui::ET_GESTURE_LONG_PRESS:
724       delegate_->selection_controller()->HandleLongPressEvent(
725           event->time_stamp(), event->location_f());
726       break;
727     case ui::ET_GESTURE_TAP:
728       delegate_->selection_controller()->HandleTapEvent(
729           event->location_f(), event->details().tap_count());
730       break;
731     case ui::ET_GESTURE_SCROLL_BEGIN:
732       delegate_->selection_controller_client()->OnScrollStarted();
733       break;
734     case ui::ET_GESTURE_SCROLL_END:
735       delegate_->selection_controller_client()->OnScrollCompleted();
736       break;
737     default:
738       break;
739   }
740 }
741 
HandleMouseEventWhileLocked(ui::MouseEvent * event)742 void RenderWidgetHostViewEventHandler::HandleMouseEventWhileLocked(
743     ui::MouseEvent* event) {
744   aura::client::CursorClient* cursor_client =
745       aura::client::GetCursorClient(window_->GetRootWindow());
746 
747   DCHECK(!cursor_client || !cursor_client->IsCursorVisible());
748 
749   if (event->type() == ui::ET_MOUSEWHEEL) {
750     blink::WebMouseWheelEvent mouse_wheel_event =
751         ui::MakeWebMouseWheelEvent(*event->AsMouseWheelEvent());
752     if (mouse_wheel_event.delta_x != 0 || mouse_wheel_event.delta_y != 0) {
753       if (ShouldRouteEvents()) {
754         host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent(
755             host_view_, &mouse_wheel_event, *event->latency());
756       } else {
757         ProcessMouseWheelEvent(mouse_wheel_event, *event->latency());
758       }
759     }
760   } else {
761     // If we receive non client mouse messages while we are in the locked state
762     // it probably means that the mouse left the borders of our window and
763     // needs to be moved back to the center.
764     if (event->flags() & ui::EF_IS_NON_CLIENT) {
765       // TODO(jonross): ideally this would not be done for mus
766       // (crbug.com/621412)
767       MoveCursorToCenter(event);
768       return;
769     }
770 
771     blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent(*event);
772 
773     bool should_not_forward = MatchesSynthesizedMovePosition(mouse_event);
774 
775     ModifyEventMovementAndCoords(*event, &mouse_event);
776 
777     if (!enable_consolidated_movement_ && should_not_forward) {
778       synthetic_move_position_.reset();
779     } else {
780       bool is_selection_popup = NeedsInputGrab(popup_child_host_view_);
781       // Forward event to renderer.
782       if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) &&
783           !(event->flags() & ui::EF_FROM_TOUCH)) {
784         if (ShouldRouteEvents()) {
785           host_->delegate()->GetInputEventRouter()->RouteMouseEvent(
786               host_view_, &mouse_event, *event->latency());
787         } else {
788           ProcessMouseEvent(mouse_event, *event->latency());
789         }
790         // Ensure that we get keyboard focus on mouse down as a plugin window
791         // may have grabbed keyboard focus.
792         if (event->type() == ui::ET_MOUSE_PRESSED)
793           SetKeyboardFocus();
794       }
795 
796       // Check if the mouse has reached the border and needs to be centered.
797       // Use event position if consolidated_movement_ is enabled, otherwise use
798       // stored global_mouse_position_.
799       if (ShouldMoveToCenter(enable_consolidated_movement_
800                                  ? gfx::PointF(mouse_event.PositionInScreen())
801                                  : global_mouse_position_)) {
802         MoveCursorToCenter(event);
803       }
804     }
805   }
806   if (!ShouldGenerateAppCommand(event))
807     event->SetHandled();
808 }
809 
ModifyEventMovementAndCoords(const ui::MouseEvent & ui_mouse_event,blink::WebMouseEvent * event)810 void RenderWidgetHostViewEventHandler::ModifyEventMovementAndCoords(
811     const ui::MouseEvent& ui_mouse_event,
812     blink::WebMouseEvent* event) {
813   if (!enable_consolidated_movement_) {
814     // If the mouse has just entered, we must report zero movementX/Y. Hence we
815     // reset any global_mouse_position set previously.
816     if (ui_mouse_event.type() == ui::ET_MOUSE_ENTERED ||
817         ui_mouse_event.type() == ui::ET_MOUSE_EXITED) {
818       global_mouse_position_ = event->PositionInScreen();
819     }
820 
821     // Movement is computed by taking the difference of the new cursor position
822     // and the previous. Under mouse lock the cursor will be warped back to the
823     // center so that we are not limited by clipping boundaries.
824     // We do not measure movement as the delta from cursor to center because
825     // we may receive more mouse movement events before our warp has taken
826     // effect.
827     // TODO(crbug.com/802067): We store event coordinates as pointF but
828     // movement_x/y are integer. In order not to lose fractional part, we need
829     // to keep the movement calculation as "floor(cur_pos) - floor(last_pos)".
830     // Remove the floor here when movement_x/y is changed to double.
831     if (!(ui_mouse_event.flags() & ui::EF_UNADJUSTED_MOUSE)) {
832       event->movement_x = gfx::ToFlooredInt(event->PositionInScreen().x()) -
833                           gfx::ToFlooredInt(global_mouse_position_.x());
834       event->movement_y = gfx::ToFlooredInt(event->PositionInScreen().y()) -
835                           gfx::ToFlooredInt(global_mouse_position_.y());
836     }
837 
838     global_mouse_position_ = event->PositionInScreen();
839   }
840 
841   // This logic is similar to |is_move_to_center_event| check when
842   // consolidated_movement disabled. We can not guarantee that |MoveCursorTo|
843   // is taking effect immediately, so wait for the event that has matching
844   // coordiantes to marked as synthesized event.
845   if (enable_consolidated_movement_ && mouse_locked_ &&
846       MatchesSynthesizedMovePosition(*event)) {
847     event->SetModifiers(event->GetModifiers() |
848                         blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
849     synthetic_move_position_.reset();
850     return;
851   }
852 
853   // Under mouse lock, coordinates of mouse are locked to what they were when
854   // mouse lock was entered.
855   if (mouse_locked_) {
856     if (!enable_consolidated_movement_) {
857       event->SetPositionInWidget(unlocked_mouse_position_.x(),
858                                  unlocked_mouse_position_.y());
859       event->SetPositionInScreen(unlocked_global_mouse_position_.x(),
860                                  unlocked_global_mouse_position_.y());
861     }
862   } else {
863     unlocked_mouse_position_ = event->PositionInWidget();
864     unlocked_global_mouse_position_ = event->PositionInScreen();
865   }
866 }
867 
MoveCursorToCenter(ui::MouseEvent * event)868 void RenderWidgetHostViewEventHandler::MoveCursorToCenter(
869     ui::MouseEvent* event) {
870   gfx::Point center(gfx::Rect(window_->bounds().size()).CenterPoint());
871   gfx::Point center_in_screen(window_->GetBoundsInScreen().CenterPoint());
872   window_->MoveCursorTo(center);
873 #if defined(OS_WIN)
874   // TODO(crbug.com/781182): Set the global position when move cursor to center.
875   // This is a workaround for a bug from Windows update 16299, and should be
876   // remove once the bug is fixed in OS. When consolidate_movement_ flag is
877   // enabled, send a synthesized event to update the blink side states.
878   global_mouse_position_ = gfx::PointF(center_in_screen);
879   if (enable_consolidated_movement_ && event) {
880     blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent(*event);
881     mouse_event.SetModifiers(
882         mouse_event.GetModifiers() |
883         blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
884     mouse_event.SetPositionInScreen(gfx::PointF(center_in_screen));
885     if (ShouldRouteEvents()) {
886       host_->delegate()->GetInputEventRouter()->RouteMouseEvent(
887           host_view_, &mouse_event, ui::LatencyInfo());
888     } else {
889       ProcessMouseEvent(mouse_event, ui::LatencyInfo());
890     }
891     return;
892   }
893 #endif
894   synthetic_move_position_ = center_in_screen;
895 }
896 
MatchesSynthesizedMovePosition(const blink::WebMouseEvent & event)897 bool RenderWidgetHostViewEventHandler::MatchesSynthesizedMovePosition(
898     const blink::WebMouseEvent& event) {
899   if (event.GetType() == blink::WebInputEvent::kMouseMove &&
900       synthetic_move_position_.has_value()) {
901     if (IsFractionalScaleFactor(host_view_->current_device_scale_factor())) {
902       // For fractional scale factors, the conversion from pixels to dip and
903       // vice versa could result in off by 1 or 2 errors which hurts us because
904       // the artificial move to center event cause the cursor to bounce around
905       // the center of the screen leading to the lock operation not working
906       // correctly. Workaround is to treat a mouse move or drag event off by
907       // atmost 2 px from the center as a move to center event.
908       // TODO(crbug.com/991236): figure out a way to avoid the conversion error.
909       return ((std::abs(event.PositionInScreen().x() -
910                         synthetic_move_position_->x()) <= 2) &&
911               (std::abs(event.PositionInScreen().y() -
912                         synthetic_move_position_->y()) <= 2));
913     } else {
914       return synthetic_move_position_.value() ==
915              gfx::ToRoundedPoint(event.PositionInScreen());
916     }
917   }
918   return false;
919 }
920 
SetKeyboardFocus()921 void RenderWidgetHostViewEventHandler::SetKeyboardFocus() {
922 #if defined(OS_WIN)
923   if (window_ && window_->delegate()->CanFocus()) {
924     aura::WindowTreeHost* host = window_->GetHost();
925     if (host) {
926       gfx::AcceleratedWidget hwnd = host->GetAcceleratedWidget();
927       if (!(::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE))
928         ::SetFocus(hwnd);
929     }
930   }
931 #endif
932   // TODO(wjmaclean): can host_ ever be null?
933   if (host_ && set_focus_on_mouse_down_or_key_event_) {
934     set_focus_on_mouse_down_or_key_event_ = false;
935     host_->Focus();
936   }
937 }
938 
ShouldMoveToCenter(gfx::PointF mouse_screen_position)939 bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter(
940     gfx::PointF mouse_screen_position) {
941   // Do not need to move to center in unadjusted movement mode as
942   // the movement value are directly from OS.
943   if (mouse_locked_unadjusted_movement_)
944     return false;
945 
946   gfx::Rect rect = window_->bounds();
947   rect = delegate_->ConvertRectToScreen(rect);
948   float border_x = rect.width() * kMouseLockBorderPercentage / 100.0;
949   float border_y = rect.height() * kMouseLockBorderPercentage / 100.0;
950 
951   return mouse_screen_position.x() < rect.x() + border_x ||
952          mouse_screen_position.x() > rect.right() - border_x ||
953          mouse_screen_position.y() < rect.y() + border_y ||
954          mouse_screen_position.y() > rect.bottom() - border_y;
955 }
956 
ShouldRouteEvents() const957 bool RenderWidgetHostViewEventHandler::ShouldRouteEvents() const {
958   if (!host_->delegate())
959     return false;
960 
961   // Do not route events that are currently targeted to page popups such as
962   // <select> element drop-downs, since these cannot contain cross-process
963   // frames.
964   if (!host_->delegate()->IsWidgetForMainFrame(host_))
965     return false;
966 
967   return !!host_->delegate()->GetInputEventRouter();
968 }
969 
ProcessMouseEvent(const blink::WebMouseEvent & event,const ui::LatencyInfo & latency)970 void RenderWidgetHostViewEventHandler::ProcessMouseEvent(
971     const blink::WebMouseEvent& event,
972     const ui::LatencyInfo& latency) {
973   host_->ForwardMouseEventWithLatencyInfo(event, latency);
974 }
975 
ProcessMouseWheelEvent(const blink::WebMouseWheelEvent & event,const ui::LatencyInfo & latency)976 void RenderWidgetHostViewEventHandler::ProcessMouseWheelEvent(
977     const blink::WebMouseWheelEvent& event,
978     const ui::LatencyInfo& latency) {
979   host_->ForwardWheelEventWithLatencyInfo(event, latency);
980 }
981 
ProcessTouchEvent(const blink::WebTouchEvent & event,const ui::LatencyInfo & latency)982 void RenderWidgetHostViewEventHandler::ProcessTouchEvent(
983     const blink::WebTouchEvent& event,
984     const ui::LatencyInfo& latency) {
985   host_->ForwardTouchEventWithLatencyInfo(event, latency);
986 }
987 
IsKeyLocked(const ui::KeyEvent & event)988 bool RenderWidgetHostViewEventHandler::IsKeyLocked(const ui::KeyEvent& event) {
989   // Note: We never consider 'ESC' to be locked as we don't want to prevent it
990   // from being handled by the browser.  Doing so would have adverse effects
991   // such as the user being unable to exit fullscreen mode.
992   if (!IsKeyboardLocked() || event.code() == ui::DomCode::ESCAPE)
993     return false;
994 
995   return scoped_keyboard_hook_->IsKeyLocked(event.code());
996 }
997 
998 }  // namespace content
999