Chromium Code Reviews| 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 |