Commit ee78df38 authored by Xida Chen's avatar Xida Chen Committed by Commit Bot

Reland of: Handle iframe touch ack time out

The previous CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1151094
was reverted. The reason is that there were some crashes.

This CL solve the problem. PS#1 is exactly the same as the previous CL
which was reverted, so that it is easier for review.

TBR=creis@chromium.org

Bug: 851644
Change-Id: I80a8c3b7dfd94e7abace4e3281b959f9fc1dcd2a
Reviewed-on: https://chromium-review.googlesource.com/1157698Reviewed-by: default avatarXida Chen <xidachen@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Commit-Queue: Xida Chen <xidachen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580589}
parent 107c0b44
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <math.h> #include <math.h>
#include "base/debug/dump_without_crashing.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "third_party/blink/public/platform/web_gesture_event.h" #include "third_party/blink/public/platform/web_gesture_event.h"
...@@ -40,6 +41,10 @@ TouchActionFilter::TouchActionFilter() ...@@ -40,6 +41,10 @@ TouchActionFilter::TouchActionFilter()
allow_current_double_tap_event_(true), allow_current_double_tap_event_(true),
force_enable_zoom_(false) {} force_enable_zoom_(false) {}
TouchActionFilter::~TouchActionFilter() {
function_call_sequence_.clear();
}
FilterGestureEventResult TouchActionFilter::FilterGestureEvent( FilterGestureEventResult TouchActionFilter::FilterGestureEvent(
WebGestureEvent* gesture_event) { WebGestureEvent* gesture_event) {
if (gesture_event->SourceDevice() != blink::kWebGestureDeviceTouchscreen) if (gesture_event->SourceDevice() != blink::kWebGestureDeviceTouchscreen)
...@@ -74,6 +79,11 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent( ...@@ -74,6 +79,11 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent(
// two-finger scrolling but a "touch-action: pan-x pinch-zoom" region // two-finger scrolling but a "touch-action: pan-x pinch-zoom" region
// doesn't. // doesn't.
// TODO(mustaq): Add it to spec? // TODO(mustaq): Add it to spec?
// TODO(https://crbug.com/870536): GSU can be generated from
// ResendEventToEmbedder(), make sure |scrolling_touch_action_| has value
// in this case.
if (!scrolling_touch_action_.has_value())
SetTouchAction(cc::kTouchActionAuto);
if (IsYAxisActionDisallowed(scrolling_touch_action_.value())) { if (IsYAxisActionDisallowed(scrolling_touch_action_.value())) {
gesture_event->data.scroll_update.delta_y = 0; gesture_event->data.scroll_update.delta_y = 0;
gesture_event->data.scroll_update.velocity_y = 0; gesture_event->data.scroll_update.velocity_y = 0;
...@@ -91,6 +101,7 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent( ...@@ -91,6 +101,7 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent(
break; break;
case WebInputEvent::kGestureScrollEnd: case WebInputEvent::kGestureScrollEnd:
function_call_sequence_.clear();
DCHECK(touchscreen_scroll_in_progress_); DCHECK(touchscreen_scroll_in_progress_);
touchscreen_scroll_in_progress_ = false; touchscreen_scroll_in_progress_ = false;
ReportGestureEventFiltered(suppress_manipulation_events_); ReportGestureEventFiltered(suppress_manipulation_events_);
...@@ -118,9 +129,6 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent( ...@@ -118,9 +129,6 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent(
// If double tap is disabled, there's no reason for the tap delay. // If double tap is disabled, there's no reason for the tap delay.
case WebInputEvent::kGestureTapUnconfirmed: { case WebInputEvent::kGestureTapUnconfirmed: {
DCHECK_EQ(1, gesture_event->data.tap.tap_count); DCHECK_EQ(1, gesture_event->data.tap.tap_count);
// TODO(https://crbug.com/851644): Make sure the value is properly set.
if (!scrolling_touch_action_.has_value())
SetTouchAction(cc::kTouchActionAuto);
allow_current_double_tap_event_ = (scrolling_touch_action_.value() & allow_current_double_tap_event_ = (scrolling_touch_action_.value() &
cc::kTouchActionDoubleTapZoom) != 0; cc::kTouchActionDoubleTapZoom) != 0;
if (!allow_current_double_tap_event_) { if (!allow_current_double_tap_event_) {
...@@ -144,11 +152,21 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent( ...@@ -144,11 +152,21 @@ FilterGestureEventResult TouchActionFilter::FilterGestureEvent(
if (gesture_event->is_source_touch_event_set_non_blocking) if (gesture_event->is_source_touch_event_set_non_blocking)
SetTouchAction(cc::kTouchActionAuto); SetTouchAction(cc::kTouchActionAuto);
scrolling_touch_action_ = allowed_touch_action_; scrolling_touch_action_ = allowed_touch_action_;
// TODO(https://crbug.com/851644): The value may not set in the case when // TODO(https://crbug.com/851644): Make sure the value is properly set.
// the gesture event is flushed due to touch ack time out after the finger if (!scrolling_touch_action_.has_value()) {
// is lifted up. Make sure the value is properly set. auto index_of_set =
if (!scrolling_touch_action_.has_value()) std::find(std::begin(function_call_sequence_),
std::end(function_call_sequence_), kOnSetTouchActionCall);
auto index_of_reset =
std::find(std::begin(function_call_sequence_),
std::end(function_call_sequence_), kResetTouchActionCall);
if (index_of_set != std::end(function_call_sequence_) &&
index_of_reset != std::end(function_call_sequence_) &&
index_of_reset > index_of_set) {
base::debug::DumpWithoutCrashing();
}
SetTouchAction(cc::kTouchActionAuto); SetTouchAction(cc::kTouchActionAuto);
}
DCHECK(!drop_current_tap_ending_event_); DCHECK(!drop_current_tap_ending_event_);
break; break;
...@@ -175,6 +193,8 @@ bool TouchActionFilter::FilterManipulationEventAndResetState() { ...@@ -175,6 +193,8 @@ bool TouchActionFilter::FilterManipulationEventAndResetState() {
} }
void TouchActionFilter::OnSetTouchAction(cc::TouchAction touch_action) { void TouchActionFilter::OnSetTouchAction(cc::TouchAction touch_action) {
if (touch_action != cc::kTouchActionAuto)
function_call_sequence_.push_back(kOnSetTouchActionCall);
// TODO(https://crbug.com/849819): add a DCHECK for // TODO(https://crbug.com/849819): add a DCHECK for
// |has_touch_event_handler_|. // |has_touch_event_handler_|.
// For multiple fingers, we take the intersection of the touch actions for // For multiple fingers, we take the intersection of the touch actions for
...@@ -233,6 +253,7 @@ void TouchActionFilter::ResetTouchAction() { ...@@ -233,6 +253,7 @@ void TouchActionFilter::ResetTouchAction() {
// their begin event(s) suppressed will be suppressed until the next // their begin event(s) suppressed will be suppressed until the next
// sequenceo. // sequenceo.
if (has_touch_event_handler_) { if (has_touch_event_handler_) {
function_call_sequence_.push_back(kResetTouchActionCall);
allowed_touch_action_.reset(); allowed_touch_action_.reset();
white_listed_touch_action_.reset(); white_listed_touch_action_.reset();
} else { } else {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "cc/input/touch_action.h" #include "cc/input/touch_action.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "third_party/blink/public/platform/web_input_event.h"
namespace blink { namespace blink {
class WebGestureEvent; class WebGestureEvent;
...@@ -30,6 +31,7 @@ enum class FilterGestureEventResult { ...@@ -30,6 +31,7 @@ enum class FilterGestureEventResult {
class CONTENT_EXPORT TouchActionFilter { class CONTENT_EXPORT TouchActionFilter {
public: public:
TouchActionFilter(); TouchActionFilter();
~TouchActionFilter();
// Returns kFilterGestureEventFiltered if the supplied gesture event should be // Returns kFilterGestureEventFiltered if the supplied gesture event should be
// dropped based on the current touch-action state. Otherwise returns // dropped based on the current touch-action state. Otherwise returns
...@@ -111,6 +113,15 @@ class CONTENT_EXPORT TouchActionFilter { ...@@ -111,6 +113,15 @@ class CONTENT_EXPORT TouchActionFilter {
// Whitelisted touch action received from the compositor. // Whitelisted touch action received from the compositor.
base::Optional<cc::TouchAction> white_listed_touch_action_; base::Optional<cc::TouchAction> white_listed_touch_action_;
// DEBUG ONLY! Record the sequence of function calls, cleared at GSE. When it
// is GSB and |scrolling_touch_action_| has no value, check this sequence.
enum FunctionCalls {
kOnSetTouchActionCall,
kResetTouchActionCall,
};
std::vector<FunctionCalls> function_call_sequence_;
DISALLOW_COPY_AND_ASSIGN(TouchActionFilter); DISALLOW_COPY_AND_ASSIGN(TouchActionFilter);
}; };
......
...@@ -1205,6 +1205,11 @@ void RenderWidgetHostInputEventRouter::DispatchTouchscreenGestureEvent( ...@@ -1205,6 +1205,11 @@ void RenderWidgetHostInputEventRouter::DispatchTouchscreenGestureEvent(
map_size_key, map_size_key,
base::StringPrintf("%u", static_cast<int>(owner_map_.size()))); base::StringPrintf("%u", static_cast<int>(owner_map_.size())));
if (events_being_flushed_) {
touchscreen_gesture_target_.target->host()
->input_router()
->ForceSetTouchActionAuto();
}
touchscreen_gesture_target_.target->ProcessGestureEvent(event, latency); touchscreen_gesture_target_.target->ProcessGestureEvent(event, latency);
if (gesture_event.GetType() == blink::WebInputEvent::kGestureFlingStart) if (gesture_event.GetType() == blink::WebInputEvent::kGestureFlingStart)
...@@ -1376,6 +1381,11 @@ RenderWidgetHostInputEventRouter::FindTargetSynchronously( ...@@ -1376,6 +1381,11 @@ RenderWidgetHostInputEventRouter::FindTargetSynchronously(
return RenderWidgetTargetResult(); return RenderWidgetTargetResult();
} }
void RenderWidgetHostInputEventRouter::SetEventsBeingFlushed(
bool events_being_flushed) {
events_being_flushed_ = events_being_flushed;
}
void RenderWidgetHostInputEventRouter::DispatchEventToTarget( void RenderWidgetHostInputEventRouter::DispatchEventToTarget(
RenderWidgetHostViewBase* root_view, RenderWidgetHostViewBase* root_view,
RenderWidgetHostViewBase* target, RenderWidgetHostViewBase* target,
......
...@@ -280,6 +280,9 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter ...@@ -280,6 +280,9 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
const blink::WebInputEvent& event, const blink::WebInputEvent& event,
const ui::LatencyInfo& latency, const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location) override; const base::Optional<gfx::PointF>& target_location) override;
// Notify whether the events in the queue are being flushed due to touch ack
// timeout, or the flushing has completed.
void SetEventsBeingFlushed(bool events_being_flushed) override;
FrameSinkIdOwnerMap owner_map_; FrameSinkIdOwnerMap owner_map_;
TargetMap touchscreen_gesture_target_map_; TargetMap touchscreen_gesture_target_map_;
...@@ -319,6 +322,7 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter ...@@ -319,6 +322,7 @@ class CONTENT_EXPORT RenderWidgetHostInputEventRouter
std::unique_ptr<RenderWidgetTargeter> event_targeter_; std::unique_ptr<RenderWidgetTargeter> event_targeter_;
bool use_viz_hit_test_ = false; bool use_viz_hit_test_ = false;
bool events_being_flushed_ = false;
std::unique_ptr<TouchEmulator> touch_emulator_; std::unique_ptr<TouchEmulator> touch_emulator_;
......
...@@ -215,6 +215,7 @@ void RenderWidgetTargeter::QueryClient( ...@@ -215,6 +215,7 @@ void RenderWidgetTargeter::QueryClient(
} }
void RenderWidgetTargeter::FlushEventQueue() { void RenderWidgetTargeter::FlushEventQueue() {
bool events_being_flushed = false;
while (!request_in_flight_ && !requests_.empty()) { while (!request_in_flight_ && !requests_.empty()) {
auto request = std::move(requests_.front()); auto request = std::move(requests_.front());
requests_.pop(); requests_.pop();
...@@ -224,9 +225,16 @@ void RenderWidgetTargeter::FlushEventQueue() { ...@@ -224,9 +225,16 @@ void RenderWidgetTargeter::FlushEventQueue() {
continue; continue;
} }
request.tracker->Stop(); request.tracker->Stop();
// Only notify the delegate once that the current event queue is being
// flushed. Once all the events are flushed, notify the delegate again.
if (!events_being_flushed) {
delegate_->SetEventsBeingFlushed(true);
events_being_flushed = true;
}
FindTargetAndDispatch(request.root_view.get(), *request.event, FindTargetAndDispatch(request.root_view.get(), *request.event,
request.latency); request.latency);
} }
delegate_->SetEventsBeingFlushed(false);
} }
void RenderWidgetTargeter::FoundFrameSinkId( void RenderWidgetTargeter::FoundFrameSinkId(
......
...@@ -70,6 +70,8 @@ class RenderWidgetTargeter { ...@@ -70,6 +70,8 @@ class RenderWidgetTargeter {
const ui::LatencyInfo& latency, const ui::LatencyInfo& latency,
const base::Optional<gfx::PointF>& target_location) = 0; const base::Optional<gfx::PointF>& target_location) = 0;
virtual void SetEventsBeingFlushed(bool events_being_flushed) = 0;
virtual RenderWidgetHostViewBase* FindViewFromFrameSinkId( virtual RenderWidgetHostViewBase* FindViewFromFrameSinkId(
const viz::FrameSinkId& frame_sink_id) const = 0; const viz::FrameSinkId& frame_sink_id) const = 0;
}; };
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "components/viz/common/features.h" #include "components/viz/common/features.h"
#include "content/browser/compositor/surface_utils.h" #include "content/browser/compositor/surface_utils.h"
#include "content/browser/renderer_host/cursor_manager.h" #include "content/browser/renderer_host/cursor_manager.h"
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "content/browser/renderer_host/input/synthetic_tap_gesture.h" #include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
#include "content/browser/renderer_host/input/touch_emulator.h" #include "content/browser/renderer_host/input/touch_emulator.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h" #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
...@@ -1314,6 +1315,105 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest, ...@@ -1314,6 +1315,105 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest,
RunTest(TouchActionBubbling); RunTest(TouchActionBubbling);
} }
#if defined(OS_ANDROID) || defined(USE_AURA)
namespace {
// This function is used in TouchActionAckTimeout and
// SubframeGestureEventRouting, which is defined either under Android or Aura.
void OnSyntheticGestureCompleted(scoped_refptr<MessageLoopRunner> runner,
SyntheticGesture::Result result) {
EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
runner->Quit();
}
#if defined(OS_ANDROID)
void GiveItSomeTime(int t) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromMilliseconds(t));
run_loop.Run();
}
#endif // defined(OS_ANDROID)
} // namespace
#endif // defined(OS_ANDROID) || defined(USE_AURA)
// Regression test for https://crbug.com/851644. The test passes as long as it
// doesn't crash.
// Touch action ack timeout is enabled on Android only.
#if defined(OS_ANDROID)
IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
TouchActionAckTimeout) {
GURL main_url(
embedded_test_server()->GetURL("/frame_tree/page_with_janky_frame.html"));
ASSERT_TRUE(NavigateToURL(shell(), main_url));
FrameTreeNode* root = web_contents()->GetFrameTree()->root();
ASSERT_EQ(1U, root->child_count());
GURL frame_url(embedded_test_server()->GetURL(
"baz.com", "/page_with_touch_start_janking_main_thread.html"));
auto* child_frame_host = root->child_at(0)->current_frame_host();
RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
root->current_frame_host()->GetRenderWidgetHost()->GetView());
RenderWidgetHostViewChildFrame* rwhv_child =
static_cast<RenderWidgetHostViewChildFrame*>(
child_frame_host->GetRenderWidgetHost()->GetView());
WaitForHitTestDataOrChildSurfaceReady(child_frame_host);
// Compute the point so that the gesture event can target the child frame.
const gfx::Rect root_bounds = rwhv_root->GetViewBounds();
const gfx::Rect child_bounds = rwhv_child->GetViewBounds();
RenderFrameSubmissionObserver render_frame_submission_observer(
shell()->web_contents());
const float page_scale_factor =
render_frame_submission_observer.LastRenderFrameMetadata()
.page_scale_factor;
const gfx::PointF point_in_child(
(child_bounds.x() - root_bounds.x() + 25) * page_scale_factor,
(child_bounds.y() - root_bounds.y() + 25) * page_scale_factor);
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor = gfx::PointF(point_in_child.x(), point_in_child.y());
params.distances.push_back(gfx::Vector2dF(0, -10));
// Make this scroll slow so that the second scroll will be queued even before
// this one ends.
params.speed_in_pixels_s = 1000;
std::unique_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner();
RenderWidgetHostImpl* render_widget_host =
root->current_frame_host()->GetRenderWidgetHost();
render_widget_host->QueueSyntheticGesture(
std::move(gesture), base::BindOnce(OnSyntheticGestureCompleted, runner));
// The first gesture takes 100ms, so wait for 120ms to ensure that it has
// finished.
runner->Run();
GiveItSomeTime(120);
SyntheticSmoothScrollGestureParams params2;
params2.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params2.anchor = gfx::PointF(point_in_child.x(), point_in_child.y());
params2.distances.push_back(gfx::Vector2dF(0, -10));
params2.speed_in_pixels_s = 100000;
std::unique_ptr<SyntheticSmoothScrollGesture> gesture2(
new SyntheticSmoothScrollGesture(params2));
render_widget_host->QueueSyntheticGesture(
std::move(gesture2), base::BindOnce(OnSyntheticGestureCompleted, runner));
runner->Run();
runner = nullptr;
// Give enough time to make sure all gesture are flushed and handled.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(2500));
run_loop.Run();
}
#endif // defined(OS_ANDROID)
#if defined(USE_AURA) || defined(OS_ANDROID) #if defined(USE_AURA) || defined(OS_ANDROID)
// When unconsumed scrolls in a child bubble to the root and start an // When unconsumed scrolls in a child bubble to the root and start an
...@@ -3048,17 +3148,6 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ...@@ -3048,17 +3148,6 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
render_widget_host->input_router()->AllowedTouchAction()); render_widget_host->input_router()->AllowedTouchAction());
} }
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();
}
} // anonymous namespace
// https://crbug.com/592320 // https://crbug.com/592320
IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
DISABLED_SubframeGestureEventRouting) { DISABLED_SubframeGestureEventRouting) {
......
<!DOCTYPE html>
<style>
.container {
touch-action: pan-y;
}
iframe {
position:absolute;
top: 50px;
left: 50px;
width: 100px;
height: 100px;
}
</style>
<html>
<body>
<div class="container">
<iframe src="/cross-site/baz.com/page_with_touch_start_janking_main_thread.html"></iframe>
</div>
This page contains a positioned cross-origin iframe.
</body>
</html>
<html>
<style>
#janktest{
width: 1000px;
height: 10000px;
}
</style>
<body onload='setup()'>
<div id="janktest"></div>
</body>
<script>
function setup() {
janktest.ontouchstart = function() {
var end = performance.now() + 1200;
while (performance.now() < end) ;
};
}
</script>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment