Chromium Code Reviews| Index: content/browser/renderer_host/render_widget_host_input_event_router.cc |
| diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc |
| index 3710a90df7f9d578833047c019537275a9ee9b4b..bd6e588018495a015ffd9b83122dd740d2c8d220 100644 |
| --- a/content/browser/renderer_host/render_widget_host_input_event_router.cc |
| +++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc |
| @@ -4,10 +4,14 @@ |
| #include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
| +#include <vector> |
| + |
| #include "base/metrics/histogram_macros.h" |
| + |
| #include "cc/quads/surface_draw_quad.h" |
| #include "cc/surfaces/surface_id_allocator.h" |
| #include "cc/surfaces/surface_manager.h" |
| +#include "content/browser/frame_host/render_widget_host_view_child_frame.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| #include "content/common/frame_messages.h" |
| @@ -64,6 +68,11 @@ void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed( |
| bubbling_gesture_scroll_target_.target = nullptr; |
| first_bubbling_scroll_target_.target = nullptr; |
| } |
| + |
| + if (view == last_mouse_move_target_) { |
| + last_mouse_move_target_ = nullptr; |
| + last_mouse_move_root_view_ = nullptr; |
| + } |
|
Navid Zolghadr
2016/09/09 14:27:41
When do we call this destructor? Is it when the if
kenrb
2016/09/12 17:44:46
Yes.
This is called when a RenderWidgetHostView i
|
| } |
| void RenderWidgetHostInputEventRouter::ClearAllObserverRegistrations() { |
| @@ -96,7 +105,9 @@ bool RenderWidgetHostInputEventRouter::HittestDelegate::AcceptHitTarget( |
| } |
| RenderWidgetHostInputEventRouter::RenderWidgetHostInputEventRouter() |
| - : active_touches_(0), |
| + : last_mouse_move_target_(nullptr), |
| + last_mouse_move_root_view_(nullptr), |
| + active_touches_(0), |
| in_touchscreen_gesture_pinch_(false), |
| gesture_pinch_did_send_scroll_begin_(false) {} |
| @@ -147,6 +158,14 @@ void RenderWidgetHostInputEventRouter::RouteMouseEvent( |
| if (!target) |
| return; |
| + // SendMouseEnterOrLeaveEvents is called with the original event |
| + // coordinates, which are transformed independently for each view that will |
| + // receive an event. |
| + if ((event->type == blink::WebInputEvent::MouseLeave || |
| + event->type == blink::WebInputEvent::MouseMove) && |
| + target != last_mouse_move_target_) |
| + SendMouseEnterOrLeaveEvents(event, target, root_view); |
| + |
| event->x = transformed_point.x(); |
| event->y = transformed_point.y(); |
| // TODO(wjmaclean): Initialize latency info correctly for OOPIFs. |
| @@ -286,6 +305,119 @@ void RenderWidgetHostInputEventRouter::RouteTouchEvent( |
| } |
| } |
| +void RenderWidgetHostInputEventRouter::SendMouseEnterOrLeaveEvents( |
| + blink::WebMouseEvent* event, |
| + RenderWidgetHostViewBase* target, |
| + RenderWidgetHostViewBase* root_view) { |
| + // This method treats RenderWidgetHostViews as a tree, where the mouse |
| + // cursor is potentially leaving one node and entering another somewhere |
| + // else in the tree. Since iframes are graphically self-contained (i.e. an |
| + // iframe can't have a descendant that renders outside of its rect |
| + // boundaries), all affected RenderWidgetHostViews are ancestors of either |
| + // the node being exited or the node being entered. |
| + // Approach: |
| + // 1. Find lowest common ancestor (LCA) of the last view and current target |
| + // view. |
| + // 2. The last view, and its ancestors up to but not including the LCA, |
| + // receive a MouseLeave. |
| + // 3. The LCA itself, unless it is the new target, receives a MouseOut |
| + // because the cursor has passed between elements within its bounds. |
| + // 4. The new target view's ancestors, up to but not including the LCA, |
| + // receive a MouseEnter. |
| + // Ordering does not matter since these are handled asynchronously relative |
| + // to each other. |
| + |
| + // If the mouse has moved onto a different root view (typically meaning it |
| + // has crossed over a popup or context menu boundary), then we invalidate |
| + // last_mouse_move_target_ because we have no reference for its coordinate |
| + // space. |
| + if (root_view != last_mouse_move_root_view_) |
| + last_mouse_move_target_ = nullptr; |
| + |
| + // Finding the LCA uses a standard approach. We build vectors of the |
| + // ancestors of each node up to the root, and then remove common ancestors. |
| + std::vector<RenderWidgetHostViewBase*> entered_views; |
| + std::vector<RenderWidgetHostViewBase*> exited_views; |
| + RenderWidgetHostViewBase* cur_view = target; |
| + entered_views.push_back(cur_view); |
| + while (cur_view != root_view) { |
| + // Non-root RWHVs are guaranteed to be RenderWidgetHostViewChildFrames. |
| + cur_view = |
| + static_cast<RenderWidgetHostViewChildFrame*>(cur_view)->GetParentView(); |
| + // cur_view can possibly be nullptr for guestviews that are not currently |
| + // connected to the webcontents tree. |
| + if (!cur_view) |
| + break; |
| + entered_views.push_back(cur_view); |
| + } |
| + |
| + cur_view = last_mouse_move_target_; |
| + if (cur_view) { |
| + exited_views.push_back(cur_view); |
| + while (cur_view != root_view) { |
| + cur_view = static_cast<RenderWidgetHostViewChildFrame*>(cur_view) |
| + ->GetParentView(); |
| + if (!cur_view) |
| + break; |
| + exited_views.push_back(cur_view); |
| + } |
| + } |
| + |
| + // This removes common ancestors from the root downward. |
| + RenderWidgetHostViewBase* common_ancestor = nullptr; |
| + while (entered_views.size() > 0 && |
| + exited_views.size() > 0 && |
| + entered_views.back() == exited_views.back()) { |
| + common_ancestor = entered_views.back(); |
| + entered_views.pop_back(); |
| + exited_views.pop_back(); |
| + } |
| + |
| + gfx::Point transformed_point; |
| + // Send MouseLeaves. |
| + for (auto view : exited_views) { |
| + blink::WebMouseEvent mouse_leave(*event); |
| + mouse_leave.type = blink::WebInputEvent::MouseLeave; |
| + transformed_point = root_view->TransformPointToCoordSpaceForView( |
| + gfx::Point(event->x, event->y), view); |
| + mouse_leave.x = transformed_point.x(); |
| + mouse_leave.y = transformed_point.y(); |
| + view->ProcessMouseEvent(mouse_leave, ui::LatencyInfo()); |
| + } |
| + |
| + // TODO(kenrb): The ancestor might need to trigger MouseOut handlers. |
| + // Unfortunately there isn't a way to get Blink to do this, so we send a |
| + // MouseLeave for now. See bug https://crbug.com/638364. |
| + if (common_ancestor && common_ancestor != target) { |
| + blink::WebMouseEvent mouse_out(*event); |
| + mouse_out.type = blink::WebInputEvent::MouseLeave; |
| + transformed_point = root_view->TransformPointToCoordSpaceForView( |
|
Navid Zolghadr
2016/09/09 14:27:41
From the perspective of the common ancestor mouse
kenrb
2016/09/12 17:44:46
Changed.
|
| + gfx::Point(event->x, event->y), common_ancestor); |
| + mouse_out.x = transformed_point.x(); |
| + mouse_out.y = transformed_point.y(); |
| + common_ancestor->ProcessMouseEvent(mouse_out, ui::LatencyInfo()); |
| + } |
| + |
| + // Send MouseEnters. |
| + for (auto view : entered_views) { |
| + if (view == target) |
| + continue; |
| + blink::WebMouseEvent mouse_enter(*event); |
| + // TODO(kenrb): Blink doesn't expect a MouseEnter to come from the browser |
| + // process, so we send a MouseMove, which can cause event handlers to fire |
| + // incorrectly. See bug https://crbug.com/638364. |
|
Navid Zolghadr
2016/09/09 14:27:41
I did a few tests myself. I believe sending a mous
kenrb
2016/09/12 17:44:46
Okay, this seems reasonable and I have changed the
|
| + mouse_enter.type = blink::WebInputEvent::MouseMove; |
| + transformed_point = root_view->TransformPointToCoordSpaceForView( |
| + gfx::Point(event->x, event->y), view); |
| + mouse_enter.x = transformed_point.x(); |
| + mouse_enter.y = transformed_point.y(); |
| + view->ProcessMouseEvent(mouse_enter, ui::LatencyInfo()); |
| + } |
| + |
| + last_mouse_move_target_ = target; |
| + last_mouse_move_root_view_ = root_view; |
| +} |
| + |
| void RenderWidgetHostInputEventRouter::BubbleScrollEvent( |
| RenderWidgetHostViewBase* target_view, |
| const blink::WebGestureEvent& event) { |