Index: content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc |
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc |
index 3cda5fbb787a553ca785b0b8e46b14bd019abdc9..2dccf4b98eb765acd0a8972f752d3fa6ed840250 100644 |
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc |
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc |
@@ -9,13 +9,20 @@ |
#include "base/macros.h" |
#include "base/memory/ptr_util.h" |
#include "base/run_loop.h" |
+#include "base/test/test_timeouts.h" |
+#include "content/browser/frame_host/render_widget_host_view_child_frame.h" |
#include "content/browser/renderer_host/render_widget_host_view_aura.h" |
#include "content/browser/renderer_host/render_widget_host_view_event_handler.h" |
#include "content/browser/web_contents/web_contents_impl.h" |
#include "content/public/test/browser_test_utils.h" |
#include "content/public/test/content_browser_test.h" |
#include "content/public/test/content_browser_test_utils.h" |
+#include "content/public/test/test_navigation_observer.h" |
+#include "content/public/test/test_utils.h" |
#include "content/shell/browser/shell.h" |
+#include "content/test/content_browser_test_utils_internal.h" |
+#include "net/dns/mock_host_resolver.h" |
+#include "net/test/embedded_test_server/embedded_test_server.h" |
#include "ui/aura/window.h" |
#include "ui/aura/window_tree_host.h" |
#include "ui/display/display_switches.h" |
@@ -170,13 +177,14 @@ class TouchSelectionControllerClientAuraTest : public ContentBrowserTest { |
rwhva->event_handler()->disable_input_event_router_for_testing(); |
} |
- private: |
+ protected: |
void SetUpOnMainThread() override { |
ContentBrowserTest::SetUpOnMainThread(); |
if (!ui::TouchSelectionMenuRunner::GetInstance()) |
menu_runner_.reset(new TestTouchSelectionMenuRunner); |
} |
+ private: |
void TearDownOnMainThread() override { |
menu_runner_ = nullptr; |
selection_controller_client_ = nullptr; |
@@ -223,6 +231,207 @@ IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, BasicSelection) { |
EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); |
} |
+class TouchSelectionControllerClientAuraSiteIsolationTest |
+ : public TouchSelectionControllerClientAuraTest, |
+ public testing::WithParamInterface<bool> { |
+ public: |
+ void SetUpCommandLine(base::CommandLine* command_line) override { |
+ IsolateAllSitesForTesting(command_line); |
+ } |
+ |
+ void SetUpOnMainThread() override { |
+ TouchSelectionControllerClientAuraTest::SetUpOnMainThread(); |
+ host_resolver()->AddRule("*", "127.0.0.1"); |
+ SetupCrossSiteRedirector(embedded_test_server()); |
+ ASSERT_TRUE(embedded_test_server()->Start()); |
+ } |
+ |
+ void SelectWithLongPress(gfx::Point point) { |
+ // Get main frame view for event insertion. |
+ RenderWidgetHostViewAura* main_view = GetRenderWidgetHostViewAura(); |
+ |
+ SendTouch(main_view, ui::ET_TOUCH_PRESSED, point); |
+ SendTouch(main_view, ui::ET_TOUCH_RELEASED, point); |
+ SendGestureTap(main_view, point); |
+ SendGestureLongPress(main_view, point); |
+ } |
+ |
+ void SimpleTap(gfx::Point point) { |
+ // Get main frame view for event insertion. |
+ RenderWidgetHostViewAura* main_view = GetRenderWidgetHostViewAura(); |
+ |
+ SendTouch(main_view, ui::ET_TOUCH_PRESSED, point); |
+ SendTouch(main_view, ui::ET_TOUCH_RELEASED, point); |
+ SendGestureTap(main_view, point); |
+ } |
+ |
+ private: |
+ void SendTouch(RenderWidgetHostViewAura* view, |
+ ui::EventType type, |
+ gfx::Point point) { |
+ DCHECK(type >= ui::ET_TOUCH_RELEASED && type << ui::ET_TOUCH_CANCELLED); |
+ ui::TouchEvent touch( |
+ type, point, ui::EventTimeForNow(), |
+ ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); |
+ view->OnTouchEvent(&touch); |
+ } |
+ |
+ void SendGestureTap(RenderWidgetHostViewAura* view, gfx::Point point) { |
+ ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN); |
+ tap_down_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
+ ui::GestureEvent gesture_tap_down(point.x(), point.y(), 0, |
+ ui::EventTimeForNow(), tap_down_details); |
+ view->OnGestureEvent(&gesture_tap_down); |
+ ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP); |
+ tap_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
+ tap_details.set_tap_count(1); |
+ ui::GestureEvent gesture_tap(point.x(), point.y(), 0, ui::EventTimeForNow(), |
+ tap_details); |
+ view->OnGestureEvent(&gesture_tap); |
+ } |
+ |
+ void SendGestureLongPress(RenderWidgetHostViewAura* view, gfx::Point point) { |
+ ui::GestureEventDetails long_press_details(ui::ET_GESTURE_LONG_PRESS); |
+ long_press_details.set_device_type( |
+ ui::GestureDeviceType::DEVICE_TOUCHSCREEN); |
+ ui::GestureEvent gesture_long_press( |
+ point.x(), point.y(), 0, ui::EventTimeForNow(), long_press_details); |
+ view->OnGestureEvent(&gesture_long_press); |
+ } |
+}; |
+ |
+class FrameStableObserver { |
+ public: |
+ FrameStableObserver(RenderWidgetHostViewBase* view, base::TimeDelta delta) |
+ : view_(view), delta_(delta) {} |
+ virtual ~FrameStableObserver() {} |
+ |
+ void WaitUntilStable() { |
+ uint32_t current_frame_number = view_->RendererFrameNumber(); |
+ uint32_t previous_frame_number; |
+ |
+ do { |
+ base::RunLoop run_loop; |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, run_loop.QuitClosure(), delta_); |
+ run_loop.Run(); |
+ previous_frame_number = current_frame_number; |
+ current_frame_number = view_->RendererFrameNumber(); |
+ } while (current_frame_number != previous_frame_number); |
+ } |
+ |
+ private: |
+ RenderWidgetHostViewBase* view_; |
+ base::TimeDelta delta_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FrameStableObserver); |
+}; |
+ |
+INSTANTIATE_TEST_CASE_P(TouchSelectionForCrossProcessFramesTests, |
+ TouchSelectionControllerClientAuraSiteIsolationTest, |
+ testing::Bool()); |
+ |
+IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAuraSiteIsolationTest, |
+ BasicSelectionIsolatedIframe) { |
+ GURL test_url(embedded_test_server()->GetURL( |
+ "a.com", "/cross_site_iframe_factory.html?a(a)")); |
+ EXPECT_TRUE(NavigateToURL(shell(), test_url)); |
+ FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
+ ->GetFrameTree() |
+ ->root(); |
+ EXPECT_EQ( |
+ " Site A\n" |
+ " +--Site A\n" |
+ "Where A = http://a.com/", |
+ FrameTreeVisualizer().DepictFrameTree(root)); |
+ TestNavigationObserver observer(shell()->web_contents()); |
+ EXPECT_EQ(1u, root->child_count()); |
+ FrameTreeNode* child = root->child_at(0); |
+ |
+ RenderWidgetHostViewAura* parent_view = |
+ static_cast<RenderWidgetHostViewAura*>( |
+ root->current_frame_host()->GetRenderWidgetHost()->GetView()); |
+ TestTouchSelectionControllerClientAura* parent_selection_controller_client = |
+ new TestTouchSelectionControllerClientAura(parent_view); |
+ parent_view->SetSelectionControllerClientForTest( |
+ base::WrapUnique(parent_selection_controller_client)); |
+ |
+ // We need to load the desired subframe and then wait until it's stable, i.e. |
+ // generates no new frames for some reasonable time period: a stray frame |
+ // between touch selection's pre-handling of GestureLongPress and the |
+ // expected frame containing the selected region can confuse the |
+ // TouchSelectionController, causing it to fail to show selection handles. |
+ // Note this is an issue with the TouchSelectionController in general, and |
+ // not a property of this test. |
+ GURL child_url( |
+ embedded_test_server()->GetURL("b.com", "/touch_selection.html")); |
+ NavigateFrameToURL(child, child_url); |
+ EXPECT_EQ( |
+ " Site A ------------ proxies for B\n" |
+ " +--Site B ------- proxies for A\n" |
+ "Where A = http://a.com/\n" |
+ " B = http://b.com/", |
+ FrameTreeVisualizer().DepictFrameTree(root)); |
+ |
+ // The child will change with the cross-site navigation. It shouldn't change |
+ // after this. |
+ child = root->child_at(0); |
+ WaitForChildFrameSurfaceReady(child->current_frame_host()); |
+ |
+ RenderWidgetHostViewChildFrame* child_view = |
+ static_cast<RenderWidgetHostViewChildFrame*>( |
+ child->current_frame_host()->GetRenderWidgetHost()->GetView()); |
+ |
+ EXPECT_EQ(child_url, observer.last_navigation_url()); |
+ EXPECT_TRUE(observer.last_navigation_succeeded()); |
+ FrameStableObserver child_frame_stable_observer(child_view, |
+ TestTimeouts::tiny_timeout()); |
+ child_frame_stable_observer.WaitUntilStable(); |
+ |
+ EXPECT_EQ(ui::TouchSelectionController::INACTIVE, |
+ parent_view->selection_controller()->active_status()); |
+ |
+ // Find the location of some text to select. |
+ gfx::PointF point_f; |
+ std::string str; |
+ EXPECT_TRUE(ExecuteScriptAndExtractString(child->current_frame_host(), |
+ "get_point_inside_text()", &str)); |
+ JSONToPoint(str, &point_f); |
+ gfx::Point origin = child_view->GetViewOriginInRoot(); |
+ gfx::Vector2dF origin_vec(origin.x(), origin.y()); |
+ point_f += origin_vec; |
+ |
+ // Initiate selection with a sequence of events that go through the targeting |
+ // system. |
+ parent_selection_controller_client->InitWaitForSelectionEvent( |
+ ui::SELECTION_HANDLES_SHOWN); |
+ |
+ SelectWithLongPress(gfx::Point(point_f.x(), point_f.y())); |
+ |
+ parent_selection_controller_client->Wait(); |
+ |
+ // Check that selection is active and the quick menu is showing. |
+ EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, |
+ parent_view->selection_controller()->active_status()); |
+ EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); |
+ |
+ // Tap inside/outside the iframe and make sure the selection handles go away. |
+ parent_selection_controller_client->InitWaitForSelectionEvent( |
+ ui::SELECTION_HANDLES_CLEARED); |
+ if (GetParam()) { |
+ gfx::PointF point_outside_iframe = gfx::PointF(-1.f, -1.f) + origin_vec; |
+ SimpleTap(gfx::Point(point_outside_iframe.x(), point_outside_iframe.y())); |
+ } else { |
+ gfx::PointF point_inside_iframe = gfx::PointF(+1.f, +1.f) + origin_vec; |
+ SimpleTap(gfx::Point(point_inside_iframe.x(), point_inside_iframe.y())); |
+ } |
+ parent_selection_controller_client->Wait(); |
+ |
+ EXPECT_EQ(ui::TouchSelectionController::INACTIVE, |
+ parent_view->selection_controller()->active_status()); |
+ EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); |
+} |
+ |
// Tests that tapping in a textfield brings up the insertion handle, but not the |
// quick menu, initially. Then, successive taps on the insertion handle toggle |
// the quick menu visibility. |