summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorwjmaclean <wjmaclean@chromium.org>2016-03-04 13:49:19 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-04 21:50:23 +0000
commit4446423513e9f2da52e156bc93b5228e6a3d6e08 (patch)
treecc86b950425ee48726f3bc57c58d7249b744523d /content
parent9a995c82fd5c1d186ebd3d0a30d5a78b29dd5e82 (diff)
downloadchromium_src-4446423513e9f2da52e156bc93b5228e6a3d6e08.zip
chromium_src-4446423513e9f2da52e156bc93b5228e6a3d6e08.tar.gz
chromium_src-4446423513e9f2da52e156bc93b5228e6a3d6e08.tar.bz2
Implement Gesture event hit testing/forwarding for OOPIF.
This CL implements plumbing to forward gesture events to OOPIF frames. Scroll bubbling will be handled in a follow-on CL. BUG=587023 CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_site_isolation Review URL: https://codereview.chromium.org/1752833002 Cr-Commit-Position: refs/heads/master@{#379367}
Diffstat (limited to 'content')
-rw-r--r--content/browser/frame_host/render_widget_host_view_child_frame.cc6
-rw-r--r--content/browser/frame_host/render_widget_host_view_child_frame.h2
-rw-r--r--content/browser/renderer_host/render_widget_host_input_event_router.cc73
-rw-r--r--content/browser/renderer_host/render_widget_host_input_event_router.h22
-rw-r--r--content/browser/renderer_host/render_widget_host_view_aura.cc25
-rw-r--r--content/browser/renderer_host/render_widget_host_view_aura.h7
-rw-r--r--content/browser/renderer_host/render_widget_host_view_base.h4
-rw-r--r--content/browser/renderer_host/render_widget_host_view_mac.h2
-rw-r--r--content/browser/renderer_host/render_widget_host_view_mac.mm6
-rw-r--r--content/browser/site_per_process_browsertest.cc236
-rw-r--r--content/test/data/page_with_click_handler.html36
11 files changed, 399 insertions, 20 deletions
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc
index 35749e9..7c60d9c 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc
@@ -426,6 +426,12 @@ void RenderWidgetHostViewChildFrame::ProcessTouchEvent(
host_->ForwardTouchEventWithLatencyInfo(event, latency);
}
+void RenderWidgetHostViewChildFrame::ProcessGestureEvent(
+ const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) {
+ host_->ForwardGestureEventWithLatencyInfo(event, latency);
+}
+
gfx::Point RenderWidgetHostViewChildFrame::TransformPointToRootCoordSpace(
const gfx::Point& point) {
if (!frame_connector_)
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.h b/content/browser/frame_host/render_widget_host_view_child_frame.h
index 615028f..f6cba54 100644
--- a/content/browser/frame_host/render_widget_host_view_child_frame.h
+++ b/content/browser/frame_host/render_widget_host_view_child_frame.h
@@ -141,6 +141,8 @@ class CONTENT_EXPORT RenderWidgetHostViewChildFrame
void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void ProcessTouchEvent(const blink::WebTouchEvent& event,
const ui::LatencyInfo& latency) override;
+ void ProcessGestureEvent(const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) override;
gfx::Point TransformPointToRootCoordSpace(const gfx::Point& point) override;
#if defined(OS_MACOSX)
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 355a358..0343bd9 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
@@ -38,10 +38,24 @@ void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed(
}
}
- if (view == current_touch_target_) {
- current_touch_target_ = nullptr;
+ if (view == touch_target_) {
+ touch_target_ = nullptr;
+ touch_delta_ = gfx::Vector2d();
active_touches_ = 0;
}
+
+ // If the target that's being destroyed is in the gesture target queue, we
+ // replace it with nullptr so that we maintain the 1:1 correspondence between
+ // queue entries and the touch sequences that underly them.
+ for (size_t i = 0; i < gesture_target_queue_.size(); ++i) {
+ if (gesture_target_queue_[i].target == view)
+ gesture_target_queue_[i].target = nullptr;
+ }
+
+ if (view == gesture_target_) {
+ gesture_target_ = nullptr;
+ gesture_delta_ = gfx::Vector2d();
+ }
}
void RenderWidgetHostInputEventRouter::ClearAllObserverRegistrations() {
@@ -74,7 +88,9 @@ bool RenderWidgetHostInputEventRouter::HittestDelegate::AcceptHitTarget(
}
RenderWidgetHostInputEventRouter::RenderWidgetHostInputEventRouter()
- : current_touch_target_(nullptr), active_touches_(0) {}
+ : touch_target_(nullptr),
+ gesture_target_(nullptr),
+ active_touches_(0) {}
RenderWidgetHostInputEventRouter::~RenderWidgetHostInputEventRouter() {
// We may be destroyed before some of the owners in the map, so we must
@@ -144,6 +160,31 @@ void RenderWidgetHostInputEventRouter::RouteMouseWheelEvent(
target->ProcessMouseWheelEvent(*event);
}
+void RenderWidgetHostInputEventRouter::RouteGestureEvent(
+ RenderWidgetHostViewBase* root_view,
+ blink::WebGestureEvent* event,
+ const ui::LatencyInfo& latency) {
+ // We use GestureTapDown to detect the start of a gesture sequence since there
+ // is no WebGestureEvent equivalent for ET_GESTURE_BEGIN. Note that this
+ // means the GestureFlingCancel that always comes between ET_GESTURE_BEGIN and
+ // GestureTapDown is sent to the previous target, in case it is still in a
+ // fling.
+ if (event->type == blink::WebInputEvent::GestureTapDown) {
+ DCHECK(!gesture_target_queue_.empty());
+ const GestureTargetData& data = gesture_target_queue_.front();
+ gesture_target_ = data.target;
+ gesture_delta_ = data.delta;
+ gesture_target_queue_.pop_front();
+ }
+
+ if (!gesture_target_)
+ return;
+
+ event->x += gesture_delta_.x();
+ event->y += gesture_delta_.y();
+ gesture_target_->ProcessGestureEvent(*event, latency);
+}
+
void RenderWidgetHostInputEventRouter::RouteTouchEvent(
RenderWidgetHostViewBase* root_view,
blink::WebTouchEvent* event,
@@ -153,46 +194,48 @@ void RenderWidgetHostInputEventRouter::RouteTouchEvent(
if (!active_touches_) {
// Since this is the first touch, it defines the target for the rest
// of this sequence.
- DCHECK(!current_touch_target_);
+ DCHECK(!touch_target_);
gfx::Point transformed_point;
gfx::Point original_point(event->touches[0].position.x,
event->touches[0].position.y);
- current_touch_target_ =
+ touch_target_ =
FindEventTarget(root_view, original_point, &transformed_point);
- if (!current_touch_target_)
- return;
// TODO(wjmaclean): Instead of just computing a delta, we should extract
// the complete transform. We assume it doesn't change for the duration
// of the touch sequence, though this could be wrong; a better approach
- // might be to always transform each point to the current_touch_target_
+ // might be to always transform each point to the touch_target_
// for the duration of the sequence.
touch_delta_ = transformed_point - original_point;
+ gesture_target_queue_.emplace_back(touch_target_, touch_delta_);
+
+ if (!touch_target_)
+ return;
}
++active_touches_;
- if (current_touch_target_) {
+ if (touch_target_) {
TransformEventTouchPositions(event, touch_delta_);
- current_touch_target_->ProcessTouchEvent(*event, latency);
+ touch_target_->ProcessTouchEvent(*event, latency);
}
break;
}
case blink::WebInputEvent::TouchMove:
- if (current_touch_target_) {
+ if (touch_target_) {
TransformEventTouchPositions(event, touch_delta_);
- current_touch_target_->ProcessTouchEvent(*event, latency);
+ touch_target_->ProcessTouchEvent(*event, latency);
}
break;
case blink::WebInputEvent::TouchEnd:
case blink::WebInputEvent::TouchCancel:
- if (!current_touch_target_)
+ if (!touch_target_)
break;
DCHECK(active_touches_);
TransformEventTouchPositions(event, touch_delta_);
- current_touch_target_->ProcessTouchEvent(*event, latency);
+ touch_target_->ProcessTouchEvent(*event, latency);
--active_touches_;
if (!active_touches_) {
- current_touch_target_ = nullptr;
+ touch_target_ = nullptr;
touch_delta_ = gfx::Vector2d();
}
break;
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index 5aba015..b4123539 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -6,9 +6,12 @@
#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_INPUT_EVENT_ROUTER_H_
#include <stdint.h>
+
+#include <deque>
#include <unordered_map>
#include "base/containers/hash_tables.h"
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "cc/surfaces/surface_hittest_delegate.h"
#include "cc/surfaces/surface_id.h"
@@ -19,6 +22,7 @@
struct FrameHostMsg_HittestData_Params;
namespace blink {
+class WebGestureEvent;
class WebMouseEvent;
class WebMouseWheelEvent;
class WebTouchEvent;
@@ -55,6 +59,9 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
blink::WebMouseEvent* event);
void RouteMouseWheelEvent(RenderWidgetHostViewBase* root_view,
blink::WebMouseWheelEvent* event);
+ void RouteGestureEvent(RenderWidgetHostViewBase* root_view,
+ blink::WebGestureEvent* event,
+ const ui::LatencyInfo& latency);
void RouteTouchEvent(RenderWidgetHostViewBase* root_view,
blink::WebTouchEvent *event,
const ui::LatencyInfo& latency);
@@ -89,6 +96,14 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
using SurfaceIdNamespaceOwnerMap =
base::hash_map<uint32_t, RenderWidgetHostViewBase*>;
+ struct GestureTargetData {
+ RenderWidgetHostViewBase* target;
+ const gfx::Vector2d delta;
+
+ GestureTargetData(RenderWidgetHostViewBase* target, gfx::Vector2d delta)
+ : target(target), delta(delta) {}
+ };
+ using GestureTargetQueue = std::deque<GestureTargetData>;
void ClearAllObserverRegistrations();
@@ -97,13 +112,18 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
gfx::Point* transformed_point);
SurfaceIdNamespaceOwnerMap owner_map_;
- RenderWidgetHostViewBase* current_touch_target_;
+ GestureTargetQueue gesture_target_queue_;
+ RenderWidgetHostViewBase* touch_target_;
+ RenderWidgetHostViewBase* gesture_target_;
gfx::Vector2d touch_delta_;
+ gfx::Vector2d gesture_delta_;
int active_touches_;
std::unordered_map<cc::SurfaceId, HittestData, cc::SurfaceIdHash>
hittest_data_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostInputEventRouter);
+ FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
+ InputEventRouterGestureTargetQueueTest);
};
} // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 6963b21..3a46b4c 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -481,6 +481,7 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host,
begin_frame_observer_proxy_(this),
set_focus_on_mouse_down_or_key_event_(false),
device_scale_factor_(0.0f),
+ disable_input_event_router_for_testing_(false),
weak_ptr_factory_(this) {
if (!is_guest_view_hack_)
host_->SetView(this);
@@ -869,7 +870,8 @@ bool RenderWidgetHostViewAura::ShouldRouteEvent(const ui::Event* event) const {
// 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();
+ bool result = host_->delegate() && host_->delegate()->GetInputEventRouter() &&
+ !disable_input_event_router_for_testing_;
if (event->IsMouseEvent())
result = result && SiteIsolationPolicy::AreCrossProcessFramesPossible();
return result;
@@ -2260,6 +2262,12 @@ void RenderWidgetHostViewAura::ProcessTouchEvent(
host_->ForwardTouchEventWithLatencyInfo(event, latency);
}
+void RenderWidgetHostViewAura::ProcessGestureEvent(
+ const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) {
+ host_->ForwardGestureEventWithLatencyInfo(event, latency);
+}
+
void RenderWidgetHostViewAura::TransformPointToLocalCoordSpace(
const gfx::Point& point,
cc::SurfaceId original_surface,
@@ -2368,11 +2376,21 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) {
blink::WebGestureEvent fling_cancel = gesture;
fling_cancel.type = blink::WebInputEvent::GestureFlingCancel;
fling_cancel.sourceDevice = blink::WebGestureDeviceTouchscreen;
- host_->ForwardGestureEvent(fling_cancel);
+ if (ShouldRouteEvent(event)) {
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
+ this, &fling_cancel, ui::LatencyInfo());
+ } else {
+ host_->ForwardGestureEvent(fling_cancel);
+ }
}
if (gesture.type != blink::WebInputEvent::Undefined) {
- host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency());
+ if (ShouldRouteEvent(event)) {
+ host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
+ this, &gesture, *event->latency());
+ } else {
+ host_->ForwardGestureEventWithLatencyInfo(gesture, *event->latency());
+ }
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
@@ -2738,6 +2756,7 @@ void RenderWidgetHostViewAura::SetSelectionControllerClientForTest(
scoped_ptr<TouchSelectionControllerClientAura> client) {
selection_controller_client_.swap(client);
CreateSelectionController();
+ disable_input_event_router_for_testing_ = true;
}
void RenderWidgetHostViewAura::InternalSetBounds(const gfx::Rect& rect) {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 76e402f..7e9b2b3 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -202,6 +202,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void ProcessTouchEvent(const blink::WebTouchEvent& event,
const ui::LatencyInfo& latency) override;
+ void ProcessGestureEvent(const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) override;
void TransformPointToLocalCoordSpace(const gfx::Point& point,
cc::SurfaceId original_surface,
gfx::Point* transformed_point) override;
@@ -703,6 +705,11 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
float device_scale_factor_;
+ // Allows tests to send gesture events for testing without first sending a
+ // corresponding touch sequence, as would be required by
+ // RenderWidgetHostInputEventRouter.
+ bool disable_input_event_router_for_testing_;
+
base::WeakPtrFactory<RenderWidgetHostViewAura> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAura);
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 94fbbb6..1cace30 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -221,7 +221,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView,
virtual void ProcessMouseEvent(const blink::WebMouseEvent& event) {}
virtual void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event) {}
virtual void ProcessTouchEvent(const blink::WebTouchEvent& event,
- const ui::LatencyInfo& latency) {}
+ const ui::LatencyInfo& latency) {}
+ virtual void ProcessGestureEvent(const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) {}
// Transform a point that is in the coordinate space of a Surface that is
// embedded within the RenderWidgetHostViewBase's Surface to the
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 46d1813..20579a5 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -362,6 +362,8 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
void ProcessMouseWheelEvent(const blink::WebMouseWheelEvent& event) override;
void ProcessTouchEvent(const blink::WebTouchEvent& event,
const ui::LatencyInfo& latency) override;
+ void ProcessGestureEvent(const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) override;
void TransformPointToLocalCoordSpace(const gfx::Point& point,
cc::SurfaceId original_surface,
gfx::Point* transformed_point) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index e1a196b..62b0b38 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1615,6 +1615,12 @@ void RenderWidgetHostViewMac::ProcessTouchEvent(
render_widget_host_->ForwardTouchEventWithLatencyInfo(event, latency);
}
+void RenderWidgetHostViewMac::ProcessGestureEvent(
+ const blink::WebGestureEvent& event,
+ const ui::LatencyInfo& latency) {
+ render_widget_host_->ForwardGestureEventWithLatencyInfo(event, latency);
+}
+
void RenderWidgetHostViewMac::TransformPointToLocalCoordSpace(
const gfx::Point& point,
cc::SurfaceId original_surface,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 4031eec..1482436 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -26,10 +26,13 @@
#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/browser/frame_host/render_widget_host_view_child_frame.h"
#include "content/browser/gpu/compositor_util.h"
+#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
+#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/frame_messages.h"
+#include "content/common/input/synthetic_tap_gesture_params.h"
#include "content/common/view_messages.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_service.h"
@@ -4576,6 +4579,239 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
"window.domAutomationController.send(getLastTouchEvent());", &result));
EXPECT_EQ("touchstart", result);
}
+
+namespace {
+
+// Declared here to be close to the SubframeGestureEventRouting test.
+void OnSyntheticGestureCompleted(scoped_refptr<MessageLoopRunner> runner,
+ SyntheticGesture::Result result) {
+ EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+ runner->Quit();
+}
+
+} // namespace anonymous
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+ SubframeGestureEventRouting) {
+ GURL main_url(embedded_test_server()->GetURL(
+ "/frame_tree/page_with_positioned_nested_frames.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = web_contents->GetFrameTree()->root();
+ ASSERT_EQ(1U, root->child_count());
+
+ GURL frame_url(
+ embedded_test_server()->GetURL("b.com", "/page_with_click_handler.html"));
+ NavigateFrameToURL(root->child_at(0), frame_url);
+ auto child_frame_host = root->child_at(0)->current_frame_host();
+ EXPECT_TRUE(WaitForRenderFrameReady(child_frame_host));
+
+ // Synchronize with the child and parent renderers to guarantee that the
+ // surface information required for event hit testing is ready.
+ RenderWidgetHostViewBase* child_rwhv = static_cast<RenderWidgetHostViewBase*>(
+ child_frame_host->GetView());
+ SurfaceHitTestReadyNotifier notifier(
+ static_cast<RenderWidgetHostViewChildFrame*>(child_rwhv));
+ notifier.WaitForSurfaceReady();
+
+ // There have been no GestureTaps sent yet.
+ {
+ std::string result;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ child_frame_host,
+ "window.domAutomationController.send(getClickStatus());", &result));
+ EXPECT_EQ("0 clicks received", result);
+ }
+
+ // Simulate touch sequence to send GestureTap to sub-frame.
+ SyntheticTapGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
+ gfx::Point center(150, 150);
+ params.position = gfx::PointF(center.x(), center.y());
+ params.duration_ms = 100;
+ scoped_ptr<SyntheticTapGesture> gesture(new SyntheticTapGesture(params));
+
+ scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner();
+
+ RenderWidgetHostImpl* render_widget_host =
+ root->current_frame_host()->GetRenderWidgetHost();
+ // TODO(wjmaclean): Convert the call to base::Bind() to a lambda someday.
+ render_widget_host->QueueSyntheticGesture(
+ std::move(gesture), base::Bind(OnSyntheticGestureCompleted, runner));
+
+ // We need to run the message loop while we wait for the synthetic gesture
+ // to be processed; the callback registered above will get us out of the
+ // message loop when that happens.
+ runner->Run();
+ runner = nullptr;
+
+ // Verify click handler in subframe was invoked
+ {
+ std::string result;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ child_frame_host,
+ "window.domAutomationController.send(getClickStatus());", &result));
+ EXPECT_EQ("1 click received", result);
+ }
+}
+
+namespace {
+
+// Defined here to be close to
+// SitePerProcessBrowserTest.InputEventRouterGestureTargetQueueTest.
+void SendTouchTapWithExpectedTarget(
+ RenderWidgetHostViewBase* root_view,
+ const gfx::Point& touch_point,
+ RenderWidgetHostViewBase*& router_touch_target,
+ const RenderWidgetHostViewBase* expected_target) {
+ auto root_view_aura = static_cast<RenderWidgetHostViewAura*>(root_view);
+ ui::TouchEvent touch_event_pressed(ui::ET_TOUCH_PRESSED, touch_point, 0,
+ 0, ui::EventTimeForNow(), 30.f, 30.f, 0.f,
+ 0.f);
+ root_view_aura->OnTouchEvent(&touch_event_pressed);
+ EXPECT_EQ(expected_target, router_touch_target);
+ ui::TouchEvent touch_event_released(ui::ET_TOUCH_RELEASED, touch_point,
+ 0, 0, ui::EventTimeForNow(), 30.f, 30.f,
+ 0.f, 0.f);
+ root_view_aura->OnTouchEvent(&touch_event_released);
+ EXPECT_EQ(nullptr, router_touch_target);
+}
+
+void SendGestureTapSequenceWithExpectedTarget(
+ RenderWidgetHostViewBase* root_view,
+ gfx::Point gesture_point,
+ RenderWidgetHostViewBase*& router_gesture_target,
+ const RenderWidgetHostViewBase* old_expected_target,
+ const RenderWidgetHostViewBase* expected_target) {
+ auto root_view_aura = static_cast<RenderWidgetHostViewAura*>(root_view);
+
+ ui::GestureEvent gesture_begin_event(
+ gesture_point.x(), gesture_point.y(), 0, ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_BEGIN));
+ root_view_aura->OnGestureEvent(&gesture_begin_event);
+ // We expect to still have the old gesture target in place for the
+ // GestureFlingCancel that will be inserted before GestureTapDown.
+ // Note: the GestureFlingCancel is inserted by RenderWidgetHostViewAura::
+ // OnGestureEvent() when it sees ui::ET_GESTURE_TAP_DOWN, so we don't
+ // explicitly add it here.
+ EXPECT_EQ(old_expected_target, router_gesture_target);
+
+ ui::GestureEvent gesture_tap_down_event(
+ gesture_point.x(), gesture_point.y(), 0, ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
+ root_view_aura->OnGestureEvent(&gesture_tap_down_event);
+ EXPECT_EQ(expected_target, router_gesture_target);
+
+ ui::GestureEvent gesture_show_press_event(
+ gesture_point.x(), gesture_point.y(), 0, ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS));
+ root_view_aura->OnGestureEvent(&gesture_show_press_event);
+ EXPECT_EQ(expected_target, router_gesture_target);
+
+ ui::GestureEventDetails gesture_tap_details(ui::ET_GESTURE_TAP);
+ gesture_tap_details.set_tap_count(1);
+ ui::GestureEvent gesture_tap_event(
+ gesture_point.x(), gesture_point.y(), 0, ui::EventTimeForNow(),
+ gesture_tap_details);
+ root_view_aura->OnGestureEvent(&gesture_tap_event);
+ EXPECT_EQ(expected_target, router_gesture_target);
+
+ ui::GestureEvent gesture_end_event(
+ gesture_point.x(), gesture_point.y(), 0, ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_END));
+ root_view_aura->OnGestureEvent(&gesture_end_event);
+ EXPECT_EQ(expected_target, router_gesture_target);
+}
+
+} // namespace anonymous
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+ InputEventRouterGestureTargetQueueTest) {
+ GURL main_url(embedded_test_server()->GetURL(
+ "/frame_tree/page_with_positioned_nested_frames.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = web_contents->GetFrameTree()->root();
+ ASSERT_EQ(1U, root->child_count());
+
+ GURL frame_url(
+ embedded_test_server()->GetURL("b.com", "/page_with_click_handler.html"));
+ NavigateFrameToURL(root->child_at(0), frame_url);
+ auto child_frame_host = root->child_at(0)->current_frame_host();
+ EXPECT_TRUE(WaitForRenderFrameReady(child_frame_host));
+
+ // Synchronize with the child and parent renderers to guarantee that the
+ // surface information required for event hit testing is ready.
+ auto rwhv_child =
+ static_cast<RenderWidgetHostViewBase*>(child_frame_host->GetView());
+ SurfaceHitTestReadyNotifier notifier(
+ static_cast<RenderWidgetHostViewChildFrame*>(rwhv_child));
+ notifier.WaitForSurfaceReady();
+
+ // All touches & gestures are sent to the main frame's view, and should be
+ // routed appropriately from there.
+ auto rwhv_parent = static_cast<RenderWidgetHostViewBase*>(
+ web_contents->GetRenderWidgetHostView());
+
+ RenderWidgetHostInputEventRouter* router =
+ web_contents->GetInputEventRouter();
+ EXPECT_TRUE(router->gesture_target_queue_.empty());
+ EXPECT_EQ(nullptr, router->gesture_target_);
+
+ // Send touch sequence to main-frame.
+ gfx::Point main_frame_point(25, 25);
+ SendTouchTapWithExpectedTarget(rwhv_parent, main_frame_point,
+ router->touch_target_, rwhv_parent);
+ EXPECT_EQ(1LU, router->gesture_target_queue_.size());
+ EXPECT_EQ(nullptr, router->gesture_target_);
+
+
+ // Send touch sequence to child.
+ gfx::Point child_center(150, 150);
+ SendTouchTapWithExpectedTarget(rwhv_parent, child_center,
+ router->touch_target_, rwhv_child);
+ EXPECT_EQ(2LU, router->gesture_target_queue_.size());
+ EXPECT_EQ(nullptr, router->gesture_target_);
+
+ // Send another touch sequence to main frame.
+ SendTouchTapWithExpectedTarget(rwhv_parent, main_frame_point,
+ router->touch_target_, rwhv_parent);
+ EXPECT_EQ(3LU, router->gesture_target_queue_.size());
+ EXPECT_EQ(nullptr, router->gesture_target_);
+
+ // Send Gestures to clear GestureTargetQueue.
+
+ // The first touch sequence should generate a GestureTapDown, sent to the
+ // main frame.
+ SendGestureTapSequenceWithExpectedTarget(rwhv_parent, main_frame_point,
+ router->gesture_target_, nullptr,
+ rwhv_parent);
+ EXPECT_EQ(2LU, router->gesture_target_queue_.size());
+ // Note: rwhv_parent is the target used for GestureFlingCancel sent by
+ // RenderWidgetHostViewAura::OnGestureEvent() at the start of the next gesture
+ // sequence; the sequence itself goes to rwhv_child.
+ EXPECT_EQ(rwhv_parent, router->gesture_target_);
+
+ // The second touch sequence should generate a GestureTapDown, sent to the
+ // child frame.
+ SendGestureTapSequenceWithExpectedTarget(rwhv_parent, child_center,
+ router->gesture_target_, rwhv_parent,
+ rwhv_child);
+ EXPECT_EQ(1LU, router->gesture_target_queue_.size());
+ EXPECT_EQ(rwhv_child, router->gesture_target_);
+
+ // The third touch sequence should generate a GestureTapDown, sent to the
+ // main frame.
+ SendGestureTapSequenceWithExpectedTarget(rwhv_parent, main_frame_point,
+ router->gesture_target_, rwhv_child,
+ rwhv_parent);
+ EXPECT_EQ(0LU, router->gesture_target_queue_.size());
+ EXPECT_EQ(rwhv_parent, router->gesture_target_);
+}
#endif // defined(USE_AURA)
// Ensure that a cross-process subframe can receive keyboard events when in
diff --git a/content/test/data/page_with_click_handler.html b/content/test/data/page_with_click_handler.html
new file mode 100644
index 0000000..cebf4ec
--- /dev/null
+++ b/content/test/data/page_with_click_handler.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html style="width: 100%; height: 100%">
+<head>
+ <script>
+ clicksReceived = 0;
+
+ function clickHandler(e) {
+ var clickResult = document.getElementById("click-results");
+ clicksReceived++;
+ if (clicksReceived == 1)
+ clickResult.textContent = "1 click received";
+ else
+ clickResult.textContent = clicksReceived + " clicks received";
+ }
+
+ function getClickStatus() {
+ var clickResult = document.getElementById("click-results");
+ return clickResult.textContent;
+ }
+
+ window.addEventListener('load', function(){
+ document.body.addEventListener('click',
+ function(e){
+ clickHandler(e);
+ }, false);
+ }, false);
+ </script>
+</head>
+
+<!-- Be sure to set 100% width/height so the test can determine an appropriate
+ screen region for targeting events. -->
+<body style="width: 100%; height: 100%">
+ Page with Click Handler
+ <div id="click-results">0 clicks received</div>
+</body>
+</html>