Index: content/browser/renderer_host/render_widget_host_view_event_handler.cc |
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2adb005ff4b91c9d162c8d2e975830de56955b90 |
--- /dev/null |
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc |
@@ -0,0 +1,873 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/renderer_host/render_widget_host_view_event_handler.h" |
+ |
+#include "base/metrics/user_metrics_action.h" |
+#include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h" |
+#include "content/browser/renderer_host/overscroll_controller.h" |
+#include "content/browser/renderer_host/render_view_host_delegate.h" |
+#include "content/browser/renderer_host/render_view_host_delegate_view.h" |
+#include "content/browser/renderer_host/render_widget_host_impl.h" |
+#include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
+#include "content/browser/renderer_host/render_widget_host_view_base.h" |
+#include "content/browser/renderer_host/text_input_manager.h" |
+#include "content/common/content_switches_internal.h" |
+#include "content/common/site_isolation_policy.h" |
+#include "content/public/browser/render_view_host.h" |
+#include "content/public/browser/render_widget_host.h" |
+#include "content/public/browser/user_metrics.h" |
+#include "ui/aura/client/cursor_client.h" |
+#include "ui/aura/client/focus_client.h" |
+#include "ui/aura/client/screen_position_client.h" |
+#include "ui/aura/window.h" |
+#include "ui/aura/window_delegate.h" |
+#include "ui/base/ime/text_input_client.h" |
+#include "ui/events/blink/blink_event_util.h" |
+#include "ui/events/blink/web_input_event.h" |
+#include "ui/touch_selection/touch_selection_controller.h" |
+ |
+#if defined(OS_WIN) |
+#include "content/browser/frame_host/render_frame_host_impl.h" |
+#include "content/public/common/context_menu_params.h" |
+#include "ui/aura/window_tree_host.h" |
+#include "ui/display/screen.h" |
+#endif // defined(OS_WIN) |
+ |
+namespace { |
+ |
+// In mouse lock mode, we need to prevent the (invisible) cursor from hitting |
+// the border of the view, in order to get valid movement information. However, |
+// forcing the cursor back to the center of the view after each mouse move |
+// doesn't work well. It reduces the frequency of useful mouse move messages |
+// significantly. Therefore, we move the cursor to the center of the view only |
+// if it approaches the border. |kMouseLockBorderPercentage| specifies the width |
+// of the border area, in percentage of the corresponding dimension. |
+const int kMouseLockBorderPercentage = 15; |
+ |
+#if defined(OS_WIN) |
+// A callback function for EnumThreadWindows to enumerate and dismiss |
+// any owned popup windows. |
+BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) { |
+ const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg); |
+ |
+ if (::IsWindowVisible(window)) { |
+ const HWND owner = ::GetWindow(window, GW_OWNER); |
+ if (toplevel_hwnd == owner) { |
+ ::PostMessage(window, WM_CANCELMODE, 0, 0); |
+ } |
+ } |
+ |
+ return TRUE; |
+} |
+#endif // defined(OS_WIN) |
+ |
+gfx::Point GetScreenLocationFromEvent(const ui::LocatedEvent& event) { |
+ aura::Window* root = |
+ static_cast<aura::Window*>(event.target())->GetRootWindow(); |
+ aura::client::ScreenPositionClient* spc = |
+ aura::client::GetScreenPositionClient(root); |
+ if (!spc) |
+ return event.root_location(); |
+ |
+ gfx::Point screen_location(event.root_location()); |
+ spc->ConvertPointToScreen(root, &screen_location); |
+ return screen_location; |
+} |
+ |
+bool IsFractionalScaleFactor(float scale_factor) { |
+ return (scale_factor - static_cast<int>(scale_factor)) > 0; |
+} |
+ |
+// We don't mark these as handled so that they're sent back to the |
+// DefWindowProc so it can generate WM_APPCOMMAND as necessary. |
+bool IsXButtonUpEvent(const ui::MouseEvent* event) { |
+#if defined(OS_WIN) |
+ switch (event->native_event().message) { |
+ case WM_XBUTTONUP: |
+ case WM_NCXBUTTONUP: |
+ return true; |
+ } |
+#endif |
+ return false; |
+} |
+ |
+// Reset unchanged touch points to StateStationary for touchmove and |
+// touchcancel. |
+void MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent* event, |
+ int changed_touch_id) { |
+ if (event->type == blink::WebInputEvent::TouchMove || |
+ event->type == blink::WebInputEvent::TouchCancel) { |
+ for (size_t i = 0; i < event->touchesLength; ++i) { |
+ if (event->touches[i].id != changed_touch_id) |
+ event->touches[i].state = blink::WebTouchPoint::StateStationary; |
+ } |
+ } |
+} |
+ |
+bool NeedsInputGrab(content::RenderWidgetHostViewBase* view) { |
+ if (!view) |
+ return false; |
+ return view->GetPopupType() == blink::WebPopupTypePage; |
+} |
+ |
+} // namespace |
+ |
+namespace content { |
+ |
+RenderWidgetHostViewEventHandler::Delegate::Delegate() |
+ : selection_controller_client_(nullptr), |
+ selection_controller_(nullptr), |
+ overscroll_controller_(nullptr) {} |
+ |
+RenderWidgetHostViewEventHandler::Delegate::~Delegate() {} |
+ |
+RenderWidgetHostViewEventHandler::RenderWidgetHostViewEventHandler( |
+ RenderWidgetHostImpl* host, |
+ RenderWidgetHostViewBase* host_view, |
+ Delegate* delegate) |
+ : accept_return_character_(false), |
+ disable_input_event_router_for_testing_(false), |
+ mouse_locked_(false), |
+ pinch_zoom_enabled_(content::IsPinchToZoomEnabled()), |
+ set_focus_on_mouse_down_or_key_event_(false), |
+ synthetic_move_sent_(false), |
+ host_(RenderWidgetHostImpl::From(host)), |
+ host_view_(host_view), |
+ popup_child_host_view_(nullptr), |
+ popup_child_event_handler_(nullptr), |
+ delegate_(delegate), |
+ window_(nullptr) {} |
+ |
+RenderWidgetHostViewEventHandler::~RenderWidgetHostViewEventHandler() {} |
+ |
+void RenderWidgetHostViewEventHandler::SetPopupChild( |
+ RenderWidgetHostViewBase* popup_child_host_view, |
+ ui::EventHandler* popup_child_event_handler) { |
+ popup_child_host_view_ = popup_child_host_view; |
+ popup_child_event_handler_ = popup_child_event_handler; |
+} |
+ |
+void RenderWidgetHostViewEventHandler::TrackHost( |
+ aura::Window* reference_window) { |
+ if (!reference_window) |
+ return; |
+ DCHECK(!host_tracker_); |
+ host_tracker_.reset(new aura::WindowTracker); |
+ host_tracker_->Add(reference_window); |
+} |
+ |
+#if defined(OS_WIN) |
+void RenderWidgetHostViewEventHandler::SetContextMenuParams( |
+ const ContextMenuParams& params) { |
+ last_context_menu_params_.reset(); |
+ if (params.source_type == ui::MENU_SOURCE_LONG_PRESS) { |
+ last_context_menu_params_.reset(new ContextMenuParams); |
+ *last_context_menu_params_ = params; |
+ } |
+} |
+ |
+void RenderWidgetHostViewEventHandler::UpdateMouseLockRegion() { |
+ RECT window_rect = |
+ display::Screen::GetScreen() |
+ ->DIPToScreenRectInWindow(window_, window_->GetBoundsInScreen()) |
+ .ToRECT(); |
+ ::ClipCursor(&window_rect); |
+} |
+#endif |
+ |
+bool RenderWidgetHostViewEventHandler::LockMouse() { |
+ aura::Window* root_window = window_->GetRootWindow(); |
+ if (!root_window) |
+ return false; |
+ |
+ if (mouse_locked_) |
+ return true; |
+ |
+ mouse_locked_ = true; |
+#if !defined(OS_WIN) |
+ window_->SetCapture(); |
+#else |
+ UpdateMouseLockRegion(); |
+#endif |
+ aura::client::CursorClient* cursor_client = |
+ aura::client::GetCursorClient(root_window); |
+ if (cursor_client) { |
+ cursor_client->HideCursor(); |
+ cursor_client->LockCursor(); |
+ } |
+ |
+ if (ShouldMoveToCenter()) { |
+ synthetic_move_sent_ = true; |
+ window_->MoveCursorTo(gfx::Rect(window_->bounds().size()).CenterPoint()); |
+ } |
+ delegate_->SetTooltipsEnabled(false); |
+ return true; |
+} |
+ |
+void RenderWidgetHostViewEventHandler::UnlockMouse() { |
+ delegate_->SetTooltipsEnabled(true); |
+ |
+ aura::Window* root_window = window_->GetRootWindow(); |
+ if (!mouse_locked_ || !root_window) |
+ return; |
+ |
+ mouse_locked_ = false; |
+ |
+ if (window_->HasCapture()) |
+ window_->ReleaseCapture(); |
+ |
+#if defined(OS_WIN) |
+ ::ClipCursor(NULL); |
+#endif |
+ |
+ // Ensure that the global mouse position is updated here to its original |
+ // value. If we don't do this then the synthesized mouse move which is posted |
+ // after the cursor is moved ends up getting a large movement delta which is |
+ // not what sites expect. The delta is computed in the |
+ // ModifyEventMovementAndCoords function. |
+ global_mouse_position_ = unlocked_global_mouse_position_; |
+ window_->MoveCursorTo(unlocked_mouse_position_); |
+ |
+ aura::client::CursorClient* cursor_client = |
+ aura::client::GetCursorClient(root_window); |
+ if (cursor_client) { |
+ cursor_client->UnlockCursor(); |
+ cursor_client->ShowCursor(); |
+ } |
+ host_->LostMouseLock(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::OnKeyEvent(ui::KeyEvent* event) { |
+ TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnKeyEvent"); |
+ |
+ if (NeedsInputGrab(popup_child_host_view_)) { |
+ popup_child_event_handler_->OnKeyEvent(event); |
+ if (event->handled()) |
+ return; |
+ } |
+ |
+ // We need to handle the Escape key for Pepper Flash. |
+ if (host_view_->is_fullscreen() && event->key_code() == ui::VKEY_ESCAPE) { |
+ // Focus the window we were created from. |
+ if (host_tracker_.get() && !host_tracker_->windows().empty()) { |
+ aura::Window* host = *(host_tracker_->windows().begin()); |
+ aura::client::FocusClient* client = aura::client::GetFocusClient(host); |
+ if (client) { |
+ // Calling host->Focus() may delete |this|. We create a local observer |
+ // for that. In that case we exit without further access to any members. |
+ auto local_tracker = std::move(host_tracker_); |
+ local_tracker->Add(window_); |
+ host->Focus(); |
+ if (!local_tracker->Contains(window_)) { |
+ event->SetHandled(); |
+ return; |
+ } |
+ } |
+ } |
+ delegate_->Shutdown(); |
+ host_tracker_.reset(); |
+ } else { |
+ if (event->key_code() == ui::VKEY_RETURN) { |
+ // Do not forward return key release events if no press event was handled. |
+ if (event->type() == ui::ET_KEY_RELEASED && !accept_return_character_) |
+ return; |
+ // Accept return key character events between press and release events. |
+ accept_return_character_ = event->type() == ui::ET_KEY_PRESSED; |
+ } |
+ |
+ // Call SetKeyboardFocus() for not only ET_KEY_PRESSED but also |
+ // ET_KEY_RELEASED. If a user closed the hotdog menu with ESC key press, |
+ // we need to notify focus to Blink on ET_KEY_RELEASED for ESC key. |
+ SetKeyboardFocus(); |
+ // We don't have to communicate with an input method here. |
+ NativeWebKeyboardEvent webkit_event(*event); |
+ delegate_->ForwardKeyboardEvent(webkit_event); |
+ } |
+ event->SetHandled(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) { |
+ TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnMouseEvent"); |
+ ForwardMouseEventToParent(event); |
+ // TODO(mgiuca): Return if event->handled() returns true. This currently |
+ // breaks drop-down lists which means something is incorrectly setting |
+ // event->handled to true (http://crbug.com/577983). |
+ |
+ if (mouse_locked_) { |
+ HandleMouseEventWhileLocked(event); |
+ return; |
+ } |
+ |
+ // As the overscroll is handled during scroll events from the trackpad, the |
+ // RWHVA window is transformed by the overscroll controller. This transform |
+ // triggers a synthetic mouse-move event to be generated (by the aura |
+ // RootWindow). But this event interferes with the overscroll gesture. So, |
+ // ignore such synthetic mouse-move events if an overscroll gesture is in |
+ // progress. |
+ OverscrollController* overscroll_controller = |
+ delegate_->overscroll_controller(); |
+ if (overscroll_controller && |
+ overscroll_controller->overscroll_mode() != OVERSCROLL_NONE && |
+ event->flags() & ui::EF_IS_SYNTHESIZED && |
+ (event->type() == ui::ET_MOUSE_ENTERED || |
+ event->type() == ui::ET_MOUSE_EXITED || |
+ event->type() == ui::ET_MOUSE_MOVED)) { |
+ event->StopPropagation(); |
+ return; |
+ } |
+ |
+ if (event->type() == ui::ET_MOUSEWHEEL) { |
+#if defined(OS_WIN) |
+ // We get mouse wheel/scroll messages even if we are not in the foreground. |
+ // So here we check if we have any owned popup windows in the foreground and |
+ // dismiss them. |
+ aura::WindowTreeHost* host = window_->GetHost(); |
+ if (host) { |
+ HWND parent = host->GetAcceleratedWidget(); |
+ HWND toplevel_hwnd = ::GetAncestor(parent, GA_ROOT); |
+ EnumThreadWindows(GetCurrentThreadId(), DismissOwnedPopups, |
+ reinterpret_cast<LPARAM>(toplevel_hwnd)); |
+ } |
+#endif |
+ blink::WebMouseWheelEvent mouse_wheel_event = |
+ ui::MakeWebMouseWheelEvent(static_cast<ui::MouseWheelEvent&>(*event), |
+ base::Bind(&GetScreenLocationFromEvent)); |
+ if (mouse_wheel_event.deltaX != 0 || mouse_wheel_event.deltaY != 0) { |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent( |
+ host_view_, &mouse_wheel_event, *event->latency()); |
+ } else { |
+ ProcessMouseWheelEvent(mouse_wheel_event, *event->latency()); |
+ } |
+ } |
+ } else { |
+ bool is_selection_popup = NeedsInputGrab(popup_child_host_view_); |
+ if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) && |
+ !(event->flags() & ui::EF_FROM_TOUCH)) { |
+ // Confirm existing composition text on mouse press, to make sure |
+ // the input caret won't be moved with an ongoing composition text. |
+ if (event->type() == ui::ET_MOUSE_PRESSED) |
+ FinishImeCompositionSession(); |
+ |
+ blink::WebMouseEvent mouse_event = ui::MakeWebMouseEvent( |
+ *event, base::Bind(&GetScreenLocationFromEvent)); |
+ ModifyEventMovementAndCoords(*event, &mouse_event); |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteMouseEvent( |
+ host_view_, &mouse_event, *event->latency()); |
+ } else { |
+ ProcessMouseEvent(mouse_event, *event->latency()); |
+ } |
+ |
+ // Ensure that we get keyboard focus on mouse down as a plugin window may |
+ // have grabbed keyboard focus. |
+ if (event->type() == ui::ET_MOUSE_PRESSED) |
+ SetKeyboardFocus(); |
+ } |
+ } |
+ |
+ switch (event->type()) { |
+ case ui::ET_MOUSE_PRESSED: |
+ window_->SetCapture(); |
+ break; |
+ case ui::ET_MOUSE_RELEASED: |
+ if (!delegate_->NeedsMouseCapture()) |
+ window_->ReleaseCapture(); |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ if (!IsXButtonUpEvent(event)) |
+ event->SetHandled(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::OnScrollEvent(ui::ScrollEvent* event) { |
+ TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnScrollEvent"); |
+ |
+ if (event->type() == ui::ET_SCROLL) { |
+#if !defined(OS_WIN) |
+ // TODO(ananta) |
+ // Investigate if this is true for Windows 8 Metro ASH as well. |
+ if (event->finger_count() != 2) |
+ return; |
+#endif |
+ blink::WebGestureEvent gesture_event = ui::MakeWebGestureEventFlingCancel(); |
+ // Coordinates need to be transferred to the fling cancel gesture only |
+ // for Surface-targeting to ensure that it is targeted to the correct |
+ // RenderWidgetHost. |
+ gesture_event.x = event->x(); |
+ gesture_event.y = event->y(); |
+ blink::WebMouseWheelEvent mouse_wheel_event = ui::MakeWebMouseWheelEvent( |
+ *event, base::Bind(&GetScreenLocationFromEvent)); |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent( |
+ host_view_, &gesture_event, |
+ ui::LatencyInfo(ui::SourceEventType::WHEEL)); |
+ host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent( |
+ host_view_, &mouse_wheel_event, *event->latency()); |
+ } else { |
+ host_->ForwardGestureEvent(gesture_event); |
+ host_->ForwardWheelEventWithLatencyInfo(mouse_wheel_event, |
+ *event->latency()); |
+ } |
+ RecordAction(base::UserMetricsAction("TrackpadScroll")); |
+ } else if (event->type() == ui::ET_SCROLL_FLING_START || |
+ event->type() == ui::ET_SCROLL_FLING_CANCEL) { |
+ blink::WebGestureEvent gesture_event = ui::MakeWebGestureEvent( |
+ *event, base::Bind(&GetScreenLocationFromEvent)); |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent( |
+ host_view_, &gesture_event, |
+ ui::LatencyInfo(ui::SourceEventType::WHEEL)); |
+ } else { |
+ host_->ForwardGestureEvent(gesture_event); |
+ } |
+ if (event->type() == ui::ET_SCROLL_FLING_START) |
+ RecordAction(base::UserMetricsAction("TrackpadScrollFling")); |
+ } |
+ |
+ event->SetHandled(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::OnTouchEvent(ui::TouchEvent* event) { |
+ TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnTouchEvent"); |
+ |
+ bool had_no_pointer = !pointer_state_.GetPointerCount(); |
+ |
+ // Update the touch event first. |
+ if (!pointer_state_.OnTouch(*event)) { |
+ event->StopPropagation(); |
+ return; |
+ } |
+ |
+ blink::WebTouchEvent touch_event; |
+ bool handled = |
+ delegate_->selection_controller()->WillHandleTouchEvent(pointer_state_); |
+ if (handled) { |
+ event->SetHandled(); |
+ } else { |
+ touch_event = ui::CreateWebTouchEventFromMotionEvent( |
+ pointer_state_, event->may_cause_scrolling()); |
+ } |
+ pointer_state_.CleanupRemovedTouchPoints(*event); |
+ |
+ if (handled) |
+ return; |
+ |
+ if (had_no_pointer) |
+ delegate_->selection_controller_client()->OnTouchDown(); |
+ if (!pointer_state_.GetPointerCount()) |
+ delegate_->selection_controller_client()->OnTouchUp(); |
+ |
+ // It is important to always mark events as being handled asynchronously when |
+ // they are forwarded. This ensures that the current event does not get |
+ // processed by the gesture recognizer before events currently awaiting |
+ // dispatch in the touch queue. |
+ event->DisableSynchronousHandling(); |
+ |
+ // Set unchanged touch point to StateStationary for touchmove and |
+ // touchcancel to make sure only send one ack per WebTouchEvent. |
+ MarkUnchangedTouchPointsAsStationary(&touch_event, event->touch_id()); |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteTouchEvent( |
+ host_view_, &touch_event, *event->latency()); |
+ } else { |
+ ProcessTouchEvent(touch_event, *event->latency()); |
+ } |
+} |
+ |
+void RenderWidgetHostViewEventHandler::OnGestureEvent(ui::GestureEvent* event) { |
+ TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnGestureEvent"); |
+ |
+ if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN || |
+ event->type() == ui::ET_GESTURE_PINCH_UPDATE || |
+ event->type() == ui::ET_GESTURE_PINCH_END) && |
+ !pinch_zoom_enabled_) { |
+ event->SetHandled(); |
+ return; |
+ } |
+ |
+ HandleGestureForTouchSelection(event); |
+ if (event->handled()) |
+ return; |
+ |
+ // Confirm existing composition text on TAP gesture, to make sure the input |
+ // caret won't be moved with an ongoing composition text. |
+ if (event->type() == ui::ET_GESTURE_TAP) |
+ FinishImeCompositionSession(); |
+ |
+ blink::WebGestureEvent gesture = |
+ ui::MakeWebGestureEvent(*event, base::Bind(&GetScreenLocationFromEvent)); |
+ if (event->type() == ui::ET_GESTURE_TAP_DOWN) { |
+ // Webkit does not stop a fling-scroll on tap-down. So explicitly send an |
+ // event to stop any in-progress flings. |
+ blink::WebGestureEvent fling_cancel = gesture; |
+ fling_cancel.type = blink::WebInputEvent::GestureFlingCancel; |
+ fling_cancel.sourceDevice = blink::WebGestureDeviceTouchscreen; |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent( |
+ host_view_, &fling_cancel, |
+ ui::LatencyInfo(ui::SourceEventType::TOUCH)); |
+ } else { |
+ host_->ForwardGestureEvent(fling_cancel); |
+ } |
+ } |
+ |
+ if (gesture.type != blink::WebInputEvent::Undefined) { |
+ if (ShouldRouteEvent(event)) { |
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent( |
+ host_view_, &gesture, *event->latency()); |
+ } else { |
+ host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency()); |
+ } |
+ |
+ if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || |
+ event->type() == ui::ET_GESTURE_SCROLL_UPDATE || |
+ event->type() == ui::ET_GESTURE_SCROLL_END) { |
+ RecordAction(base::UserMetricsAction("TouchscreenScroll")); |
+ } else if (event->type() == ui::ET_SCROLL_FLING_START) { |
+ RecordAction(base::UserMetricsAction("TouchscreenScrollFling")); |
+ } |
+ } |
+ |
+ // If a gesture is not processed by the webpage, then WebKit processes it |
+ // (e.g. generates synthetic mouse events). |
+ event->SetHandled(); |
+} |
+ |
+bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent( |
+ const ui::MouseEvent* event, |
+ bool mouse_locked, |
+ bool selection_popup) const { |
+ if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) |
+ return false; |
+ |
+ if (event->type() == ui::ET_MOUSE_EXITED) { |
+ if (mouse_locked || selection_popup) |
+ return false; |
+#if defined(OS_WIN) |
+ // Don't forward the mouse leave message which is received when the context |
+ // menu is displayed by the page. This confuses the page and causes state |
+ // changes. |
+ if (host_view_->IsShowingContextMenu()) |
+ return false; |
+#endif |
+ return true; |
+ } |
+ |
+#if defined(OS_WIN) |
+ // Renderer cannot handle WM_XBUTTON or NC events. |
+ switch (event->native_event().message) { |
+ case WM_XBUTTONDOWN: |
+ case WM_XBUTTONUP: |
+ case WM_XBUTTONDBLCLK: |
+ case WM_NCMOUSELEAVE: |
+ case WM_NCMOUSEMOVE: |
+ case WM_NCLBUTTONDOWN: |
+ case WM_NCLBUTTONUP: |
+ case WM_NCLBUTTONDBLCLK: |
+ case WM_NCRBUTTONDOWN: |
+ case WM_NCRBUTTONUP: |
+ case WM_NCRBUTTONDBLCLK: |
+ case WM_NCMBUTTONDOWN: |
+ case WM_NCMBUTTONUP: |
+ case WM_NCMBUTTONDBLCLK: |
+ case WM_NCXBUTTONDOWN: |
+ case WM_NCXBUTTONUP: |
+ case WM_NCXBUTTONDBLCLK: |
+ return false; |
+ default: |
+ break; |
+ } |
+#elif defined(USE_X11) |
+ // Renderer only supports standard mouse buttons, so ignore programmable |
+ // buttons. |
+ switch (event->type()) { |
+ case ui::ET_MOUSE_PRESSED: |
+ case ui::ET_MOUSE_RELEASED: { |
+ const int kAllowedButtons = ui::EF_LEFT_MOUSE_BUTTON | |
+ ui::EF_MIDDLE_MOUSE_BUTTON | |
+ ui::EF_RIGHT_MOUSE_BUTTON; |
+ return (event->flags() & kAllowedButtons) != 0; |
+ } |
+ default: |
+ break; |
+ } |
+#endif |
+ return true; |
+} |
+ |
+void RenderWidgetHostViewEventHandler::FinishImeCompositionSession() { |
+ if (!host_view_->GetTextInputClient()->HasCompositionText()) |
+ return; |
+ |
+ TextInputManager* text_input_manager = host_view_->GetTextInputManager(); |
+ if (!!text_input_manager && !!text_input_manager->GetActiveWidget()) |
+ text_input_manager->GetActiveWidget()->ImeFinishComposingText(false); |
+ host_view_->ImeCancelComposition(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::ForwardMouseEventToParent( |
+ ui::MouseEvent* event) { |
+ // Needed to propagate mouse event to |window_->parent()->delegate()|, but |
+ // note that it might be something other than a WebContentsViewAura instance. |
+ // TODO(pkotwicz): Find a better way of doing this. |
+ // In fullscreen mode which is typically used by flash, don't forward |
+ // the mouse events to the parent. The renderer and the plugin process |
+ // handle these events. |
+ if (host_view_->is_fullscreen()) |
+ return; |
+ |
+ if (event->flags() & ui::EF_FROM_TOUCH) |
+ return; |
+ |
+ if (!window_->parent() || !window_->parent()->delegate()) |
+ return; |
+ |
+ // Take a copy of |event|, to avoid ConvertLocationToTarget mutating the |
+ // event. |
+ std::unique_ptr<ui::Event> event_copy = ui::Event::Clone(*event); |
+ ui::MouseEvent* mouse_event = static_cast<ui::MouseEvent*>(event_copy.get()); |
+ mouse_event->ConvertLocationToTarget(window_, window_->parent()); |
+ window_->parent()->delegate()->OnMouseEvent(mouse_event); |
+ if (mouse_event->handled()) |
+ event->SetHandled(); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::HandleGestureForTouchSelection( |
+ ui::GestureEvent* event) { |
+ switch (event->type()) { |
+ case ui::ET_GESTURE_LONG_PRESS: |
+ if (delegate_->selection_controller()->WillHandleLongPressEvent( |
+ event->time_stamp(), event->location_f())) { |
+ event->SetHandled(); |
+ } |
+ break; |
+ case ui::ET_GESTURE_TAP: |
+ if (delegate_->selection_controller()->WillHandleTapEvent( |
+ event->location_f(), event->details().tap_count())) { |
+ event->SetHandled(); |
+ } |
+ break; |
+ case ui::ET_GESTURE_SCROLL_BEGIN: |
+ delegate_->selection_controller_client()->OnScrollStarted(); |
+ break; |
+ case ui::ET_GESTURE_SCROLL_END: |
+ delegate_->selection_controller_client()->OnScrollCompleted(); |
+ break; |
+#if defined(OS_WIN) |
+ case ui::ET_GESTURE_LONG_TAP: { |
+ if (!last_context_menu_params_) |
+ break; |
+ |
+ std::unique_ptr<ContextMenuParams> context_menu_params = |
+ std::move(last_context_menu_params_); |
+ |
+ // On Windows we want to display the context menu when the long press |
+ // gesture is released. To achieve that, we switch the saved context |
+ // menu params source type to MENU_SOURCE_TOUCH. This is to ensure that |
+ // the RenderWidgetHostViewBase::OnShowContextMenu function which is |
+ // called from the ShowContextMenu call below, does not treat it as |
+ // a context menu request coming in from the long press gesture. |
+ DCHECK(context_menu_params->source_type == ui::MENU_SOURCE_LONG_PRESS); |
+ context_menu_params->source_type = ui::MENU_SOURCE_TOUCH; |
+ |
+ delegate_->ShowContextMenu(*context_menu_params); |
+ event->SetHandled(); |
+ // WARNING: we may have been deleted during the call to ShowContextMenu(). |
+ break; |
+ } |
+#endif |
+ default: |
+ break; |
+ } |
+} |
+ |
+void RenderWidgetHostViewEventHandler::HandleMouseEventWhileLocked( |
+ ui::MouseEvent* event) { |
+ aura::client::CursorClient* cursor_client = |
+ aura::client::GetCursorClient(window_->GetRootWindow()); |
+ |
+ DCHECK(!cursor_client || !cursor_client->IsCursorVisible()); |
+ |
+ if (event->type() == ui::ET_MOUSEWHEEL) { |
+ blink::WebMouseWheelEvent mouse_wheel_event = |
+ ui::MakeWebMouseWheelEvent(static_cast<ui::MouseWheelEvent&>(*event), |
+ base::Bind(&GetScreenLocationFromEvent)); |
+ if (mouse_wheel_event.deltaX != 0 || mouse_wheel_event.deltaY != 0) |
+ host_->ForwardWheelEvent(mouse_wheel_event); |
+ return; |
+ } |
+ |
+ gfx::Point center(gfx::Rect(window_->bounds().size()).CenterPoint()); |
+ |
+ // If we receive non client mouse messages while we are in the locked state |
+ // it probably means that the mouse left the borders of our window and |
+ // needs to be moved back to the center. |
+ if (event->flags() & ui::EF_IS_NON_CLIENT) { |
+ // TODO(jonross): ideally this would not be done for mus (crbug.com/621412) |
+ synthetic_move_sent_ = true; |
+ window_->MoveCursorTo(center); |
+ return; |
+ } |
+ |
+ blink::WebMouseEvent mouse_event = |
+ ui::MakeWebMouseEvent(*event, base::Bind(&GetScreenLocationFromEvent)); |
+ |
+ bool is_move_to_center_event = (event->type() == ui::ET_MOUSE_MOVED || |
+ event->type() == ui::ET_MOUSE_DRAGGED) && |
+ mouse_event.x == center.x() && |
+ mouse_event.y == center.y(); |
+ |
+ // For fractional scale factors, the conversion from pixels to dip and |
+ // vice versa could result in off by 1 or 2 errors which hurts us because |
+ // we want to avoid sending the artificial move to center event to the |
+ // renderer. Sending the move to center to the renderer cause the cursor |
+ // to bounce around the center of the screen leading to the lock operation |
+ // not working correctly. |
+ // Workaround is to treat a mouse move or drag event off by at most 2 px |
+ // from the center as a move to center event. |
+ if (synthetic_move_sent_ && |
+ IsFractionalScaleFactor(host_view_->current_device_scale_factor())) { |
+ if (event->type() == ui::ET_MOUSE_MOVED || |
+ event->type() == ui::ET_MOUSE_DRAGGED) { |
+ if ((abs(mouse_event.x - center.x()) <= 2) && |
+ (abs(mouse_event.y - center.y()) <= 2)) { |
+ is_move_to_center_event = true; |
+ } |
+ } |
+ } |
+ |
+ ModifyEventMovementAndCoords(*event, &mouse_event); |
+ |
+ bool should_not_forward = is_move_to_center_event && synthetic_move_sent_; |
+ if (should_not_forward) { |
+ synthetic_move_sent_ = false; |
+ } else { |
+ // Check if the mouse has reached the border and needs to be centered. |
+ if (ShouldMoveToCenter()) { |
+ synthetic_move_sent_ = true; |
+ window_->MoveCursorTo(center); |
+ } |
+ bool is_selection_popup = NeedsInputGrab(popup_child_host_view_); |
+ // Forward event to renderer. |
+ if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) && |
+ !(event->flags() & ui::EF_FROM_TOUCH)) { |
+ host_->ForwardMouseEvent(mouse_event); |
+ // Ensure that we get keyboard focus on mouse down as a plugin window |
+ // may have grabbed keyboard focus. |
+ if (event->type() == ui::ET_MOUSE_PRESSED) |
+ SetKeyboardFocus(); |
+ } |
+ } |
+} |
+ |
+void RenderWidgetHostViewEventHandler::ModifyEventMovementAndCoords( |
+ const ui::MouseEvent& ui_mouse_event, |
+ blink::WebMouseEvent* event) { |
+ // If the mouse has just entered, we must report zero movementX/Y. Hence we |
+ // reset any global_mouse_position set previously. |
+ if (ui_mouse_event.type() == ui::ET_MOUSE_ENTERED || |
+ ui_mouse_event.type() == ui::ET_MOUSE_EXITED) { |
+ global_mouse_position_.SetPoint(event->globalX, event->globalY); |
+ } |
+ |
+ // Movement is computed by taking the difference of the new cursor position |
+ // and the previous. Under mouse lock the cursor will be warped back to the |
+ // center so that we are not limited by clipping boundaries. |
+ // We do not measure movement as the delta from cursor to center because |
+ // we may receive more mouse movement events before our warp has taken |
+ // effect. |
+ event->movementX = event->globalX - global_mouse_position_.x(); |
+ event->movementY = event->globalY - global_mouse_position_.y(); |
+ |
+ global_mouse_position_.SetPoint(event->globalX, event->globalY); |
+ |
+ // Under mouse lock, coordinates of mouse are locked to what they were when |
+ // mouse lock was entered. |
+ if (mouse_locked_) { |
+ event->x = unlocked_mouse_position_.x(); |
+ event->y = unlocked_mouse_position_.y(); |
+ event->windowX = unlocked_mouse_position_.x(); |
+ event->windowY = unlocked_mouse_position_.y(); |
+ event->globalX = unlocked_global_mouse_position_.x(); |
+ event->globalY = unlocked_global_mouse_position_.y(); |
+ } else { |
+ unlocked_mouse_position_.SetPoint(event->x, event->y); |
+ unlocked_global_mouse_position_.SetPoint(event->globalX, event->globalY); |
+ } |
+} |
+ |
+void RenderWidgetHostViewEventHandler::SetKeyboardFocus() { |
+#if defined(OS_WIN) |
+ if (window_ && window_->delegate()->CanFocus()) { |
+ aura::WindowTreeHost* host = window_->GetHost(); |
+ if (host) { |
+ gfx::AcceleratedWidget hwnd = host->GetAcceleratedWidget(); |
+ if (!(::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE)) |
+ ::SetFocus(hwnd); |
+ } |
+ } |
+#endif |
+ // TODO(wjmaclean): can host_ ever be null? |
+ if (host_ && set_focus_on_mouse_down_or_key_event_) { |
+ set_focus_on_mouse_down_or_key_event_ = false; |
+ host_->Focus(); |
+ } |
+} |
+ |
+bool RenderWidgetHostViewEventHandler::ShouldMoveToCenter() { |
+ gfx::Rect rect = window_->bounds(); |
+ rect = delegate_->ConvertRectToScreen(rect); |
+ int border_x = rect.width() * kMouseLockBorderPercentage / 100; |
+ int border_y = rect.height() * kMouseLockBorderPercentage / 100; |
+ |
+ return global_mouse_position_.x() < rect.x() + border_x || |
+ global_mouse_position_.x() > rect.right() - border_x || |
+ global_mouse_position_.y() < rect.y() + border_y || |
+ global_mouse_position_.y() > rect.bottom() - border_y; |
+} |
+ |
+bool RenderWidgetHostViewEventHandler::ShouldRouteEvent( |
+ const ui::Event* event) const { |
+ // We should route an event in two cases: |
+ // 1) Mouse events are routed only if cross-process frames are possible. |
+ // 2) Touch events are always routed. In the absence of a BrowserPlugin |
+ // we expect the routing to always send the event to this view. If |
+ // one or more BrowserPlugins are present, then the event may be targeted |
+ // to one of them, or this view. This allows GuestViews to have access to |
+ // them while still forcing pinch-zoom to be handled by the top-level |
+ // frame. TODO(wjmaclean): At present, this doesn't work for OOPIF, but |
+ // it should be a simple extension to modify RenderWidgetHostViewChildFrame |
+ // in a similar manner to RenderWidgetHostViewGuest. |
+ bool result = host_->delegate() && host_->delegate()->GetInputEventRouter() && |
+ !disable_input_event_router_for_testing_; |
+ // ScrollEvents get transformed into MouseWheel events, and so are treated |
+ // the same as mouse events for routing purposes. |
+ if (event->IsMouseEvent() || event->type() == ui::ET_SCROLL) |
+ result = result && SiteIsolationPolicy::AreCrossProcessFramesPossible(); |
+ return result; |
+} |
+ |
+void RenderWidgetHostViewEventHandler::ProcessMouseEvent( |
+ const blink::WebMouseEvent& event, |
+ const ui::LatencyInfo& latency) { |
+ host_->ForwardMouseEventWithLatencyInfo(event, latency); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::ProcessMouseWheelEvent( |
+ const blink::WebMouseWheelEvent& event, |
+ const ui::LatencyInfo& latency) { |
+ host_->ForwardWheelEventWithLatencyInfo(event, latency); |
+} |
+ |
+void RenderWidgetHostViewEventHandler::ProcessTouchEvent( |
+ const blink::WebTouchEvent& event, |
+ const ui::LatencyInfo& latency) { |
+ host_->ForwardTouchEventWithLatencyInfo(event, latency); |
+} |
+ |
+} // namespace content |