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; | |
48 | |
49 #if defined(OS_WIN) | |
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 points to StateStationary for touchmove and | |
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); | |
sadrul
2016/10/13 16:27:41
Maybe DCHECK that host_tracker_ is null or empty?
jonross
2016/10/17 16:04:02
Done.
| |
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; | |
sadrul
2016/10/13 16:27:41
Actually, can you use host_tracker_ for this? I me
jonross
2016/10/17 16:04:02
Done.
| |
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(); | |
sadrul
2016/10/13 16:27:41
Should this reset |host_tracker_|?
jonross
2016/10/17 16:04:02
Done.
| |
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, *event->latency()); | |
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( | |
358 host_view_, &mouse_event, *event->latency()); | |
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, | |
407 ui::LatencyInfo(ui::SourceEventType::WHEEL)); | |
408 host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent( | |
409 host_view_, &mouse_wheel_event, *event->latency()); | |
410 } else { | |
411 host_->ForwardGestureEvent(gesture_event); | |
412 host_->ForwardWheelEventWithLatencyInfo(mouse_wheel_event, | |
413 *event->latency()); | |
414 } | |
415 RecordAction(base::UserMetricsAction("TrackpadScroll")); | |
416 } else if (event->type() == ui::ET_SCROLL_FLING_START || | |
417 event->type() == ui::ET_SCROLL_FLING_CANCEL) { | |
418 blink::WebGestureEvent gesture_event = ui::MakeWebGestureEvent( | |
419 *event, base::Bind(&GetScreenLocationFromEvent)); | |
420 if (ShouldRouteEvent(event)) { | |
421 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
422 host_view_, &gesture_event, | |
423 ui::LatencyInfo(ui::SourceEventType::WHEEL)); | |
424 } else { | |
425 host_->ForwardGestureEvent(gesture_event); | |
426 } | |
427 if (event->type() == ui::ET_SCROLL_FLING_START) | |
428 RecordAction(base::UserMetricsAction("TrackpadScrollFling")); | |
429 } | |
430 | |
431 event->SetHandled(); | |
432 } | |
433 | |
434 void RenderWidgetHostViewEventHandler::OnTouchEvent(ui::TouchEvent* event) { | |
435 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnTouchEvent"); | |
436 | |
437 bool had_no_pointer = !pointer_state_.GetPointerCount(); | |
438 | |
439 // Update the touch event first. | |
440 if (!pointer_state_.OnTouch(*event)) { | |
441 event->StopPropagation(); | |
442 return; | |
443 } | |
444 | |
445 blink::WebTouchEvent touch_event; | |
446 bool handled = | |
447 delegate_->selection_controller()->WillHandleTouchEvent(pointer_state_); | |
448 if (handled) { | |
449 event->SetHandled(); | |
450 } else { | |
451 touch_event = ui::CreateWebTouchEventFromMotionEvent( | |
452 pointer_state_, event->may_cause_scrolling()); | |
453 } | |
454 pointer_state_.CleanupRemovedTouchPoints(*event); | |
455 | |
456 if (handled) | |
457 return; | |
458 | |
459 if (had_no_pointer) | |
460 delegate_->selection_controller_client()->OnTouchDown(); | |
461 if (!pointer_state_.GetPointerCount()) | |
462 delegate_->selection_controller_client()->OnTouchUp(); | |
463 | |
464 // It is important to always mark events as being handled asynchronously when | |
465 // they are forwarded. This ensures that the current event does not get | |
466 // processed by the gesture recognizer before events currently awaiting | |
467 // dispatch in the touch queue. | |
468 event->DisableSynchronousHandling(); | |
469 | |
470 // Set unchanged touch point to StateStationary for touchmove and | |
471 // touchcancel to make sure only send one ack per WebTouchEvent. | |
472 MarkUnchangedTouchPointsAsStationary(&touch_event, event->touch_id()); | |
473 if (ShouldRouteEvent(event)) { | |
474 host_->delegate()->GetInputEventRouter()->RouteTouchEvent( | |
475 host_view_, &touch_event, *event->latency()); | |
476 } else { | |
477 ProcessTouchEvent(touch_event, *event->latency()); | |
478 } | |
479 } | |
480 | |
481 void RenderWidgetHostViewEventHandler::OnGestureEvent(ui::GestureEvent* event) { | |
482 TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnGestureEvent"); | |
483 | |
484 if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN || | |
485 event->type() == ui::ET_GESTURE_PINCH_UPDATE || | |
486 event->type() == ui::ET_GESTURE_PINCH_END) && | |
487 !pinch_zoom_enabled_) { | |
488 event->SetHandled(); | |
489 return; | |
490 } | |
491 | |
492 HandleGestureForTouchSelection(event); | |
493 if (event->handled()) | |
494 return; | |
495 | |
496 // Confirm existing composition text on TAP gesture, to make sure the input | |
497 // caret won't be moved with an ongoing composition text. | |
498 if (event->type() == ui::ET_GESTURE_TAP) | |
499 FinishImeCompositionSession(); | |
500 | |
501 blink::WebGestureEvent gesture = | |
502 ui::MakeWebGestureEvent(*event, base::Bind(&GetScreenLocationFromEvent)); | |
503 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
504 // Webkit does not stop a fling-scroll on tap-down. So explicitly send an | |
505 // event to stop any in-progress flings. | |
506 blink::WebGestureEvent fling_cancel = gesture; | |
507 fling_cancel.type = blink::WebInputEvent::GestureFlingCancel; | |
508 fling_cancel.sourceDevice = blink::WebGestureDeviceTouchscreen; | |
509 if (ShouldRouteEvent(event)) { | |
510 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
511 host_view_, &fling_cancel, | |
512 ui::LatencyInfo(ui::SourceEventType::TOUCH)); | |
513 } else { | |
514 host_->ForwardGestureEvent(fling_cancel); | |
515 } | |
516 } | |
517 | |
518 if (gesture.type != blink::WebInputEvent::Undefined) { | |
519 if (ShouldRouteEvent(event)) { | |
520 host_->delegate()->GetInputEventRouter()->RouteGestureEvent( | |
521 host_view_, &gesture, *event->latency()); | |
522 } else { | |
523 host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency()); | |
524 } | |
525 | |
526 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || | |
527 event->type() == ui::ET_GESTURE_SCROLL_UPDATE || | |
528 event->type() == ui::ET_GESTURE_SCROLL_END) { | |
529 RecordAction(base::UserMetricsAction("TouchscreenScroll")); | |
530 } else if (event->type() == ui::ET_SCROLL_FLING_START) { | |
531 RecordAction(base::UserMetricsAction("TouchscreenScrollFling")); | |
532 } | |
533 } | |
534 | |
535 // If a gesture is not processed by the webpage, then WebKit processes it | |
536 // (e.g. generates synthetic mouse events). | |
537 event->SetHandled(); | |
538 } | |
539 | |
540 bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent( | |
541 const ui::MouseEvent* event, | |
542 bool mouse_locked, | |
543 bool selection_popup) const { | |
544 if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) | |
545 return false; | |
546 | |
547 if (event->type() == ui::ET_MOUSE_EXITED) { | |
548 if (mouse_locked || selection_popup) | |
549 return false; | |
550 #if defined(OS_WIN) | |
551 // Don't forward the mouse leave message which is received when the context | |
552 // menu is displayed by the page. This confuses the page and causes state | |
553 // changes. | |
554 if (host_view_->IsShowingContextMenu()) | |
555 return false; | |
556 #endif | |
557 return true; | |
558 } | |
559 | |
560 #if defined(OS_WIN) | |
561 // Renderer cannot handle WM_XBUTTON or NC events. | |
562 switch (event->native_event().message) { | |
563 case WM_XBUTTONDOWN: | |
564 case WM_XBUTTONUP: | |
565 case WM_XBUTTONDBLCLK: | |
566 case WM_NCMOUSELEAVE: | |
567 case WM_NCMOUSEMOVE: | |
568 case WM_NCLBUTTONDOWN: | |
569 case WM_NCLBUTTONUP: | |
570 case WM_NCLBUTTONDBLCLK: | |
571 case WM_NCRBUTTONDOWN: | |
572 case WM_NCRBUTTONUP: | |
573 case WM_NCRBUTTONDBLCLK: | |
574 case WM_NCMBUTTONDOWN: | |
575 case WM_NCMBUTTONUP: | |
576 case WM_NCMBUTTONDBLCLK: | |
577 case WM_NCXBUTTONDOWN: | |
578 case WM_NCXBUTTONUP: | |
579 case WM_NCXBUTTONDBLCLK: | |
580 return false; | |
581 default: | |
582 break; | |
583 } | |
584 #elif defined(USE_X11) | |
585 // Renderer only supports standard mouse buttons, so ignore programmable | |
586 // buttons. | |
587 switch (event->type()) { | |
588 case ui::ET_MOUSE_PRESSED: | |
589 case ui::ET_MOUSE_RELEASED: { | |
590 const int kAllowedButtons = ui::EF_LEFT_MOUSE_BUTTON | | |
591 ui::EF_MIDDLE_MOUSE_BUTTON | | |
592 ui::EF_RIGHT_MOUSE_BUTTON; | |
593 return (event->flags() & kAllowedButtons) != 0; | |
594 } | |
595 default: | |
596 break; | |
597 } | |
598 #endif | |
599 return true; | |
600 } | |
601 | |
602 void RenderWidgetHostViewEventHandler::FinishImeCompositionSession() { | |
603 if (!host_view_->GetTextInputClient()->HasCompositionText()) | |
604 return; | |
605 | |
606 TextInputManager* text_input_manager = host_view_->GetTextInputManager(); | |
607 if (!!text_input_manager && !!text_input_manager->GetActiveWidget()) | |
608 text_input_manager->GetActiveWidget()->ImeFinishComposingText(false); | |
609 host_view_->ImeCancelComposition(); | |
610 } | |
611 | |
612 void RenderWidgetHostViewEventHandler::ForwardMouseEventToParent( | |
613 ui::MouseEvent* event) { | |
614 // Needed to propagate mouse event to |window_->parent()->delegate()|, but | |
615 // note that it might be something other than a WebContentsViewAura instance. | |
616 // TODO(pkotwicz): Find a better way of doing this. | |
617 // In fullscreen mode which is typically used by flash, don't forward | |
618 // the mouse events to the parent. The renderer and the plugin process | |
619 // handle these events. | |
620 if (host_view_->is_fullscreen()) | |
621 return; | |
622 | |
623 if (event->flags() & ui::EF_FROM_TOUCH) | |
624 return; | |
625 | |
626 if (!window_->parent() || !window_->parent()->delegate()) | |
627 return; | |
628 | |
629 // Take a copy of |event|, to avoid ConvertLocationToTarget mutating the | |
630 // event. | |
631 std::unique_ptr<ui::Event> event_copy = ui::Event::Clone(*event); | |
632 ui::MouseEvent* mouse_event = static_cast<ui::MouseEvent*>(event_copy.get()); | |
633 mouse_event->ConvertLocationToTarget(window_, window_->parent()); | |
634 window_->parent()->delegate()->OnMouseEvent(mouse_event); | |
635 if (mouse_event->handled()) | |
636 event->SetHandled(); | |
637 } | |
638 | |
639 RenderViewHostDelegateView* | |
640 RenderWidgetHostViewEventHandler::GetRenderViewHostDelegateView() { | |
641 // Use RenderViewHostDelegate to get to the WebContentsViewAura, which will | |
642 // actually show the disambiguation popup. | |
643 RenderViewHost* rvh = RenderViewHost::From(host_); | |
644 if (!rvh) | |
645 return nullptr; | |
646 | |
647 RenderViewHostDelegate* delegate = rvh->GetDelegate(); | |
648 if (!delegate) | |
649 return nullptr; | |
650 | |
651 return delegate->GetDelegateView(); | |
652 } | |
653 | |
654 void RenderWidgetHostViewEventHandler::HandleGestureForTouchSelection( | |
655 ui::GestureEvent* event) { | |
656 switch (event->type()) { | |
657 case ui::ET_GESTURE_LONG_PRESS: | |
658 if (delegate_->selection_controller()->WillHandleLongPressEvent( | |
659 event->time_stamp(), event->location_f())) { | |
660 event->SetHandled(); | |
661 } | |
662 break; | |
663 case ui::ET_GESTURE_TAP: | |
664 if (delegate_->selection_controller()->WillHandleTapEvent( | |
665 event->location_f(), event->details().tap_count())) { | |
666 event->SetHandled(); | |
667 } | |
668 break; | |
669 case ui::ET_GESTURE_SCROLL_BEGIN: | |
670 delegate_->selection_controller_client()->OnScrollStarted(); | |
671 break; | |
672 case ui::ET_GESTURE_SCROLL_END: | |
673 delegate_->selection_controller_client()->OnScrollCompleted(); | |
674 break; | |
675 #if defined(OS_WIN) | |
676 case ui::ET_GESTURE_LONG_TAP: { | |
677 if (!last_context_menu_params_) | |
678 break; | |
679 | |
680 std::unique_ptr<ContextMenuParams> context_menu_params = | |
681 std::move(last_context_menu_params_); | |
682 | |
683 // On Windows we want to display the context menu when the long press | |
684 // gesture is released. To achieve that, we switch the saved context | |
685 // menu params source type to MENU_SOURCE_TOUCH. This is to ensure that | |
686 // the RenderWidgetHostViewBase::OnShowContextMenu function which is | |
687 // called from the ShowContextMenu call below, does not treat it as | |
688 // a context menu request coming in from the long press gesture. | |
689 DCHECK(context_menu_params->source_type == ui::MENU_SOURCE_LONG_PRESS); | |
690 context_menu_params->source_type = ui::MENU_SOURCE_TOUCH; | |
691 | |
692 RenderViewHostDelegateView* delegate_view = | |
693 GetRenderViewHostDelegateView(); | |
sadrul
2016/10/13 16:27:41
Not a huge fan of this living in here. Can this be
jonross
2016/10/17 16:04:02
Done.
| |
694 if (delegate_view) | |
695 delegate_view->ShowContextMenu(delegate_->GetFocusedFrame(), | |
696 *context_menu_params); | |
697 | |
698 event->SetHandled(); | |
699 // WARNING: we may have been deleted during the call to ShowContextMenu(). | |
700 break; | |
701 } | |
702 #endif | |
703 default: | |
704 break; | |
705 } | |
706 } | |
707 | |
708 void RenderWidgetHostViewEventHandler::HandleMouseEventWhileLocked( | |
709 ui::MouseEvent* event) { | |
710 aura::client::CursorClient* cursor_client = | |
711 aura::client::GetCursorClient(window_->GetRootWindow()); | |
712 | |
713 DCHECK(!cursor_client || !cursor_client->IsCursorVisible()); | |
714 | |
715 if (event->type() == ui::ET_MOUSEWHEEL) { | |
716 blink::WebMouseWheelEvent mouse_wheel_event = | |
717 ui::MakeWebMouseWheelEvent(static_cast<ui::MouseWheelEvent&>(*event), | |
718 base::Bind(&GetScreenLocationFromEvent)); | |
719 if (mouse_wheel_event.deltaX != 0 || mouse_wheel_event.deltaY != 0) | |
720 host_->ForwardWheelEvent(mouse_wheel_event); | |
721 return; | |
722 } | |
723 | |
724 gfx::Point center(gfx::Rect(window_->bounds().size()).CenterPoint()); | |
725 | |
726 // If we receive non client mouse messages while we are in the locked state | |
727 // it probably means that the mouse left the borders of our window and | |
728 // needs to be moved back to the center. | |
729 if (event->flags() & ui::EF_IS_NON_CLIENT) { | |
730 synthetic_move_sent_ = true; | |
731 window_->MoveCursorTo(center); | |
sadrul
2016/10/13 16:27:41
Leave a TODO here that we would ideally not have t
jonross
2016/10/17 16:04:02
Done.
| |
732 return; | |
733 } | |
734 | |
735 blink::WebMouseEvent mouse_event = | |
736 ui::MakeWebMouseEvent(*event, base::Bind(&GetScreenLocationFromEvent)); | |
737 | |
738 bool is_move_to_center_event = (event->type() == ui::ET_MOUSE_MOVED || | |
739 event->type() == ui::ET_MOUSE_DRAGGED) && | |
740 mouse_event.x == center.x() && | |
741 mouse_event.y == center.y(); | |
742 | |
743 // For fractional scale factors, the conversion from pixels to dip and | |
744 // vice versa could result in off by 1 or 2 errors which hurts us because | |
745 // we want to avoid sending the artificial move to center event to the | |
746 // renderer. Sending the move to center to the renderer cause the cursor | |
747 // to bounce around the center of the screen leading to the lock operation | |
748 // not working correctly. | |
749 // Workaround is to treat a mouse move or drag event off by at most 2 px | |
750 // from the center as a move to center event. | |
751 if (synthetic_move_sent_ && | |
752 IsFractionalScaleFactor(host_view_->current_device_scale_factor())) { | |
753 if (event->type() == ui::ET_MOUSE_MOVED || | |
754 event->type() == ui::ET_MOUSE_DRAGGED) { | |
755 if ((abs(mouse_event.x - center.x()) <= 2) && | |
756 (abs(mouse_event.y - center.y()) <= 2)) { | |
757 is_move_to_center_event = true; | |
758 } | |
759 } | |
760 } | |
761 | |
762 ModifyEventMovementAndCoords(&mouse_event); | |
763 | |
764 bool should_not_forward = is_move_to_center_event && synthetic_move_sent_; | |
765 if (should_not_forward) { | |
766 synthetic_move_sent_ = false; | |
767 } else { | |
768 // Check if the mouse has reached the border and needs to be centered. | |
769 if (ShouldMoveToCenter()) { | |
770 synthetic_move_sent_ = true; | |
771 window_->MoveCursorTo(center); | |
772 } | |
773 bool is_selection_popup = NeedsInputGrab(popup_child_host_view_); | |
774 // Forward event to renderer. | |
775 if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) && | |
776 !(event->flags() & ui::EF_FROM_TOUCH)) { | |
777 host_->ForwardMouseEvent(mouse_event); | |
778 // Ensure that we get keyboard focus on mouse down as a plugin window | |
779 // may have grabbed keyboard focus. | |
780 if (event->type() == ui::ET_MOUSE_PRESSED) | |
781 SetKeyboardFocus(); | |
782 } | |
783 } | |
784 } | |
785 | |
786 void RenderWidgetHostViewEventHandler::ModifyEventMovementAndCoords( | |
787 blink::WebMouseEvent* event) { | |
788 // If the mouse has just entered, we must report zero movementX/Y. Hence we | |
789 // reset any global_mouse_position set previously. | |
790 if (event->type == blink::WebInputEvent::MouseEnter || | |
791 event->type == blink::WebInputEvent::MouseLeave) | |
792 global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
793 | |
794 // Movement is computed by taking the difference of the new cursor position | |
795 // and the previous. Under mouse lock the cursor will be warped back to the | |
796 // center so that we are not limited by clipping boundaries. | |
797 // We do not measure movement as the delta from cursor to center because | |
798 // we may receive more mouse movement events before our warp has taken | |
799 // effect. | |
800 event->movementX = event->globalX - global_mouse_position_.x(); | |
801 event->movementY = event->globalY - global_mouse_position_.y(); | |
802 | |
803 global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
804 | |
805 // Under mouse lock, coordinates of mouse are locked to what they were when | |
806 // mouse lock was entered. | |
807 if (mouse_locked_) { | |
808 event->x = unlocked_mouse_position_.x(); | |
809 event->y = unlocked_mouse_position_.y(); | |
810 event->windowX = unlocked_mouse_position_.x(); | |
811 event->windowY = unlocked_mouse_position_.y(); | |
812 event->globalX = unlocked_global_mouse_position_.x(); | |
813 event->globalY = unlocked_global_mouse_position_.y(); | |
814 } else { | |
815 unlocked_mouse_position_.SetPoint(event->x, event->y); | |
816 unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY); | |
817 } | |
818 } | |
819 | |
820 void RenderWidgetHostViewEventHandler::SetKeyboardFocus() { | |
821 #if defined(OS_WIN) | |
822 if (window_ && window_->delegate()->CanFocus()) { | |
823 aura::WindowTreeHost* host = window_->GetHost(); | |
824 if (host) { | |
825 gfx::AcceleratedWidget hwnd = host->GetAcceleratedWidget(); | |
826 if (!(::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE)) | |
827 ::SetFocus(hwnd); | |
828 } | |
829 } | |
830 #endif | |
831 // TODO(wjmaclean): can host_ ever be null? | |
832 if (host_ && set_focus_on_mouse_down_or_key_event_) { | |
833 set_focus_on_mouse_down_or_key_event_ = false; | |
834 host_->Focus(); | |
835 } | |
836 } | |
837 | |
838 bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter() { | |
839 gfx::Rect rect = window_->bounds(); | |
840 rect = delegate_->ConvertRectToScreen(rect); | |
841 int border_x = rect.width() * kMouseLockBorderPercentage / 100; | |
842 int border_y = rect.height() * kMouseLockBorderPercentage / 100; | |
843 | |
844 return global_mouse_position_.x() < rect.x() + border_x || | |
845 global_mouse_position_.x() > rect.right() - border_x || | |
846 global_mouse_position_.y() < rect.y() + border_y || | |
847 global_mouse_position_.y() > rect.bottom() - border_y; | |
848 } | |
849 | |
850 bool RenderWidgetHostViewEventHandler::ShouldRouteEvent( | |
851 const ui::Event* event) const { | |
852 // We should route an event in two cases: | |
853 // 1) Mouse events are routed only if cross-process frames are possible. | |
854 // 2) Touch events are always routed. In the absence of a BrowserPlugin | |
855 // we expect the routing to always send the event to this view. If | |
856 // one or more BrowserPlugins are present, then the event may be targeted | |
857 // to one of them, or this view. This allows GuestViews to have access to | |
858 // them while still forcing pinch-zoom to be handled by the top-level | |
859 // frame. TODO(wjmaclean): At present, this doesn't work for OOPIF, but | |
860 // it should be a simple extension to modify RenderWidgetHostViewChildFrame | |
861 // in a similar manner to RenderWidgetHostViewGuest. | |
862 bool result = host_->delegate() && host_->delegate()->GetInputEventRouter() && | |
863 !disable_input_event_router_for_testing_; | |
864 // ScrollEvents get transformed into MouseWheel events, and so are treated | |
865 // the same as mouse events for routing purposes. | |
866 if (event->IsMouseEvent() || event->type() == ui::ET_SCROLL) | |
867 result = result && SiteIsolationPolicy::AreCrossProcessFramesPossible(); | |
868 return result; | |
869 } | |
870 | |
871 void RenderWidgetHostViewEventHandler::ProcessMouseEvent( | |
872 const blink::WebMouseEvent& event, | |
873 const ui::LatencyInfo& latency) { | |
874 host_->ForwardMouseEventWithLatencyInfo(event, latency); | |
875 } | |
876 | |
877 void RenderWidgetHostViewEventHandler::ProcessMouseWheelEvent( | |
878 const blink::WebMouseWheelEvent& event, | |
879 const ui::LatencyInfo& latency) { | |
880 host_->ForwardWheelEventWithLatencyInfo(event, latency); | |
881 } | |
882 | |
883 void RenderWidgetHostViewEventHandler::ProcessTouchEvent( | |
884 const blink::WebTouchEvent& event, | |
885 const ui::LatencyInfo& latency) { | |
886 host_->ForwardTouchEventWithLatencyInfo(event, latency); | |
887 } | |
888 | |
889 } // namespace content | |
OLD | NEW |