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