Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / android / overscroll_controller_android.cc
blob857d2f64d98a10100d76a4df76fcb8e3ccc48f7d
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/android/overscroll_controller_android.h"
7 #include "base/android/build_info.h"
8 #include "base/command_line.h"
9 #include "cc/layers/layer.h"
10 #include "cc/output/compositor_frame_metadata.h"
11 #include "content/browser/android/content_view_core_impl.h"
12 #include "content/browser/android/edge_effect.h"
13 #include "content/browser/android/edge_effect_l.h"
14 #include "content/common/input/did_overscroll_params.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/user_metrics.h"
17 #include "content/public/common/content_switches.h"
18 #include "third_party/WebKit/public/web/WebInputEvent.h"
19 #include "ui/android/resources/resource_manager.h"
20 #include "ui/android/window_android.h"
21 #include "ui/android/window_android_compositor.h"
22 #include "ui/base/l10n/l10n_util_android.h"
24 namespace content {
25 namespace {
27 // Used for conditional creation of EdgeEffect types for the overscroll glow.
28 const int kAndroidLSDKVersion = 21;
30 // If the glow effect alpha is greater than this value, the refresh effect will
31 // be suppressed. This value was experimentally determined to provide a
32 // reasonable balance between avoiding accidental refresh activation and
33 // minimizing the wait required to refresh after the glow has been triggered.
34 const float kMinGlowAlphaToDisableRefreshOnL = 0.085f;
36 bool IsAndroidLOrNewer() {
37 static bool android_l_or_newer =
38 base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion;
39 return android_l_or_newer;
42 // Suppressing refresh detection when the glow is still animating prevents
43 // visual confusion and accidental activation after repeated scrolls.
44 float MinGlowAlphaToDisableRefresh() {
45 // Only the L effect is guaranteed to be both sufficiently brief and prominent
46 // to provide a meaningful "wait" signal. The refresh effect on previous
47 // Android releases can be quite faint, depending on the OEM-supplied
48 // overscroll resources, and lasts nearly twice as long.
49 if (IsAndroidLOrNewer())
50 return kMinGlowAlphaToDisableRefreshOnL;
52 // Any value greater than 1 effectively prevents the glow effect from ever
53 // suppressing the refresh effect.
54 return 1.01f;
57 scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect(
58 ui::ResourceManager* resource_manager,
59 float dpi_scale) {
60 DCHECK(resource_manager);
61 if (IsAndroidLOrNewer())
62 return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager));
64 return scoped_ptr<EdgeEffectBase>(
65 new EdgeEffect(resource_manager, dpi_scale));
68 scoped_ptr<OverscrollGlow> CreateGlowEffect(OverscrollGlowClient* client,
69 float dpi_scale) {
70 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
71 switches::kDisableOverscrollEdgeEffect)) {
72 return nullptr;
75 return make_scoped_ptr(new OverscrollGlow(client));
78 scoped_ptr<OverscrollRefresh> CreateRefreshEffect(
79 OverscrollRefreshHandler* handler) {
80 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
81 switches::kDisablePullToRefreshEffect)) {
82 return nullptr;
85 return make_scoped_ptr(new OverscrollRefresh(handler));
88 } // namespace
90 OverscrollControllerAndroid::OverscrollControllerAndroid(
91 ContentViewCoreImpl* content_view_core)
92 : compositor_(content_view_core->GetWindowAndroid()->GetCompositor()),
93 dpi_scale_(content_view_core->GetDpiScale()),
94 enabled_(true),
95 glow_effect_(CreateGlowEffect(this, dpi_scale_)),
96 refresh_effect_(CreateRefreshEffect(content_view_core)) {
97 DCHECK(compositor_);
100 OverscrollControllerAndroid::~OverscrollControllerAndroid() {
103 bool OverscrollControllerAndroid::WillHandleGestureEvent(
104 const blink::WebGestureEvent& event) {
105 if (!enabled_)
106 return false;
108 if (!refresh_effect_)
109 return false;
111 // Suppress refresh detection if the glow effect is still prominent.
112 if (glow_effect_ && glow_effect_->IsActive()) {
113 if (glow_effect_->GetVisibleAlpha() > MinGlowAlphaToDisableRefresh())
114 return false;
117 bool handled = false;
118 switch (event.type) {
119 case blink::WebInputEvent::GestureScrollBegin:
120 refresh_effect_->OnScrollBegin();
121 break;
123 case blink::WebInputEvent::GestureScrollUpdate: {
124 gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX,
125 event.data.scrollUpdate.deltaY);
126 scroll_delta.Scale(dpi_scale_);
127 handled = refresh_effect_->WillHandleScrollUpdate(scroll_delta);
128 } break;
130 case blink::WebInputEvent::GestureScrollEnd:
131 refresh_effect_->OnScrollEnd(gfx::Vector2dF());
132 break;
134 case blink::WebInputEvent::GestureFlingStart: {
135 if (refresh_effect_->IsActive()) {
136 gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX,
137 event.data.flingStart.velocityY);
138 scroll_velocity.Scale(dpi_scale_);
139 refresh_effect_->OnScrollEnd(scroll_velocity);
140 // TODO(jdduke): Figure out a cleaner way of suppressing a fling.
141 // It's important that the any downstream code sees a scroll-ending
142 // event (in this case GestureFlingStart) if it has seen a scroll begin.
143 // Thus, we cannot simply consume the fling. Changing the event type to
144 // a GestureScrollEnd might work in practice, but could lead to
145 // unexpected results. For now, simply truncate the fling velocity, but
146 // not to zero as downstream code may not expect a zero-velocity fling.
147 blink::WebGestureEvent& modified_event =
148 const_cast<blink::WebGestureEvent&>(event);
149 modified_event.data.flingStart.velocityX = .01f;
150 modified_event.data.flingStart.velocityY = .01f;
152 } break;
154 case blink::WebInputEvent::GesturePinchBegin:
155 refresh_effect_->ReleaseWithoutActivation();
156 break;
158 default:
159 break;
162 return handled;
165 void OverscrollControllerAndroid::OnGestureEventAck(
166 const blink::WebGestureEvent& event,
167 InputEventAckState ack_result) {
168 if (!enabled_)
169 return;
171 // The overscroll effect requires an explicit release signal that may not be
172 // sent from the renderer compositor.
173 if (event.type == blink::WebInputEvent::GestureScrollEnd ||
174 event.type == blink::WebInputEvent::GestureFlingStart) {
175 OnOverscrolled(DidOverscrollParams());
178 if (event.type == blink::WebInputEvent::GestureScrollUpdate &&
179 refresh_effect_) {
180 // The effect should only be allowed if both the causal touch events go
181 // unconsumed and the generated scroll events go unconsumed.
182 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED ||
183 event.data.scrollUpdate.previousUpdateInSequencePrevented;
184 refresh_effect_->OnScrollUpdateAck(consumed);
188 void OverscrollControllerAndroid::OnOverscrolled(
189 const DidOverscrollParams& params) {
190 if (!enabled_)
191 return;
193 if (refresh_effect_ && (refresh_effect_->IsActive() ||
194 refresh_effect_->IsAwaitingScrollUpdateAck())) {
195 // An active (or potentially active) refresh effect should always pre-empt
196 // the passive glow effect.
197 return;
200 if (glow_effect_ &&
201 glow_effect_->OnOverscrolled(
202 base::TimeTicks::Now(),
203 gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale_),
204 gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale_),
205 gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale_),
206 gfx::ScaleVector2d(
207 params.causal_event_viewport_point.OffsetFromOrigin(),
208 dpi_scale_))) {
209 SetNeedsAnimate();
213 bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time,
214 cc::Layer* parent_layer) {
215 DCHECK(parent_layer);
216 if (!enabled_ || !glow_effect_)
217 return false;
219 return glow_effect_->Animate(current_time, parent_layer);
222 void OverscrollControllerAndroid::OnFrameMetadataUpdated(
223 const cc::CompositorFrameMetadata& frame_metadata) {
224 if (!refresh_effect_ && !glow_effect_)
225 return;
227 const float scale_factor =
228 frame_metadata.page_scale_factor * frame_metadata.device_scale_factor;
229 gfx::SizeF viewport_size =
230 gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor);
231 gfx::SizeF content_size =
232 gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor);
233 gfx::Vector2dF content_scroll_offset =
234 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor);
236 if (refresh_effect_) {
237 refresh_effect_->OnFrameUpdated(content_scroll_offset,
238 frame_metadata.root_overflow_y_hidden);
241 if (glow_effect_) {
242 glow_effect_->OnFrameUpdated(viewport_size, content_size,
243 content_scroll_offset);
247 void OverscrollControllerAndroid::Enable() {
248 enabled_ = true;
251 void OverscrollControllerAndroid::Disable() {
252 if (!enabled_)
253 return;
254 enabled_ = false;
255 if (!enabled_) {
256 if (refresh_effect_)
257 refresh_effect_->Reset();
258 if (glow_effect_)
259 glow_effect_->Reset();
263 scoped_ptr<EdgeEffectBase> OverscrollControllerAndroid::CreateEdgeEffect() {
264 return CreateGlowEdgeEffect(&compositor_->GetResourceManager(), dpi_scale_);
267 void OverscrollControllerAndroid::SetNeedsAnimate() {
268 compositor_->SetNeedsAnimate();
271 } // namespace content