Index: content/browser/android/overscroll_controller_android.cc |
diff --git a/content/browser/android/overscroll_controller_android.cc b/content/browser/android/overscroll_controller_android.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4e3b5fb5a67cf56303ff8ed9dcae466ede3a7151 |
--- /dev/null |
+++ b/content/browser/android/overscroll_controller_android.cc |
@@ -0,0 +1,228 @@ |
+// 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/browser/android/overscroll_controller_android.h" |
+ |
+#include "base/android/build_info.h" |
+#include "cc/layers/layer.h" |
+#include "cc/output/compositor_frame_metadata.h" |
+#include "content/browser/android/content_view_core_impl.h" |
+#include "content/browser/android/edge_effect.h" |
+#include "content/browser/android/edge_effect_l.h" |
+#include "content/common/input/did_overscroll_params.h" |
+#include "ui/base/android/window_android.h" |
+#include "ui/base/android/window_android_compositor.h" |
+ |
+namespace content { |
+namespace { |
+ |
+// Used for conditional creation of EdgeEffect types for the overscroll glow. |
+const int kAndroidLSDKVersion = 21; |
+ |
+ui::SystemUIResourceManager* GetSystemUIResourceManager( |
+ ContentViewCore* content_view_core) { |
+ ui::WindowAndroid* window = content_view_core->GetWindowAndroid(); |
+ DCHECK(window); |
+ ui::WindowAndroidCompositor* compositor = window->GetCompositor(); |
+ DCHECK(compositor); |
+ return &compositor->GetSystemUIResourceManager(); |
+} |
+ |
+scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect( |
+ ui::SystemUIResourceManager* resource_manager, |
+ float dpi_scale) { |
+ DCHECK(resource_manager); |
+ static bool use_l_flavoured_effect = |
+ base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion; |
+ if (use_l_flavoured_effect) |
+ return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager)); |
+ |
+ return scoped_ptr<EdgeEffectBase>( |
+ new EdgeEffect(resource_manager, dpi_scale)); |
+} |
+ |
+} // namespace |
+ |
+OverscrollControllerAndroid::OverscrollControllerAndroid( |
+ ContentViewCoreImpl* content_view_core) |
+ : WebContentsObserver(content_view_core->GetWebContents()), |
+ content_view_core_(content_view_core), |
+ enabled_(true), |
+ glow_effect_(base::Bind(&CreateGlowEdgeEffect, |
+ GetSystemUIResourceManager(content_view_core), |
+ content_view_core->GetDpiScale())), |
+ refresh_effect_(GetSystemUIResourceManager(content_view_core), this), |
+ triggered_refresh_active_(false) { |
+} |
+ |
+OverscrollControllerAndroid::~OverscrollControllerAndroid() { |
+} |
+ |
+bool OverscrollControllerAndroid::WillHandleGestureEvent( |
+ const blink::WebGestureEvent& event) { |
+ if (!enabled_) |
+ return false; |
+ |
+ bool handled = false; |
+ bool maybe_needs_animate = false; |
+ switch (event.type) { |
+ case blink::WebInputEvent::GestureScrollBegin: |
+ refresh_effect_.OnScrollBegin(); |
+ break; |
+ |
+ case blink::WebInputEvent::GestureScrollUpdate: { |
+ gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX, |
+ event.data.scrollUpdate.deltaY); |
+ scroll_delta.Scale(GetDpiScale()); |
+ maybe_needs_animate = true; |
+ handled = refresh_effect_.WillHandleScrollUpdate(scroll_delta); |
+ } break; |
+ |
+ case blink::WebInputEvent::GestureScrollEnd: |
+ refresh_effect_.OnScrollEnd(gfx::Vector2dF()); |
+ maybe_needs_animate = true; |
+ break; |
+ |
+ case blink::WebInputEvent::GestureFlingStart: { |
+ gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX, |
+ event.data.flingStart.velocityY); |
+ scroll_velocity.Scale(GetDpiScale()); |
+ refresh_effect_.OnScrollEnd(scroll_velocity); |
+ if (refresh_effect_.IsActive()) { |
+ // TODO(jdduke): Figure out a cleaner way of suppressing a fling. |
+ // It's important that the any downstream code sees a scroll-ending |
+ // event (in this case GestureFlingStart) if it has seen a scroll begin. |
+ // Thus, we cannot simply consume the fling. Changing the event type to |
+ // a GestureScrollEnd might work in practice, but could lead to |
+ // unexpected results. For now, simply truncate the fling velocity, but |
+ // not to zero as downstream code may not expect a zero-velocity fling. |
+ blink::WebGestureEvent& modified_event = |
+ const_cast<blink::WebGestureEvent&>(event); |
+ modified_event.data.flingStart.velocityX = .01f; |
+ modified_event.data.flingStart.velocityY = .01f; |
+ } |
+ maybe_needs_animate = true; |
+ } break; |
+ |
+ default: |
+ break; |
+ } |
+ |
+ if (maybe_needs_animate && refresh_effect_.IsActive()) |
+ content_view_core_->SetNeedsAnimate(); |
+ |
+ return handled; |
+} |
+ |
+void OverscrollControllerAndroid::OnGestureEventAck( |
+ const blink::WebGestureEvent& event, |
+ InputEventAckState ack_result) { |
+ if (!enabled_) |
+ return; |
+ |
+ if (event.type == blink::WebInputEvent::GestureScrollUpdate) { |
+ // The effect should only be allowed if both the causal touch events go |
+ // unconsumed and the generated scroll events go unconsumed. |
+ // TODO(jdduke): Prevent activation if the first touchmove was consumed, |
+ // i.e., the first GSU was prevented. |
+ bool scroll_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED; |
+ OverscrollRefresh::ActivationAllowance activation_allowance = |
+ scroll_consumed ? OverscrollRefresh::PREVENT_ACTIVATION |
+ : OverscrollRefresh::ALLOW_ACTIVATION; |
+ refresh_effect_.OnScrollUpdateAck(activation_allowance); |
+ } |
+} |
+ |
+void OverscrollControllerAndroid::OnOverscrolled( |
+ const DidOverscrollParams& params) { |
+ if (!enabled_) |
+ return; |
+ |
+ if (refresh_effect_.IsActive() || |
+ refresh_effect_.IsPendingScrollUpdateAck()) { |
+ // An active (or potentially active) refresh effect should always pre-empt |
+ // the passive glow effect. |
+ return; |
+ } |
+ |
+ const float dpi_scale = GetDpiScale(); |
+ if (glow_effect_.OnOverscrolled( |
+ base::TimeTicks::Now(), |
+ gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale), |
+ gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale), |
+ gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale), |
+ gfx::ScaleVector2d( |
+ params.causal_event_viewport_point.OffsetFromOrigin(), |
+ dpi_scale))) { |
+ content_view_core_->SetNeedsAnimate(); |
+ } |
+} |
+ |
+bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time) { |
+ if (!enabled_) |
+ return false; |
+ |
+ scoped_refptr<cc::Layer> parent = content_view_core_->GetLayer(); |
+ bool needs_animate = refresh_effect_.Animate(current_time, parent.get()); |
+ needs_animate |= glow_effect_.Animate(current_time, parent.get()); |
+ return needs_animate; |
+} |
+ |
+void OverscrollControllerAndroid::OnFrameMetadataUpdated( |
+ const cc::CompositorFrameMetadata& frame_metadata) { |
+ const float scale_factor = |
+ frame_metadata.page_scale_factor * frame_metadata.device_scale_factor; |
+ gfx::SizeF viewport_size = |
+ gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor); |
+ gfx::SizeF content_size = |
+ gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor); |
+ gfx::Vector2dF content_scroll_offset = |
+ gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor); |
+ |
+ refresh_effect_.UpdateDisplay(viewport_size, content_scroll_offset); |
+ glow_effect_.UpdateDisplay(viewport_size, content_size, |
+ content_scroll_offset); |
+} |
+ |
+void OverscrollControllerAndroid::Enable() { |
+ enabled_ = true; |
+} |
+ |
+void OverscrollControllerAndroid::Disable() { |
+ if (!enabled_) |
+ return; |
+ enabled_ = false; |
+ if (!enabled_) { |
+ refresh_effect_.Reset(); |
+ glow_effect_.Reset(); |
+ } |
+} |
+ |
+void OverscrollControllerAndroid::DidNavigateMainFrame( |
+ const LoadCommittedDetails& details, |
+ const FrameNavigateParams& params) { |
+ // Once the main frame has navigated, there's little need to further animate |
+ // the reload effect. Note that the effect will naturally time out should the |
+ // reload be interruped for any reason. |
+ triggered_refresh_active_ = false; |
+} |
+ |
+void OverscrollControllerAndroid::TriggerRefresh() { |
+ triggered_refresh_active_ = false; |
+ WebContents* web_contents = content_view_core_->GetWebContents(); |
+ if (web_contents) { |
+ triggered_refresh_active_ = true; |
+ web_contents->ReloadFocusedFrame(false); |
+ } |
+} |
+ |
+bool OverscrollControllerAndroid::IsTriggeredRefreshActive() const { |
+ return triggered_refresh_active_; |
+} |
+ |
+float OverscrollControllerAndroid::GetDpiScale() const { |
+ return content_view_core_->GetDpiScale(); |
+} |
+ |
+} // namespace content |