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