blob: da8a18cecaf9e1b34016c612f07ac35af0ac53a1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/web_gesture_curve_impl.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "third_party/WebKit/public/platform/WebFloatSize.h"
#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
#include "ui/events/gestures/fling_curve.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
#if defined(OS_ANDROID)
#include "ui/events/android/scroller.h"
#endif
using blink::WebGestureCurve;
namespace content {
namespace {
scoped_ptr<ui::GestureCurve> CreateDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity) {
DCHECK(!initial_velocity.IsZero());
#if defined(OS_ANDROID)
auto scroller = make_scoped_ptr(new ui::Scroller(ui::Scroller::Config()));
scroller->Fling(0,
0,
initial_velocity.x(),
initial_velocity.y(),
INT_MIN,
INT_MAX,
INT_MIN,
INT_MAX,
base::TimeTicks());
return scroller.Pass();
#else
return make_scoped_ptr(
new ui::FlingCurve(initial_velocity, base::TimeTicks()));
#endif
}
} // namespace
// static
scoped_ptr<WebGestureCurve> WebGestureCurveImpl::CreateFromDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity,
const gfx::Vector2dF& initial_offset,
bool on_main_thread) {
return scoped_ptr<WebGestureCurve>(new WebGestureCurveImpl(
CreateDefaultPlatformCurve(initial_velocity), initial_offset,
on_main_thread ? ThreadType::MAIN : ThreadType::IMPL));
}
// static
scoped_ptr<WebGestureCurve> WebGestureCurveImpl::CreateFromUICurveForTesting(
scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset) {
return scoped_ptr<WebGestureCurve>(
new WebGestureCurveImpl(curve.Pass(), initial_offset, ThreadType::TEST));
}
WebGestureCurveImpl::WebGestureCurveImpl(scoped_ptr<ui::GestureCurve> curve,
const gfx::Vector2dF& initial_offset,
ThreadType animating_thread_type)
: curve_(curve.Pass()),
last_offset_(initial_offset),
animating_thread_type_(animating_thread_type),
ticks_since_first_animate_(0),
first_animate_time_(0),
last_animate_time_(0) {
}
WebGestureCurveImpl::~WebGestureCurveImpl() {
if (ticks_since_first_animate_ <= 1)
return;
if (last_animate_time_ <= first_animate_time_)
return;
switch (animating_thread_type_) {
case ThreadType::MAIN:
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Event.Frequency.Renderer.FlingAnimate",
gfx::ToRoundedInt(ticks_since_first_animate_ /
(last_animate_time_ - first_animate_time_)),
1, 240, 120);
break;
case ThreadType::IMPL:
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Event.Frequency.RendererImpl.FlingAnimate",
gfx::ToRoundedInt(ticks_since_first_animate_ /
(last_animate_time_ - first_animate_time_)),
1, 240, 120);
break;
case ThreadType::TEST:
break;
}
}
bool WebGestureCurveImpl::apply(double time,
blink::WebGestureCurveTarget* target) {
// If the fling has yet to start, simply return and report true to prevent
// fling termination.
if (time <= 0)
return true;
if (!first_animate_time_) {
first_animate_time_ = last_animate_time_ = time;
} else if (time != last_animate_time_) {
// Animation can occur multiple times a frame, but with the same timestamp.
// Suppress recording of such redundant animate calls, avoiding artificially
// inflated FPS computation.
last_animate_time_ = time;
++ticks_since_first_animate_;
}
const base::TimeTicks time_ticks =
base::TimeTicks() + base::TimeDelta::FromSecondsD(time);
gfx::Vector2dF offset, velocity;
bool still_active =
curve_->ComputeScrollOffset(time_ticks, &offset, &velocity);
gfx::Vector2dF delta = offset - last_offset_;
last_offset_ = offset;
// As successive timestamps can be arbitrarily close (but monotonic!), don't
// assume that a zero delta means the curve has terminated.
if (delta.IsZero())
return still_active;
// scrollBy() could delete this curve if the animation is over, so don't touch
// any member variables after making that call.
bool did_scroll = target->scrollBy(blink::WebFloatSize(delta),
blink::WebFloatSize(velocity));
return did_scroll && still_active;
}
} // namespace content