OLD | NEW |
---|---|
(Empty) | |
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/metrics/user_metrics_action.h" | |
8 #include "content/browser/renderer_host/input/touch_selection_controller_client_ aura.h" | |
9 #include "content/browser/renderer_host/overscroll_controller.h" | |
10 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
11 #include "content/browser/renderer_host/render_view_host_delegate_view.h" | |
12 #include "content/browser/renderer_host/render_widget_host_impl.h" | |
13 #include "content/browser/renderer_host/render_widget_host_input_event_router.h" | |
14 #include "content/browser/renderer_host/render_widget_host_view_base.h" | |
15 #include "content/browser/renderer_host/text_input_manager.h" | |
16 #include "content/common/content_switches_internal.h" | |
17 #include "content/common/site_isolation_policy.h" | |
18 #include "content/public/browser/render_view_host.h" | |
19 #include "content/public/browser/render_widget_host.h" | |
20 #include "content/public/browser/user_metrics.h" | |
21 #include "ui/aura/client/cursor_client.h" | |
22 #include "ui/aura/client/focus_client.h" | |
23 #include "ui/aura/client/screen_position_client.h" | |
24 #include "ui/aura/window.h" | |
25 #include "ui/aura/window_delegate.h" | |
26 #include "ui/base/ime/text_input_client.h" | |
27 #include "ui/events/blink/blink_event_util.h" | |
28 #include "ui/events/blink/web_input_event.h" | |
29 #include "ui/touch_selection/touch_selection_controller.h" | |
30 | |
31 #if defined(OS_WIN) | |
32 #include "content/browser/frame_host/render_frame_host_impl.h" | |
33 #include "content/public/common/context_menu_params.h" | |
34 #include "ui/aura/window_tree_host.h" | |
35 #include "ui/display/screen.h" | |
36 #endif // defined(OS_WIN) | |
37 | |
38 namespace { | |
39 | |
40 // In mouse lock mode, we need to prevent the (invisible) cursor from hitting | |
41 // the border of the view, in order to get valid movement information. However, | |
42 // forcing the cursor back to the center of the view after each mouse move | |
43 // doesn't work well. It reduces the frequency of useful mouse move messages | |
44 // significantly. Therefore, we move the cursor to the center of the view only | |
45 // if it approaches the border. |kMouseLockBorderPercentage| specifies the width | |
46 // of the border area, in percentage of the corresponding dimension. | |
47 const int kMouseLockBorderPercentage = 15; | |
tdresser
2016/09/23 13:54:09
I am sad that this review forced me to learn that
jonross
2016/10/05 15:37:57
:( same
| |
48 | |
49 #if defined(OS_WIN) | |
tdresser
2016/09/23 13:54:09
Not in this patch, but I wonder how hard it would
jonross
2016/10/05 15:44:17
crbug.com/653126 filed
| |
50 // A callback function for EnumThreadWindows to enumerate and dismiss | |
51 // any owned popup windows. | |
52 BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) { | |
53 const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg); | |
54 | |
55 if (::IsWindowVisible(window)) { | |
56 const HWND owner = ::GetWindow(window, GW_OWNER); | |
57 if (toplevel_hwnd == owner) { | |
58 ::PostMessage(window, WM_CANCELMODE, 0, 0); | |
59 } | |
60 } | |
61 | |
62 return TRUE; | |
63 } | |
64 #endif // defined(OS_WIN) | |
65 | |
66 gfx::Point GetScreenLocationFromEvent(const ui::LocatedEvent& event) { | |
67 aura::Window* root = | |
68 static_cast<aura::Window*>(event.target())->GetRootWindow(); | |
69 aura::client::ScreenPositionClient* spc = | |
70 aura::client::GetScreenPositionClient(root); | |
71 if (!spc) | |
72 return event.root_location(); | |
73 | |
74 gfx::Point screen_location(event.root_location()); | |
75 spc->ConvertPointToScreen(root, &screen_location); | |
76 return screen_location; | |
77 } | |
78 | |
79 bool IsFractionalScaleFactor(float scale_factor) { | |
80 return (scale_factor - static_cast<int>(scale_factor)) > 0; | |
81 } | |
82 | |
83 // We don't mark these as handled so that they're sent back to the | |
84 // DefWindowProc so it can generate WM_APPCOMMAND as necessary. | |
85 bool IsXButtonUpEvent(const ui::MouseEvent* event) { | |
86 #if defined(OS_WIN) | |
87 switch (event->native_event().message) { | |
88 case WM_XBUTTONUP: | |
89 case WM_NCXBUTTONUP: | |
90 return true; | |
91 } | |
92 #endif | |
93 return false; | |
94 } | |
95 | |
96 // Reset unchanged touch point to StateStationary for touchmove and | |
tdresser
2016/09/23 13:54:09
point -> points
jonross
2016/10/05 15:37:57
Done.
| |
97 // touchcancel. | |
98 void MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent* event, | |
99 int changed_touch_id) { | |
100 if (event->type == blink::WebInputEvent::TouchMove || | |
101 event->type == blink::WebInputEvent::TouchCancel) { | |
102 for (size_t i = 0; i < event->touchesLength; ++i) { | |
103 if (event->touches[i].id != changed_touch_id) | |
104 event->touches[i].state = blink::WebTouchPoint::StateStationary; | |
105 } | |
106 } | |
107 } | |
108 | |
109 bool NeedsInputGrab(content::RenderWidgetHostViewBase* view) { | |
110 if (!view) | |
111 return false; | |
112 return view->GetPopupType() == blink::WebPopupTypePage; | |
113 } | |
114 | |
115 } // namespace | |
116 | |
117 namespace content { | |
118 | |
119 RenderWidgetHostViewEventHandler::Delegate::Delegate() | |
120 : selection_controller_client_(nullptr), | |
121 selection_controller_(nullptr), | |
122 overscroll_controller_(nullptr) {} | |
123 | |
124 RenderWidgetHostViewEventHandler::Delegate::~Delegate() {} | |
125 | |
126 RenderWidgetHostViewEventHandler::RenderWidgetHostViewEventHandler( | |
127 RenderWidgetHostImpl* host, | |
128 RenderWidgetHostViewBase* host_view, | |
129 Delegate* delegate) | |
130 : accept_return_character_(false), | |
131 disable_input_event_router_for_testing_(false), | |
132 mouse_locked_(false), | |
133 pinch_zoom_enabled_(content::IsPinchToZoomEnabled()), | |
134 set_focus_on_mouse_down_or_key_event_(false), | |
135 synthetic_move_sent_(false), | |
136 host_(RenderWidgetHostImpl::From(host)), | |
137 host_view_(host_view), | |
138 popup_child_host_view_(nullptr), | |
139 popup_child_event_handler_(nullptr), | |
140 delegate_(delegate), | |
141 window_(nullptr) {} | |
142 | |
143 RenderWidgetHostViewEventHandler::~RenderWidgetHostViewEventHandler() {} | |
144 | |
145 void RenderWidgetHostViewEventHandler::SetPopupChild( | |
146 RenderWidgetHostViewBase* popup_child_host_view, | |
147 ui::EventHandler* popup_child_event_handler) { | |
148 popup_child_host_view_ = popup_child_host_view; | |
149 popup_child_event_handler_ = popup_child_event_handler; | |
150 } | |
151 | |
152 void RenderWidgetHostViewEventHandler::TrackHost( | |
153 aura::Window* reference_window) { | |
154 if (!reference_window) | |
155 return; | |
156 host_tracker_.reset(new aura::WindowTracker); | |
157 host_tracker_->Add(reference_window); | |
158 } | |
159 | |
160 #if defined(OS_WIN) | |
161 void RenderWidgetHostViewEventHandler::SetContextMenuParams( | |
162 const ContextMenuParams& params) { | |
163 last_context_menu_params_.reset(); | |
164 if (params.source_type == ui::MENU_SOURCE_LONG_PRESS) { | |
165 last_context_menu_params_.reset(new ContextMenuParams); | |
166 *last_context_menu_params_ = params; | |
167 } | |
168 } | |
169 | |
170 void RenderWidgetHostViewEventHandler::UpdateMouseLockRegion() { | |
171 RECT window_rect = | |
172 display::Screen::GetScreen() | |
173 ->DIPToScreenRectInWindow(window_, window_->GetBoundsInScreen()) | |
174 .ToRECT(); | |
175 ::ClipCursor(&window_rect); | |
176 } | |
177 #endif | |
178 | |
179 bool RenderWidgetHostViewEventHandler::LockMouse() { | |
180 aura::Window* root_window = window_->GetRootWindow(); | |
181 if (!root_window) | |
182 return false; | |
183 | |
184 if (mouse_locked_) | |
185 return true; | |
186 | |
187 mouse_locked_ = true; | |
188 #if !defined(OS_WIN) | |
189 window_->SetCapture(); | |
190 #else | |
191 UpdateMouseLockRegion(); | |
192 #endif | |
193 aura::client::CursorClient* cursor_client = | |
194 aura::client::GetCursorClient(root_window); | |
195 if (cursor_client) { | |
196 cursor_client->HideCursor(); | |
197 cursor_client->LockCursor(); | |
198 } | |
199 | |
200 if (ShouldMoveToCenter()) { | |
201 synthetic_move_sent_ = true; | |
202 window_->MoveCursorTo(gfx::Rect(window_->bounds().size()).CenterPoint()); | |
203 } | |
204 delegate_->SetTooltipsEnabled(false); | |
205 return true; | |
206 } | |
207 | |
208 void RenderWidgetHostViewEventHandler::UnlockMouse() { | |
209 delegate_->SetTooltipsEnabled(true); | |
210 | |
211 aura::Window* root_window = window_->GetRootWindow(); | |
212 if (!mouse_locked_ || !root_window) | |
213 return; | |
214 | |
215 mouse_locked_ = false; | |
216 | |
217 if (window_->HasCapture()) | |
218 window_->ReleaseCapture(); | |
219 | |
220 #if defined(OS_WIN) | |
221 ::ClipCursor(NULL); | |
222 #endif | |
223 | |
224 // Ensure that the global mouse position is updated here to its original | |
225 // value. If we don't do this then the synthesized mouse move which is posted | |
226 // after the cursor is moved ends up getting a large movement delta which is | |
227 // not what sites expect. The delta is computed in the | |
228 // ModifyEventMovementAndCoords function. | |
229 global_mouse_position_ = unlocked_global_mouse_position_; | |
230 window_->MoveCursorTo(unlocked_mouse_position_); | |
231 | |
232 aura::client::CursorClient* cursor_client = | |
233 aura::client::GetCursorClient(root_window); | |
234 if (cursor_client) { | |
235 cursor_client->UnlockCursor(); | |
236 cursor_client->ShowCursor(); | |
237 } | |
238 host_->LostMouseLock(); | |
239 } | |
240 | |
241 void RenderWidgetHostViewEventHandler::OnKeyEvent(ui::KeyEvent* event) { | |
242 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnKeyEvent"); | |
243 | |
244 if (NeedsInputGrab(popup_child_host_view_)) { | |
245 popup_child_event_handler_->OnKeyEvent(event); | |
246 if (event->handled()) | |
247 return; | |
248 } | |
249 | |
250 // We need to handle the Escape key for Pepper Flash. | |
251 if (host_view_->is_fullscreen() && event->key_code() == ui::VKEY_ESCAPE) { | |
252 // Focus the window we were created from. | |
253 if (host_tracker_.get() && !host_tracker_->windows().empty()) { | |
254 aura::Window* host = *(host_tracker_->windows().begin()); | |
255 aura::client::FocusClient* client = aura::client::GetFocusClient(host); | |
256 if (client) { | |
257 // Calling host->Focus() may delete |this|. We create a local observer | |
258 // for that. In that case we exit without further access to any members. | |
259 aura::WindowTracker tracker; | |
260 aura::Window* window = window_; | |
261 tracker.Add(window); | |
262 host->Focus(); | |
263 if (!tracker.Contains(window)) { | |
264 event->SetHandled(); | |
265 return; | |
266 } | |
267 } | |
268 } | |
269 delegate_->Shutdown(); | |
270 } else { | |
271 if (event->key_code() == ui::VKEY_RETURN) { | |
272 // Do not forward return key release events if no press event was handled. | |
273 if (event->type() == ui::ET_KEY_RELEASED && !accept_return_character_) | |
274 return; | |
275 // Accept return key character events between press and release events. | |
276 accept_return_character_ = event->type() == ui::ET_KEY_PRESSED; | |
277 } | |
278 | |
279 // Call SetKeyboardFocus() for not only ET_KEY_PRESSED but also | |
280 // ET_KEY_RELEASED. If a user closed the hotdog menu with ESC key press, | |
281 // we need to notify focus to Blink on ET_KEY_RELEASED for ESC key. | |
282 SetKeyboardFocus(); | |
283 // We don't have to communicate with an input method here. | |
284 NativeWebKeyboardEvent webkit_event(*event); | |
285 delegate_->ForwardKeyboardEvent(webkit_event); | |
286 } | |
287 event->SetHandled(); | |
288 } | |
289 | |
290 void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) { | |
291 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnMouseEvent"); | |
292 ForwardMouseEventToParent(event); | |
293 // TODO(mgiuca): Return if event->handled() returns true. This currently | |
294 // breaks drop-down lists which means something is incorrectly setting | |
295 // event->handled to true (http://crbug.com/577983). | |
296 | |
297 if (mouse_locked_) { | |
298 HandleMouseEventWhileLocked(event); | |
299 return; | |
300 } | |
301 | |
302 // As the overscroll is handled during scroll events from the trackpad, the | |
303 // RWHVA window is transformed by the overscroll controller. This transform | |
304 // triggers a synthetic mouse-move event to be generated (by the aura | |
305 // RootWindow). But this event interferes with the overscroll gesture. So, | |
306 // ignore such synthetic mouse-move events if an overscroll gesture is in | |
307 // progress. | |
308 OverscrollController* overscroll_controller = | |
309 delegate_->overscroll_controller(); | |
310 if (overscroll_controller && | |
311 overscroll_controller->overscroll_mode() != OVERSCROLL_NONE && | |
312 event->flags() & ui::EF_IS_SYNTHESIZED && | |
313 (event->type() == ui::ET_MOUSE_ENTERED || | |
314 event->type() == ui::ET_MOUSE_EXITED || | |
315 event->type() == ui::ET_MOUSE_MOVED)) { | |
316 event->StopPropagation(); | |
317 return; | |
318 } | |
319 | |
320 if (event->type() == ui::ET_MOUSEWHEEL) { | |
321 #if defined(OS_WIN) | |
322 // We get mouse wheel/scroll messages even if we are not in the foreground. | |
323 // So here we check if we have any owned popup windows in the foreground and | |
324 // dismiss them. | |
325 aura::WindowTreeHost* host = window_->GetHost(); | |
326 if (host) { | |
327 HWND parent = host->GetAcceleratedWidget(); | |
328 HWND toplevel_hwnd = ::GetAncestor(parent, GA_ROOT); | |
329 EnumThreadWindows(GetCurrentThreadId(), DismissOwnedPopups, | |
330 reinterpret_cast<LPARAM>(toplevel_hwnd)); | |
331 } | |
332 #endif | |
333 blink::WebMouseWheelEvent mouse_wheel_event = | |
334 ui::MakeWebMouseWheelEvent(static_cast<ui::MouseWheelEvent&>(*event), | |
335 base::Bind(&GetScreenLocationFromEvent)); | |
336 if (mouse_wheel_event.deltaX != 0 || mouse_wheel_event.deltaY != 0) { | |
337 if (ShouldRouteEvent(event)) { | |
338 host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent( | |
339 host_view_, &mouse_wheel_event); | |
340 } else { | |
341 ProcessMouseWheelEvent(mouse_wheel_event, *event->latency()); | |
342 } | |
343 } | |
344 } else { | |
345 bool is_selection_popup = NeedsInputGrab(popup_child_host_view_); | |
346 if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) && | |
347 !(event->flags() & ui::EF_FROM_TOUCH)) { | |
348 // Confirm existing composition text on mouse press, to make sure | |
349 // the input caret won't be moved with an ongoing composition text. | |
350 if (event->type() == ui::ET_MOUSE_PRESSED) | |
351 FinishImeCompositionSession(); | |
352 | |
353 blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent( | |
354 *event, base::Bind(&GetScreenLocationFromEvent)); | |
355 ModifyEventMovementAndCoords(&mouse_event); | |
356 if (ShouldRouteEvent(event)) { | |
357 host_->delegate()->GetInputEventRouter()->RouteMouseEvent(host_view_, | |
358 &mouse_event); | |
359 } else { | |
360 ProcessMouseEvent(mouse_event, *event->latency()); | |
361 } | |
362 | |
363 // Ensure that we get keyboard focus on mouse down as a plugin window may | |
364 // have grabbed keyboard focus. | |
365 if (event->type() == ui::ET_MOUSE_PRESSED) | |
366 SetKeyboardFocus(); | |
367 } | |
368 } | |
369 | |
370 switch (event->type()) { | |
371 case ui::ET_MOUSE_PRESSED: | |
372 window_->SetCapture(); | |
373 break; | |
374 case ui::ET_MOUSE_RELEASED: | |
375 if (!delegate_->NeedsMouseCapture()) | |
376 window_->ReleaseCapture(); | |
377 break; | |
378 default: | |
379 break; | |
380 } | |
381 | |
382 if (!IsXButtonUpEvent(event)) | |
383 event->SetHandled(); | |
384 } | |
385 | |
386 void RenderWidgetHostViewEventHandler::OnScrollEvent(ui::ScrollEvent* event) { | |
387 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnScrollEvent"); | |
388 | |
389 if (event->type() == ui::ET_SCROLL) { | |
390 #if !defined(OS_WIN) | |
391 // TODO(ananta) | |
392 // Investigate if this is true for Windows 8 Metro ASH as well. | |
393 if (event->finger_count() != 2) | |
394 return; | |
395 #endif | |
396 blink::WebGestureEvent gesture_event = ui::MakeWebGestureEventFlingCancel(); | |
397 // Coordinates need to be transferred to the fling cancel gesture only | |
398 // for Surface-targeting to ensure that it is targeted to the correct | |
399 // RenderWidgetHost. | |
400 gesture_event.x = event->x(); | |
401 gesture_event.y = event->y(); | |
402 blink::WebMouseWheelEvent mouse_wheel_event = ui::MakeWebMouseWheelEvent( | |
403 *event, base::Bind(&GetScreenLocationFromEvent)); | |
404 if (ShouldRouteEvent(event)) { | |
405 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
406 host_view_, &gesture_event, ui::LatencyInfo()); | |
407 host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent( | |
408 host_view_, &mouse_wheel_event); | |
409 } else { | |
410 host_->ForwardGestureEvent(gesture_event); | |
411 host_->ForwardWheelEventWithLatencyInfo(mouse_wheel_event, | |
412 *event->latency()); | |
413 } | |
414 RecordAction(base::UserMetricsAction("TrackpadScroll")); | |
415 } else if (event->type() == ui::ET_SCROLL_FLING_START || | |
416 event->type() == ui::ET_SCROLL_FLING_CANCEL) { | |
417 blink::WebGestureEvent gesture_event = ui::MakeWebGestureEvent( | |
418 *event, base::Bind(&GetScreenLocationFromEvent)); | |
419 if (ShouldRouteEvent(event)) { | |
420 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
421 host_view_, &gesture_event, ui::LatencyInfo()); | |
422 } else { | |
423 host_->ForwardGestureEvent(gesture_event); | |
424 } | |
425 if (event->type() == ui::ET_SCROLL_FLING_START) | |
426 RecordAction(base::UserMetricsAction("TrackpadScrollFling")); | |
427 } | |
428 | |
429 event->SetHandled(); | |
430 } | |
431 | |
432 void RenderWidgetHostViewEventHandler::OnTouchEvent(ui::TouchEvent* event) { | |
433 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnTouchEvent"); | |
434 | |
435 bool had_no_pointer = !pointer_state_.GetPointerCount(); | |
436 | |
437 // Update the touch event first. | |
438 if (!pointer_state_.OnTouch(*event)) { | |
439 event->StopPropagation(); | |
440 return; | |
441 } | |
442 | |
443 blink::WebTouchEvent touch_event; | |
444 bool handled = | |
445 delegate_->selection_controller()->WillHandleTouchEvent(pointer_state_); | |
446 if (handled) { | |
447 event->SetHandled(); | |
448 } else { | |
449 touch_event = ui::CreateWebTouchEventFromMotionEvent( | |
450 pointer_state_, event->may_cause_scrolling()); | |
451 } | |
452 pointer_state_.CleanupRemovedTouchPoints(*event); | |
453 | |
454 if (handled) | |
455 return; | |
456 | |
457 if (had_no_pointer) | |
458 delegate_->selection_controller_client()->OnTouchDown(); | |
459 if (!pointer_state_.GetPointerCount()) | |
460 delegate_->selection_controller_client()->OnTouchUp(); | |
461 | |
462 // It is important to always mark events as being handled asynchronously when | |
463 // they are forwarded. This ensures that the current event does not get | |
464 // processed by the gesture recognizer before events currently awaiting | |
465 // dispatch in the touch queue. | |
466 event->DisableSynchronousHandling(); | |
467 | |
468 // Set unchanged touch point to StateStationary for touchmove and | |
469 // touchcancel to make sure only send one ack per WebTouchEvent. | |
470 MarkUnchangedTouchPointsAsStationary(&touch_event, event->touch_id()); | |
471 if (ShouldRouteEvent(event)) { | |
472 host_->delegate()->GetInputEventRouter()->RouteTouchEvent( | |
473 host_view_, &touch_event, *event->latency()); | |
474 } else { | |
475 ProcessTouchEvent(touch_event, *event->latency()); | |
476 } | |
477 } | |
478 | |
479 void RenderWidgetHostViewEventHandler::OnGestureEvent(ui::GestureEvent* event) { | |
480 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnGestureEvent"); | |
481 | |
482 if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN || | |
483 event->type() == ui::ET_GESTURE_PINCH_UPDATE || | |
484 event->type() == ui::ET_GESTURE_PINCH_END) && | |
485 !pinch_zoom_enabled_) { | |
486 event->SetHandled(); | |
487 return; | |
488 } | |
489 | |
490 HandleGestureForTouchSelection(event); | |
491 if (event->handled()) | |
492 return; | |
493 | |
494 // Confirm existing composition text on TAP gesture, to make sure the input | |
495 // caret won't be moved with an ongoing composition text. | |
496 if (event->type() == ui::ET_GESTURE_TAP) | |
497 FinishImeCompositionSession(); | |
498 | |
499 blink::WebGestureEvent gesture = | |
500 ui::MakeWebGestureEvent(*event, base::Bind(&GetScreenLocationFromEvent)); | |
501 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
502 // Webkit does not stop a fling-scroll on tap-down. So explicitly send an | |
503 // event to stop any in-progress flings. | |
504 blink::WebGestureEvent fling_cancel = gesture; | |
505 fling_cancel.type = blink::WebInputEvent::GestureFlingCancel; | |
506 fling_cancel.sourceDevice = blink::WebGestureDeviceTouchscreen; | |
507 if (ShouldRouteEvent(event)) { | |
508 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
509 host_view_, &fling_cancel, ui::LatencyInfo()); | |
510 } else { | |
511 host_->ForwardGestureEvent(fling_cancel); | |
512 } | |
513 } | |
514 | |
515 if (gesture.type != blink::WebInputEvent::Undefined) { | |
516 if (ShouldRouteEvent(event)) { | |
517 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
518 host_view_, &gesture, *event->latency()); | |
519 } else { | |
520 host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency()); | |
521 } | |
522 | |
523 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || | |
524 event->type() == ui::ET_GESTURE_SCROLL_UPDATE || | |
525 event->type() == ui::ET_GESTURE_SCROLL_END) { | |
526 RecordAction(base::UserMetricsAction("TouchscreenScroll")); | |
527 } else if (event->type() == ui::ET_SCROLL_FLING_START) { | |
528 RecordAction(base::UserMetricsAction("TouchscreenScrollFling")); | |
529 } | |
530 } | |
531 | |
532 // If a gesture is not processed by the webpage, then WebKit processes it | |
533 // (e.g. generates synthetic mouse events). | |
534 event->SetHandled(); | |
535 } | |
536 | |
537 bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent( | |
538 const ui::MouseEvent* event, | |
539 bool mouse_locked, | |
540 bool selection_popup) const { | |
541 if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) | |
542 return false; | |
543 | |
544 if (event->type() == ui::ET_MOUSE_EXITED) { | |
545 if (mouse_locked || selection_popup) | |
546 return false; | |
547 #if defined(OS_WIN) | |
548 // Don't forward the mouse leave message which is received when the context | |
549 // menu is displayed by the page. This confuses the page and causes state | |
550 // changes. | |
551 if (host_view_->IsShowingContextMenu()) | |
552 return false; | |
553 #endif | |
554 return true; | |
555 } | |
556 | |
557 #if defined(OS_WIN) | |
558 // Renderer cannot handle WM_XBUTTON or NC events. | |
559 switch (event->native_event().message) { | |
560 case WM_XBUTTONDOWN: | |
561 case WM_XBUTTONUP: | |
562 case WM_XBUTTONDBLCLK: | |
563 case WM_NCMOUSELEAVE: | |
564 case WM_NCMOUSEMOVE: | |
565 case WM_NCLBUTTONDOWN: | |
566 case WM_NCLBUTTONUP: | |
567 case WM_NCLBUTTONDBLCLK: | |
568 case WM_NCRBUTTONDOWN: | |
569 case WM_NCRBUTTONUP: | |
570 case WM_NCRBUTTONDBLCLK: | |
571 case WM_NCMBUTTONDOWN: | |
572 case WM_NCMBUTTONUP: | |
573 case WM_NCMBUTTONDBLCLK: | |
574 case WM_NCXBUTTONDOWN: | |
575 case WM_NCXBUTTONUP: | |
576 case WM_NCXBUTTONDBLCLK: | |
577 return false; | |
578 default: | |
579 break; | |
580 } | |
581 #elif defined(USE_X11) | |
582 // Renderer only supports standard mouse buttons, so ignore programmable | |
583 // buttons. | |
584 switch (event->type()) { | |
585 case ui::ET_MOUSE_PRESSED: | |
586 case ui::ET_MOUSE_RELEASED: { | |
587 const int kAllowedButtons = ui::EF_LEFT_MOUSE_BUTTON | | |
588 ui::EF_MIDDLE_MOUSE_BUTTON | | |
589 ui::EF_RIGHT_MOUSE_BUTTON; | |
590 return (event->flags() & kAllowedButtons) != 0; | |
591 } | |
592 default: | |
593 break; | |
594 } | |
595 #endif | |
596 return true; | |
597 } | |
598 | |
599 void RenderWidgetHostViewEventHandler::FinishImeCompositionSession() { | |
600 if (!host_view_->GetTextInputClient()->HasCompositionText()) | |
601 return; | |
602 | |
603 TextInputManager* text_input_manager = host_view_->GetTextInputManager(); | |
604 if (!!text_input_manager && !!text_input_manager->GetActiveWidget()) | |
605 text_input_manager->GetActiveWidget()->ImeFinishComposingText(false); | |
606 host_view_->ImeCancelComposition(); | |
607 } | |
608 | |
609 void RenderWidgetHostViewEventHandler::ForwardMouseEventToParent( | |
610 ui::MouseEvent* event) { | |
611 // Needed to propagate mouse event to |window_->parent()->delegate()|, but | |
612 // note that it might be something other than a WebContentsViewAura instance. | |
613 // TODO(pkotwicz): Find a better way of doing this. | |
614 // In fullscreen mode which is typically used by flash, don't forward | |
615 // the mouse events to the parent. The renderer and the plugin process | |
616 // handle these events. | |
617 if (host_view_->is_fullscreen()) | |
618 return; | |
619 | |
620 if (event->flags() & ui::EF_FROM_TOUCH) | |
621 return; | |
622 | |
623 if (!window_->parent() || !window_->parent()->delegate()) | |
624 return; | |
625 | |
626 // Take a copy of |event|, to avoid ConvertLocationToTarget mutating the | |
627 // event. | |
628 std::unique_ptr<ui::Event> event_copy = ui::Event::Clone(*event); | |
629 ui::MouseEvent* mouse_event = static_cast<ui::MouseEvent*>(event_copy.get()); | |
630 mouse_event->ConvertLocationToTarget(window_, window_->parent()); | |
631 window_->parent()->delegate()->OnMouseEvent(mouse_event); | |
632 if (mouse_event->handled()) | |
633 event->SetHandled(); | |
634 } | |
635 | |
636 RenderViewHostDelegateView* | |
637 RenderWidgetHostViewEventHandler::GetRenderViewHostDelegateView() { | |
638 // Use RenderViewHostDelegate to get to the WebContentsViewAura, which will | |
639 // actually show the disambiguation popup. | |
640 RenderViewHost* rvh = RenderViewHost::From(host_); | |
641 if (!rvh) | |
642 return nullptr; | |
643 | |
644 RenderViewHostDelegate* delegate = rvh->GetDelegate(); | |
645 if (!delegate) | |
646 return nullptr; | |
647 | |
648 return delegate->GetDelegateView(); | |
649 } | |
650 | |
651 void RenderWidgetHostViewEventHandler::HandleGestureForTouchSelection( | |
652 ui::GestureEvent* event) { | |
653 switch (event->type()) { | |
654 case ui::ET_GESTURE_LONG_PRESS: | |
655 if (delegate_->selection_controller()->WillHandleLongPressEvent( | |
656 event->time_stamp(), event->location_f())) { | |
657 event->SetHandled(); | |
658 } | |
659 break; | |
660 case ui::ET_GESTURE_TAP: | |
661 if (delegate_->selection_controller()->WillHandleTapEvent( | |
662 event->location_f(), event->details().tap_count())) { | |
663 event->SetHandled(); | |
664 } | |
665 break; | |
666 case ui::ET_GESTURE_SCROLL_BEGIN: | |
667 delegate_->selection_controller_client()->OnScrollStarted(); | |
668 break; | |
669 case ui::ET_GESTURE_SCROLL_END: | |
670 delegate_->selection_controller_client()->OnScrollCompleted(); | |
671 break; | |
672 #if defined(OS_WIN) | |
673 case ui::ET_GESTURE_LONG_TAP: { | |
674 if (!last_context_menu_params_) | |
675 break; | |
676 | |
677 std::unique_ptr<ContextMenuParams> context_menu_params = | |
678 std::move(last_context_menu_params_); | |
679 | |
680 // On Windows we want to display the context menu when the long press | |
681 // gesture is released. To achieve that, we switch the saved context | |
682 // menu params source type to MENU_SOURCE_TOUCH. This is to ensure that | |
683 // the RenderWidgetHostViewBase::OnShowContextMenu function which is | |
684 // called from the ShowContextMenu call below, does not treat it as | |
685 // a context menu request coming in from the long press gesture. | |
686 DCHECK(context_menu_params->source_type == ui::MENU_SOURCE_LONG_PRESS); | |
687 context_menu_params->source_type = ui::MENU_SOURCE_TOUCH; | |
688 | |
689 RenderViewHostDelegateView* delegate_view = | |
690 GetRenderViewHostDelegateView(); | |
691 if (delegate_view) | |
692 delegate_view->ShowContextMenu(delegate_->GetFocusedFrame(), | |
693 *context_menu_params); | |
694 | |
695 event->SetHandled(); | |
696 // WARNING: we may have been deleted during the call to ShowContextMenu(). | |
697 break; | |
698 } | |
699 #endif | |
700 default: | |
701 break; | |
702 } | |
703 } | |
704 | |
705 void RenderWidgetHostViewEventHandler::HandleMouseEventWhileLocked( | |
706 ui::MouseEvent* event) { | |
707 aura::client::CursorClient* cursor_client = | |
708 aura::client::GetCursorClient(window_->GetRootWindow()); | |
709 | |
710 DCHECK(!cursor_client || !cursor_client->IsCursorVisible()); | |
711 | |
712 if (event->type() == ui::ET_MOUSEWHEEL) { | |
713 blink::WebMouseWheelEvent mouse_wheel_event = | |
714 ui::MakeWebMouseWheelEvent(static_cast<ui::MouseWheelEvent&>(*event), | |
715 base::Bind(&GetScreenLocationFromEvent)); | |
716 if (mouse_wheel_event.deltaX != 0 || mouse_wheel_event.deltaY != 0) | |
717 host_->ForwardWheelEvent(mouse_wheel_event); | |
718 return; | |
719 } | |
720 | |
721 gfx::Point center(gfx::Rect(window_->bounds().size()).CenterPoint()); | |
722 | |
723 // If we receive non client mouse messages while we are in the locked state | |
724 // it probably means that the mouse left the borders of our window and | |
725 // needs to be moved back to the center. | |
726 if (event->flags() & ui::EF_IS_NON_CLIENT) { | |
727 synthetic_move_sent_ = true; | |
728 window_->MoveCursorTo(center); | |
729 return; | |
730 } | |
731 | |
732 blink::WebMouseEvent mouse_event = | |
733 ui::MakeWebMouseEvent(*event, base::Bind(&GetScreenLocationFromEvent)); | |
734 | |
735 bool is_move_to_center_event = (event->type() == ui::ET_MOUSE_MOVED || | |
736 event->type() == ui::ET_MOUSE_DRAGGED) && | |
737 mouse_event.x == center.x() && | |
738 mouse_event.y == center.y(); | |
739 | |
740 // For fractional scale factors, the conversion from pixels to dip and | |
741 // vice versa could result in off by 1 or 2 errors which hurts us because | |
742 // we want to avoid sending the artificial move to center event to the | |
743 // renderer. Sending the move to center to the renderer cause the cursor | |
744 // to bounce around the center of the screen leading to the lock operation | |
745 // not working correctly. | |
746 // Workaround is to treat a mouse move or drag event off by at most 2 px | |
747 // from the center as a move to center event. | |
748 if (synthetic_move_sent_ && | |
749 IsFractionalScaleFactor(host_view_->current_device_scale_factor())) { | |
750 if (event->type() == ui::ET_MOUSE_MOVED || | |
751 event->type() == ui::ET_MOUSE_DRAGGED) { | |
752 if ((abs(mouse_event.x - center.x()) <= 2) && | |
753 (abs(mouse_event.y - center.y()) <= 2)) { | |
754 is_move_to_center_event = true; | |
755 } | |
756 } | |
757 } | |
758 | |
759 ModifyEventMovementAndCoords(&mouse_event); | |
760 | |
761 bool should_not_forward = is_move_to_center_event && synthetic_move_sent_; | |
762 if (should_not_forward) { | |
763 synthetic_move_sent_ = false; | |
764 } else { | |
765 // Check if the mouse has reached the border and needs to be centered. | |
766 if (ShouldMoveToCenter()) { | |
767 synthetic_move_sent_ = true; | |
768 window_->MoveCursorTo(center); | |
769 } | |
770 bool is_selection_popup = NeedsInputGrab(popup_child_host_view_); | |
771 // Forward event to renderer. | |
772 if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) && | |
773 !(event->flags() & ui::EF_FROM_TOUCH)) { | |
774 host_->ForwardMouseEvent(mouse_event); | |
775 // Ensure that we get keyboard focus on mouse down as a plugin window | |
776 // may have grabbed keyboard focus. | |
777 if (event->type() == ui::ET_MOUSE_PRESSED) | |
778 SetKeyboardFocus(); | |
779 } | |
780 } | |
781 } | |
782 | |
783 void RenderWidgetHostViewEventHandler::ModifyEventMovementAndCoords( | |
784 blink::WebMouseEvent* event) { | |
785 // If the mouse has just entered, we must report zero movementX/Y. Hence we | |
786 // reset any global_mouse_position set previously. | |
787 if (event->type == blink::WebInputEvent::MouseEnter || | |
788 event->type == blink::WebInputEvent::MouseLeave) | |
789 global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
790 | |
791 // Movement is computed by taking the difference of the new cursor position | |
792 // and the previous. Under mouse lock the cursor will be warped back to the | |
793 // center so that we are not limited by clipping boundaries. | |
794 // We do not measure movement as the delta from cursor to center because | |
795 // we may receive more mouse movement events before our warp has taken | |
796 // effect. | |
797 event->movementX = event->globalX - global_mouse_position_.x(); | |
798 event->movementY = event->globalY - global_mouse_position_.y(); | |
799 | |
800 global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
801 | |
802 // Under mouse lock, coordinates of mouse are locked to what they were when | |
803 // mouse lock was entered. | |
804 if (mouse_locked_) { | |
805 event->x = unlocked_mouse_position_.x(); | |
806 event->y = unlocked_mouse_position_.y(); | |
807 event->windowX = unlocked_mouse_position_.x(); | |
808 event->windowY = unlocked_mouse_position_.y(); | |
809 event->globalX = unlocked_global_mouse_position_.x(); | |
810 event->globalY = unlocked_global_mouse_position_.y(); | |
811 } else { | |
812 unlocked_mouse_position_.SetPoint(event->x, event->y); | |
813 unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
814 } | |
815 } | |
816 | |
817 void RenderWidgetHostViewEventHandler::SetKeyboardFocus() { | |
818 #if defined(OS_WIN) | |
819 if (window_ && window_->delegate()->CanFocus()) { | |
820 aura::WindowTreeHost* host = window_->GetHost(); | |
821 if (host) { | |
822 gfx::AcceleratedWidget hwnd = host->GetAcceleratedWidget(); | |
823 if (!(::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE)) | |
824 ::SetFocus(hwnd); | |
825 } | |
826 } | |
827 #endif | |
828 // TODO(wjmaclean): can host_ ever be null? | |
829 if (host_ && set_focus_on_mouse_down_or_key_event_) { | |
830 set_focus_on_mouse_down_or_key_event_ = false; | |
831 host_->Focus(); | |
832 } | |
833 } | |
834 | |
835 bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter() { | |
836 gfx::Rect rect = window_->bounds(); | |
837 rect = delegate_->ConvertRectToScreen(rect); | |
838 int border_x = rect.width() * kMouseLockBorderPercentage / 100; | |
839 int border_y = rect.height() * kMouseLockBorderPercentage / 100; | |
840 | |
841 return global_mouse_position_.x() < rect.x() + border_x || | |
842 global_mouse_position_.x() > rect.right() - border_x || | |
843 global_mouse_position_.y() < rect.y() + border_y || | |
844 global_mouse_position_.y() > rect.bottom() - border_y; | |
845 } | |
846 | |
847 bool RenderWidgetHostViewEventHandler::ShouldRouteEvent( | |
848 const ui::Event* event) const { | |
849 // We should route an event in two cases: | |
850 // 1) Mouse events are routed only if cross-process frames are possible. | |
851 // 2) Touch events are always routed. In the absence of a BrowserPlugin | |
852 // we expect the routing to always send the event to this view. If | |
853 // one or more BrowserPlugins are present, then the event may be targeted | |
854 // to one of them, or this view. This allows GuestViews to have access to | |
855 // them while still forcing pinch-zoom to be handled by the top-level | |
856 // frame. TODO(wjmaclean): At present, this doesn't work for OOPIF, but | |
857 // it should be a simple extension to modify RenderWidgetHostViewChildFrame | |
858 // in a similar manner to RenderWidgetHostViewGuest. | |
859 bool result = host_->delegate() && host_->delegate()->GetInputEventRouter() && | |
860 !disable_input_event_router_for_testing_; | |
861 // ScrollEvents get transformed into MouseWheel events, and so are treated | |
862 // the same as mouse events for routing purposes. | |
863 if (event->IsMouseEvent() || event->type() == ui::ET_SCROLL) | |
864 result = result && SiteIsolationPolicy::AreCrossProcessFramesPossible(); | |
865 return result; | |
866 } | |
867 | |
868 void RenderWidgetHostViewEventHandler::ProcessMouseEvent( | |
869 const blink::WebMouseEvent& event, | |
870 const ui::LatencyInfo& latency) { | |
871 host_->ForwardMouseEventWithLatencyInfo(event, latency); | |
872 } | |
873 | |
874 void RenderWidgetHostViewEventHandler::ProcessMouseWheelEvent( | |
875 const blink::WebMouseWheelEvent& event, | |
876 const ui::LatencyInfo& latency) { | |
877 host_->ForwardWheelEventWithLatencyInfo(event, latency); | |
878 } | |
879 | |
880 void RenderWidgetHostViewEventHandler::ProcessTouchEvent( | |
881 const blink::WebTouchEvent& event, | |
882 const ui::LatencyInfo& latency) { | |
883 host_->ForwardTouchEventWithLatencyInfo(event, latency); | |
884 } | |
885 | |
886 } // namespace content | |
OLD | NEW |