Commit bfab3d02 authored by tdresser@chromium.org's avatar tdresser@chromium.org

OverscrollController consumes scroll updates only during gesture-nav.

Previously the OverscrollController consume all scroll updates, which
broke touchmove throttling.

The test ensures that touchmove throttling interacts correctly we
gesture-nav.

BUG=403329
TEST=WebContentsViewAuraTest.OverscrollNavigationTouchThrottling

Review URL: https://codereview.chromium.org/478023002

Cr-Commit-Position: refs/heads/master@{#290740}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290740 0039d316-1c4b-4281-b951-d872f2087c98
parent b33f9614
......@@ -212,20 +212,18 @@ bool OverscrollController::ProcessEventForOverscroll(
static_cast<const blink::WebMouseWheelEvent&>(event);
if (!wheel.hasPreciseScrollingDeltas)
break;
ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
wheel.deltaY * wheel.accelerationRatioY,
wheel.type);
event_processed = true;
event_processed =
ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
wheel.deltaY * wheel.accelerationRatioY,
wheel.type);
break;
}
case blink::WebInputEvent::GestureScrollUpdate: {
const blink::WebGestureEvent& gesture =
static_cast<const blink::WebGestureEvent&>(event);
ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
gesture.data.scrollUpdate.deltaY,
gesture.type);
event_processed = true;
event_processed = ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
gesture.data.scrollUpdate.deltaY,
gesture.type);
break;
}
case blink::WebInputEvent::GestureFlingStart: {
......@@ -263,7 +261,7 @@ bool OverscrollController::ProcessEventForOverscroll(
return event_processed;
}
void OverscrollController::ProcessOverscroll(float delta_x,
bool OverscrollController::ProcessOverscroll(float delta_x,
float delta_y,
blink::WebInputEvent::Type type) {
if (scroll_state_ != STATE_CONTENT_SCROLLING)
......@@ -279,7 +277,7 @@ void OverscrollController::ProcessOverscroll(float delta_x,
if (fabs(overscroll_delta_x_) <= horiz_threshold &&
fabs(overscroll_delta_y_) <= vert_threshold) {
SetOverscrollMode(OVERSCROLL_NONE);
return;
return true;
}
// Compute the current overscroll direction. If the direction is different
......@@ -306,7 +304,7 @@ void OverscrollController::ProcessOverscroll(float delta_x,
SetOverscrollMode(OVERSCROLL_NONE);
if (overscroll_mode_ == OVERSCROLL_NONE)
return;
return false;
// Tell the delegate about the overscroll update so that it can update
// the display accordingly (e.g. show history preview etc.).
......@@ -332,8 +330,9 @@ void OverscrollController::ProcessOverscroll(float delta_x,
} else {
delegate_delta_y = 0.f;
}
delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
}
return false;
}
void OverscrollController::CompleteAction() {
......
......@@ -91,8 +91,9 @@ class OverscrollController {
// Processes horizontal overscroll. This can update both the overscroll mode
// and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_|
// and |overscroll_delta_y_|).
void ProcessOverscroll(float delta_x,
// and |overscroll_delta_y_|). Returns true if overscroll was handled by the
// delegate.
bool ProcessOverscroll(float delta_x,
float delta_y,
blink::WebInputEvent::Type event_type);
......
......@@ -23,8 +23,9 @@ class OverscrollControllerDelegate {
// events will only be processed if the visible bounds are non-empty.
virtual gfx::Rect GetVisibleBounds() const = 0;
// This is called for each update in the overscroll amount.
virtual void OnOverscrollUpdate(float delta_x, float delta_y) = 0;
// This is called for each update in the overscroll amount. Returns true if
// the delegate consumed the event.
virtual bool OnOverscrollUpdate(float delta_x, float delta_y) = 0;
// This is called when the overscroll completes.
virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) = 0;
......
......@@ -123,9 +123,10 @@ class TestOverscrollDelegate : public OverscrollControllerDelegate {
return view_->IsShowing() ? view_->GetViewBounds() : gfx::Rect();
}
virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE {
virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE {
delta_x_ = delta_x;
delta_y_ = delta_y;
return true;
}
virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE {
......@@ -2534,9 +2535,10 @@ TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) {
EXPECT_EQ(0U, sink_->message_count());
// Send another update event, but in the reverse direction. The overscroll
// controller will consume the event, and reset the overscroll mode.
// controller will not consume the event, because it is not triggering
// gesture-nav.
SimulateGestureScrollUpdateEvent(-260, 0, 0);
EXPECT_EQ(0U, sink_->message_count());
EXPECT_EQ(1U, sink_->message_count());
EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
// Since the overscroll mode has been reset, the next scroll update events
......@@ -2546,6 +2548,33 @@ TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) {
EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
}
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
OverscrollDirectionChangeMouseWheel) {
SetUpOverscrollEnvironment();
// Send wheel event and receive ack as not consumed.
SimulateWheelEvent(125, -5, 0, true);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
EXPECT_EQ(0U, sink_->message_count());
// Send another wheel event, but in the reverse direction. The overscroll
// controller will not consume the event, because it is not triggering
// gesture-nav.
SimulateWheelEvent(-260, 0, 0, true);
EXPECT_EQ(1U, sink_->message_count());
EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
// Since the overscroll mode has been reset, the next wheel event should reach
// the renderer.
SimulateWheelEvent(-20, 0, 0, true);
EXPECT_EQ(1U, sink_->message_count());
EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
}
// Tests that if a mouse-move event completes the overscroll gesture, future
// move events do reach the renderer.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) {
......
......@@ -145,9 +145,9 @@ void GestureNavSimple::CompleteGestureAnimation() {
ApplyEffectsAndDestroy(arrow_->transform(), 0.f);
}
void GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
bool GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
if (!arrow_)
return;
return false;
CHECK_GT(completion_threshold_, 0.f);
CHECK_GE(delta_x, 0.f);
double complete = std::min(1.f, delta_x / completion_threshold_);
......@@ -157,14 +157,15 @@ void GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
0.f);
arrow_->SetTransform(transform);
arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f));
return true;
}
gfx::Rect GestureNavSimple::GetVisibleBounds() const {
return web_contents_->GetNativeView()->bounds();
}
void GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) {
ApplyEffectsForDelta(std::abs(delta_x) + 50.f);
bool GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) {
return ApplyEffectsForDelta(std::abs(delta_x) + 50.f);
}
void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) {
......
......@@ -32,11 +32,12 @@ class GestureNavSimple : public OverscrollControllerDelegate {
void ApplyEffectsAndDestroy(const gfx::Transform& transform, float opacity);
void AbortGestureAnimation();
void CompleteGestureAnimation();
void ApplyEffectsForDelta(float delta_x);
bool ApplyEffectsForDelta(float delta_x);
// OverscrollControllerDelegate:
virtual gfx::Rect GetVisibleBounds() const OVERRIDE;
virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE;
// Returns true if the scroll update was consumed.
virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE;
virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE;
virtual void OnOverscrollModeChange(OverscrollMode old_mode,
OverscrollMode new_mode) OVERRIDE;
......
......@@ -1281,9 +1281,9 @@ gfx::Rect WebContentsViewAura::GetVisibleBounds() const {
return rwhv->GetViewBounds();
}
void WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) {
bool WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) {
if (current_overscroll_gesture_ == OVERSCROLL_NONE)
return;
return false;
aura::Window* target = GetWindowToAnimateForOverscroll();
gfx::Vector2d translate = GetTranslationForOverscroll(delta_x, delta_y);
......@@ -1298,6 +1298,7 @@ void WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) {
}
OverscrollUpdateForWebContentsDelegate(translate.y());
return !translate.IsZero();
}
void WebContentsViewAura::OnOverscrollComplete(OverscrollMode mode) {
......
......@@ -139,7 +139,7 @@ class WebContentsViewAura
// Overridden from OverscrollControllerDelegate:
virtual gfx::Rect GetVisibleBounds() const OVERRIDE;
virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE;
virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE;
virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE;
virtual void OnOverscrollModeChange(OverscrollMode old_mode,
OverscrollMode new_mode) OVERRIDE;
......
......@@ -18,7 +18,10 @@
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_switches.h"
......@@ -32,9 +35,26 @@
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_switches.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
namespace {
// TODO(tdresser): Find a way to avoid sleeping like this. See crbug.com/405282
// for details.
void GiveItSomeTime() {
base::RunLoop run_loop;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
run_loop.QuitClosure(),
base::TimeDelta::FromMillisecondsD(10));
run_loop.Run();
}
} //namespace
namespace content {
// This class keeps track of the RenderViewHost whose screenshot was captured.
......@@ -127,6 +147,55 @@ class NavigationWatcher : public WebContentsObserver {
DISALLOW_COPY_AND_ASSIGN(NavigationWatcher);
};
class InputEventMessageFilterWaitsForAcks : public BrowserMessageFilter {
public:
InputEventMessageFilterWaitsForAcks()
: BrowserMessageFilter(InputMsgStart),
type_(blink::WebInputEvent::Undefined),
state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
void WaitForAck(blink::WebInputEvent::Type type) {
base::RunLoop run_loop;
base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure());
base::AutoReset<blink::WebInputEvent::Type> reset_type(&type_, type);
run_loop.Run();
}
InputEventAckState last_ack_state() const { return state_; }
protected:
virtual ~InputEventMessageFilterWaitsForAcks() {}
private:
void ReceivedEventAck(blink::WebInputEvent::Type type,
InputEventAckState state) {
if (type_ == type) {
state_ = state;
quit_.Run();
}
}
// BrowserMessageFilter:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) {
InputHostMsg_HandleInputEvent_ACK::Param params;
InputHostMsg_HandleInputEvent_ACK::Read(&message, &params);
blink::WebInputEvent::Type type = params.a.type;
InputEventAckState ack = params.a.state;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&InputEventMessageFilterWaitsForAcks::ReceivedEventAck,
this, type, ack));
}
return false;
}
base::Closure quit_;
blink::WebInputEvent::Type type_;
InputEventAckState state_;
DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilterWaitsForAcks);
};
class WebContentsViewAuraTest : public ContentBrowserTest {
public:
WebContentsViewAuraTest()
......@@ -156,6 +225,11 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
controller->SetScreenshotManager(screenshot_manager_);
}
virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
cmd->AppendSwitchASCII(switches::kTouchEvents,
switches::kTouchEventsEnabled);
}
void TestOverscrollNavigation(bool touch_handler) {
ASSERT_NO_FATAL_FAILURE(
StartTestWithPage("files/overscroll_navigation.html"));
......@@ -256,14 +330,62 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
return index;
}
int ExecuteScriptAndExtractInt(const std::string& script) {
int value = 0;
EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
shell()->web_contents(),
"domAutomationController.send(" + script + ")",
&value));
return value;
}
RenderViewHost* GetRenderViewHost() const {
RenderViewHost* const rvh = shell()->web_contents()->GetRenderViewHost();
CHECK(rvh);
return rvh;
}
RenderWidgetHostImpl* GetRenderWidgetHost() const {
RenderWidgetHostImpl* const rwh =
RenderWidgetHostImpl::From(shell()
->web_contents()
->GetRenderWidgetHostView()
->GetRenderWidgetHost());
CHECK(rwh);
return rwh;
}
RenderWidgetHostViewBase* GetRenderWidgetHostView() const {
return static_cast<RenderWidgetHostViewBase*>(
GetRenderViewHost()->GetView());
}
InputEventMessageFilterWaitsForAcks* filter() {
return filter_.get();
}
void WaitAFrame() {
uint32 frame = GetRenderWidgetHostView()->RendererFrameNumber();
while (!GetRenderWidgetHost()->ScheduleComposite())
GiveItSomeTime();
while (GetRenderWidgetHostView()->RendererFrameNumber() == frame)
GiveItSomeTime();
}
protected:
ScreenshotTracker* screenshot_manager() { return screenshot_manager_; }
void set_min_screenshot_interval(int interval_ms) {
screenshot_manager_->SetScreenshotInterval(interval_ms);
}
void AddInputEventMessageFilter() {
filter_ = new InputEventMessageFilterWaitsForAcks();
GetRenderWidgetHost()->GetProcess()->AddFilter(filter_);
}
private:
ScreenshotTracker* screenshot_manager_;
scoped_refptr<InputEventMessageFilterWaitsForAcks> filter_;
DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest);
};
......@@ -747,4 +869,100 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, WebContentsViewReparent) {
EXPECT_TRUE(rwhva->has_snapped_to_boundary());
}
// Flaky on Windows, likely for the same reason as other flaky overscroll tests.
// http://crbug.com/305722
#if defined(OS_WIN)
#define MAYBE_OverscrollNavigationTouchThrottling \
DISABLED_OverscrollNavigationTouchThrottling
#else
#define MAYBE_OverscrollNavigationTouchThrottling \
OverscrollNavigationTouchThrottling
#endif
// Tests that touch moves are not throttled when performing a scroll gesture on
// a non-scrollable area, except during gesture-nav.
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
MAYBE_OverscrollNavigationTouchThrottling) {
ASSERT_NO_FATAL_FAILURE(
StartTestWithPage("files/overscroll_navigation.html"));
AddInputEventMessageFilter();
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
aura::Window* content = web_contents->GetContentNativeView();
gfx::Rect bounds = content->GetBoundsInRootWindow();
const int dx = 20;
ExecuteSyncJSFunction(web_contents->GetMainFrame(),
"install_touchmove_handler()");
WaitAFrame();
for (int navigated = 0; navigated <= 1; ++navigated) {
if (navigated) {
ExecuteSyncJSFunction(web_contents->GetMainFrame(), "navigate_next()");
ExecuteSyncJSFunction(web_contents->GetMainFrame(),
"reset_touchmove_count()");
}
// Send touch press.
SyntheticWebTouchEvent touch;
touch.PressPoint(bounds.x() + 2, bounds.y() + 10);
GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch,
ui::LatencyInfo());
filter()->WaitForAck(blink::WebInputEvent::TouchStart);
// Assert on the ack, because we'll end up waiting for acks that will never
// come if this is not true.
ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
// Send first touch move, and then a scroll begin.
touch.MovePoint(0, bounds.x() + 20 + 1 * dx, bounds.y() + 100);
GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch,
ui::LatencyInfo());
filter()->WaitForAck(blink::WebInputEvent::TouchMove);
ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
blink::WebGestureEvent scroll_begin =
SyntheticWebGestureEventBuilder::BuildScrollBegin(1, 1);
GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo(
scroll_begin, ui::LatencyInfo());
// Scroll begin ignores ack disposition, so don't wait for the ack.
// GiveItSomeTime();
WaitAFrame();
// First touchmove already sent, start at 2.
for (int i = 2; i <= 10; ++i) {
// Send a touch move, followed by a scroll update
touch.MovePoint(0, bounds.x() + 20 + i * dx, bounds.y() + 100);
GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(
touch, ui::LatencyInfo());
WaitAFrame();
blink::WebGestureEvent scroll_update =
SyntheticWebGestureEventBuilder::BuildScrollUpdate(dx, 5, 0);
GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo(
scroll_update, ui::LatencyInfo());
WaitAFrame();
}
touch.ReleasePoint(0);
GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch,
ui::LatencyInfo());
WaitAFrame();
blink::WebGestureEvent scroll_end;
scroll_end.type = blink::WebInputEvent::GestureScrollEnd;
GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo(
scroll_end, ui::LatencyInfo());
WaitAFrame();
if (!navigated)
EXPECT_EQ(10, ExecuteScriptAndExtractInt("touchmoveCount"));
else
EXPECT_GT(10, ExecuteScriptAndExtractInt("touchmoveCount"));
}
}
} // namespace content
......@@ -28,6 +28,8 @@
<script>
window.touchmoveCount = 0;
function get_current() {
if (location.hash.length == 0)
return 0;
......@@ -49,10 +51,22 @@ function navigate_prev() {
function touch_start_handler() {
}
function touch_move_handler() {
window.touchmoveCount++;
}
function install_touch_handler() {
document.addEventListener('touchstart', touch_start_handler);
}
function install_touchmove_handler() {
document.addEventListener('touchmove', touch_move_handler);
}
function reset_touchmove_count() {
window.touchmoveCount = 0;
}
function uninstall_touch_handler() {
document.removeEventListener('touchstart', touch_start_handler);
}
......
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