| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/android/overscroll_controller_android.h" | 5 #include "content/browser/android/overscroll_controller_android.h" |
| 6 | 6 |
| 7 #include "base/android/build_info.h" | 7 #include "base/android/build_info.h" |
| 8 #include "base/bind.h" | 8 #include "base/command_line.h" |
| 9 #include "cc/layers/layer.h" | 9 #include "cc/layers/layer.h" |
| 10 #include "cc/output/compositor_frame_metadata.h" | 10 #include "cc/output/compositor_frame_metadata.h" |
| 11 #include "content/browser/android/edge_effect.h" | 11 #include "content/browser/android/edge_effect.h" |
| 12 #include "content/browser/android/edge_effect_l.h" | 12 #include "content/browser/android/edge_effect_l.h" |
| 13 #include "content/common/input/did_overscroll_params.h" | 13 #include "content/common/input/did_overscroll_params.h" |
| 14 #include "content/public/browser/web_contents.h" | 14 #include "content/public/browser/web_contents.h" |
| 15 #include "content/public/common/content_switches.h" |
| 15 #include "third_party/WebKit/public/web/WebInputEvent.h" | 16 #include "third_party/WebKit/public/web/WebInputEvent.h" |
| 16 #include "ui/base/android/window_android_compositor.h" | 17 #include "ui/base/android/window_android_compositor.h" |
| 17 | 18 |
| 18 namespace content { | 19 namespace content { |
| 19 namespace { | 20 namespace { |
| 20 | 21 |
| 21 // Used for conditional creation of EdgeEffect types for the overscroll glow. | 22 // Used for conditional creation of EdgeEffect types for the overscroll glow. |
| 22 const int kAndroidLSDKVersion = 21; | 23 const int kAndroidLSDKVersion = 21; |
| 23 | 24 |
| 24 // Default offset in dips from the top of the view beyond which the refresh | 25 // Default offset in dips from the top of the view beyond which the refresh |
| 25 // action will be activated. | 26 // action will be activated. |
| 26 const int kDefaultRefreshDragTargetDips = 64; | 27 const int kDefaultRefreshDragTargetDips = 64; |
| 27 | 28 |
| 28 scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect( | 29 scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect( |
| 29 ui::SystemUIResourceManager* resource_manager, | 30 ui::SystemUIResourceManager* resource_manager, |
| 30 float dpi_scale) { | 31 float dpi_scale) { |
| 31 DCHECK(resource_manager); | 32 DCHECK(resource_manager); |
| 32 static bool use_l_flavoured_effect = | 33 static bool use_l_flavoured_effect = |
| 33 base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion; | 34 base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion; |
| 34 if (use_l_flavoured_effect) | 35 if (use_l_flavoured_effect) |
| 35 return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager)); | 36 return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager)); |
| 36 | 37 |
| 37 return scoped_ptr<EdgeEffectBase>( | 38 return scoped_ptr<EdgeEffectBase>( |
| 38 new EdgeEffect(resource_manager, dpi_scale)); | 39 new EdgeEffect(resource_manager, dpi_scale)); |
| 39 } | 40 } |
| 40 | 41 |
| 42 scoped_ptr<OverscrollGlow> CreateGlowEffect(OverscrollGlowClient* client, |
| 43 float dpi_scale) { |
| 44 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 45 switches::kDisableOverscrollEdgeEffect)) { |
| 46 return nullptr; |
| 47 } |
| 48 |
| 49 return make_scoped_ptr(new OverscrollGlow(client)); |
| 50 } |
| 51 |
| 52 scoped_ptr<OverscrollRefresh> CreateRefreshEffect( |
| 53 ui::WindowAndroidCompositor* compositor, |
| 54 OverscrollRefreshClient* client, |
| 55 float dpi_scale) { |
| 56 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 57 switches::kDisablePullToRefreshEffect)) { |
| 58 return nullptr; |
| 59 } |
| 60 |
| 61 return make_scoped_ptr( |
| 62 new OverscrollRefresh(&compositor->GetSystemUIResourceManager(), client, |
| 63 kDefaultRefreshDragTargetDips * dpi_scale)); |
| 64 } |
| 65 |
| 41 } // namespace | 66 } // namespace |
| 42 | 67 |
| 43 OverscrollControllerAndroid::OverscrollControllerAndroid( | 68 OverscrollControllerAndroid::OverscrollControllerAndroid( |
| 44 WebContents* web_contents, | 69 WebContents* web_contents, |
| 45 ui::WindowAndroidCompositor* compositor, | 70 ui::WindowAndroidCompositor* compositor, |
| 46 float dpi_scale) | 71 float dpi_scale) |
| 47 : WebContentsObserver(web_contents), | 72 : compositor_(compositor), |
| 48 compositor_(compositor), | |
| 49 dpi_scale_(dpi_scale), | 73 dpi_scale_(dpi_scale), |
| 50 enabled_(true), | 74 enabled_(true), |
| 51 glow_effect_(base::Bind(&CreateGlowEdgeEffect, | 75 glow_effect_(CreateGlowEffect(this, dpi_scale)), |
| 52 &compositor->GetSystemUIResourceManager(), | 76 refresh_effect_(CreateRefreshEffect(compositor, this, dpi_scale)), |
| 53 dpi_scale_)), | |
| 54 refresh_effect_(&compositor->GetSystemUIResourceManager(), | |
| 55 this, | |
| 56 kDefaultRefreshDragTargetDips * dpi_scale), | |
| 57 triggered_refresh_active_(false) { | 77 triggered_refresh_active_(false) { |
| 58 DCHECK(web_contents); | 78 DCHECK(web_contents); |
| 79 DCHECK(compositor); |
| 80 if (refresh_effect_) |
| 81 Observe(web_contents); |
| 59 } | 82 } |
| 60 | 83 |
| 61 OverscrollControllerAndroid::~OverscrollControllerAndroid() { | 84 OverscrollControllerAndroid::~OverscrollControllerAndroid() { |
| 62 } | 85 } |
| 63 | 86 |
| 64 bool OverscrollControllerAndroid::WillHandleGestureEvent( | 87 bool OverscrollControllerAndroid::WillHandleGestureEvent( |
| 65 const blink::WebGestureEvent& event) { | 88 const blink::WebGestureEvent& event) { |
| 66 if (!enabled_) | 89 if (!enabled_) |
| 67 return false; | 90 return false; |
| 68 | 91 |
| 92 if (!refresh_effect_) |
| 93 return false; |
| 94 |
| 69 bool handled = false; | 95 bool handled = false; |
| 70 bool maybe_needs_animate = false; | 96 bool maybe_needs_animate = false; |
| 71 switch (event.type) { | 97 switch (event.type) { |
| 72 case blink::WebInputEvent::GestureScrollBegin: | 98 case blink::WebInputEvent::GestureScrollBegin: |
| 73 refresh_effect_.OnScrollBegin(); | 99 refresh_effect_->OnScrollBegin(); |
| 74 break; | 100 break; |
| 75 | 101 |
| 76 case blink::WebInputEvent::GestureScrollUpdate: { | 102 case blink::WebInputEvent::GestureScrollUpdate: { |
| 77 gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX, | 103 gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX, |
| 78 event.data.scrollUpdate.deltaY); | 104 event.data.scrollUpdate.deltaY); |
| 79 scroll_delta.Scale(dpi_scale_); | 105 scroll_delta.Scale(dpi_scale_); |
| 80 maybe_needs_animate = true; | 106 maybe_needs_animate = true; |
| 81 handled = refresh_effect_.WillHandleScrollUpdate(scroll_delta); | 107 handled = refresh_effect_->WillHandleScrollUpdate(scroll_delta); |
| 82 } break; | 108 } break; |
| 83 | 109 |
| 84 case blink::WebInputEvent::GestureScrollEnd: | 110 case blink::WebInputEvent::GestureScrollEnd: |
| 85 refresh_effect_.OnScrollEnd(gfx::Vector2dF()); | 111 refresh_effect_->OnScrollEnd(gfx::Vector2dF()); |
| 86 maybe_needs_animate = true; | 112 maybe_needs_animate = true; |
| 87 break; | 113 break; |
| 88 | 114 |
| 89 case blink::WebInputEvent::GestureFlingStart: { | 115 case blink::WebInputEvent::GestureFlingStart: { |
| 90 gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX, | 116 gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX, |
| 91 event.data.flingStart.velocityY); | 117 event.data.flingStart.velocityY); |
| 92 scroll_velocity.Scale(dpi_scale_); | 118 scroll_velocity.Scale(dpi_scale_); |
| 93 refresh_effect_.OnScrollEnd(scroll_velocity); | 119 refresh_effect_->OnScrollEnd(scroll_velocity); |
| 94 if (refresh_effect_.IsActive()) { | 120 if (refresh_effect_->IsActive()) { |
| 95 // TODO(jdduke): Figure out a cleaner way of suppressing a fling. | 121 // TODO(jdduke): Figure out a cleaner way of suppressing a fling. |
| 96 // It's important that the any downstream code sees a scroll-ending | 122 // It's important that the any downstream code sees a scroll-ending |
| 97 // event (in this case GestureFlingStart) if it has seen a scroll begin. | 123 // event (in this case GestureFlingStart) if it has seen a scroll begin. |
| 98 // Thus, we cannot simply consume the fling. Changing the event type to | 124 // Thus, we cannot simply consume the fling. Changing the event type to |
| 99 // a GestureScrollEnd might work in practice, but could lead to | 125 // a GestureScrollEnd might work in practice, but could lead to |
| 100 // unexpected results. For now, simply truncate the fling velocity, but | 126 // unexpected results. For now, simply truncate the fling velocity, but |
| 101 // not to zero as downstream code may not expect a zero-velocity fling. | 127 // not to zero as downstream code may not expect a zero-velocity fling. |
| 102 blink::WebGestureEvent& modified_event = | 128 blink::WebGestureEvent& modified_event = |
| 103 const_cast<blink::WebGestureEvent&>(event); | 129 const_cast<blink::WebGestureEvent&>(event); |
| 104 modified_event.data.flingStart.velocityX = .01f; | 130 modified_event.data.flingStart.velocityX = .01f; |
| 105 modified_event.data.flingStart.velocityY = .01f; | 131 modified_event.data.flingStart.velocityY = .01f; |
| 106 } | 132 } |
| 107 maybe_needs_animate = true; | 133 maybe_needs_animate = true; |
| 108 } break; | 134 } break; |
| 109 | 135 |
| 110 default: | 136 default: |
| 111 break; | 137 break; |
| 112 } | 138 } |
| 113 | 139 |
| 114 if (maybe_needs_animate && refresh_effect_.IsActive()) | 140 if (maybe_needs_animate && refresh_effect_->IsActive()) |
| 115 SetNeedsAnimate(); | 141 SetNeedsAnimate(); |
| 116 | 142 |
| 117 return handled; | 143 return handled; |
| 118 } | 144 } |
| 119 | 145 |
| 120 void OverscrollControllerAndroid::OnGestureEventAck( | 146 void OverscrollControllerAndroid::OnGestureEventAck( |
| 121 const blink::WebGestureEvent& event, | 147 const blink::WebGestureEvent& event, |
| 122 InputEventAckState ack_result) { | 148 InputEventAckState ack_result) { |
| 123 if (!enabled_) | 149 if (!enabled_) |
| 124 return; | 150 return; |
| 125 | 151 |
| 126 // The overscroll effect requires an explicit release signal that may not be | 152 // The overscroll effect requires an explicit release signal that may not be |
| 127 // sent from the renderer compositor. | 153 // sent from the renderer compositor. |
| 128 if (event.type == blink::WebInputEvent::GestureScrollEnd || | 154 if (event.type == blink::WebInputEvent::GestureScrollEnd || |
| 129 event.type == blink::WebInputEvent::GestureFlingStart) { | 155 event.type == blink::WebInputEvent::GestureFlingStart) { |
| 130 OnOverscrolled(DidOverscrollParams()); | 156 OnOverscrolled(DidOverscrollParams()); |
| 131 } | 157 } |
| 132 | 158 |
| 133 if (event.type == blink::WebInputEvent::GestureScrollUpdate) { | 159 if (event.type == blink::WebInputEvent::GestureScrollUpdate && |
| 160 refresh_effect_) { |
| 134 // The effect should only be allowed if both the causal touch events go | 161 // The effect should only be allowed if both the causal touch events go |
| 135 // unconsumed and the generated scroll events go unconsumed. | 162 // unconsumed and the generated scroll events go unconsumed. |
| 136 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED || | 163 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED || |
| 137 event.data.scrollUpdate.previousUpdateInSequencePrevented; | 164 event.data.scrollUpdate.previousUpdateInSequencePrevented; |
| 138 refresh_effect_.OnScrollUpdateAck(consumed); | 165 refresh_effect_->OnScrollUpdateAck(consumed); |
| 139 } | 166 } |
| 140 } | 167 } |
| 141 | 168 |
| 142 void OverscrollControllerAndroid::OnOverscrolled( | 169 void OverscrollControllerAndroid::OnOverscrolled( |
| 143 const DidOverscrollParams& params) { | 170 const DidOverscrollParams& params) { |
| 144 if (!enabled_) | 171 if (!enabled_) |
| 145 return; | 172 return; |
| 146 | 173 |
| 147 if (refresh_effect_.IsActive() || | 174 if (refresh_effect_ && (refresh_effect_->IsActive() || |
| 148 refresh_effect_.IsAwaitingScrollUpdateAck()) { | 175 refresh_effect_->IsAwaitingScrollUpdateAck())) { |
| 149 // An active (or potentially active) refresh effect should always pre-empt | 176 // An active (or potentially active) refresh effect should always pre-empt |
| 150 // the passive glow effect. | 177 // the passive glow effect. |
| 151 return; | 178 return; |
| 152 } | 179 } |
| 153 | 180 |
| 154 if (glow_effect_.OnOverscrolled( | 181 if (glow_effect_ && |
| 155 base::TimeTicks::Now(), | 182 glow_effect_->OnOverscrolled( |
| 156 gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale_), | 183 base::TimeTicks::Now(), |
| 157 gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale_), | 184 gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale_), |
| 158 gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale_), | 185 gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale_), |
| 159 gfx::ScaleVector2d(params.causal_event_viewport_point.OffsetFromOrigin(), | 186 gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale_), |
| 160 dpi_scale_))) { | 187 gfx::ScaleVector2d( |
| 188 params.causal_event_viewport_point.OffsetFromOrigin(), |
| 189 dpi_scale_))) { |
| 161 SetNeedsAnimate(); | 190 SetNeedsAnimate(); |
| 162 } | 191 } |
| 163 } | 192 } |
| 164 | 193 |
| 165 bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time, | 194 bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time, |
| 166 cc::Layer* parent_layer) { | 195 cc::Layer* parent_layer) { |
| 167 DCHECK(parent_layer); | 196 DCHECK(parent_layer); |
| 168 if (!enabled_) | 197 if (!enabled_) |
| 169 return false; | 198 return false; |
| 170 | 199 |
| 171 bool needs_animate = refresh_effect_.Animate(current_time, parent_layer); | 200 bool needs_animate = false; |
| 172 needs_animate |= glow_effect_.Animate(current_time, parent_layer); | 201 if (refresh_effect_) |
| 202 needs_animate |= refresh_effect_->Animate(current_time, parent_layer); |
| 203 if (glow_effect_) |
| 204 needs_animate |= glow_effect_->Animate(current_time, parent_layer); |
| 173 return needs_animate; | 205 return needs_animate; |
| 174 } | 206 } |
| 175 | 207 |
| 176 void OverscrollControllerAndroid::OnFrameMetadataUpdated( | 208 void OverscrollControllerAndroid::OnFrameMetadataUpdated( |
| 177 const cc::CompositorFrameMetadata& frame_metadata) { | 209 const cc::CompositorFrameMetadata& frame_metadata) { |
| 210 if (!refresh_effect_ && !glow_effect_) |
| 211 return; |
| 212 |
| 178 const float scale_factor = | 213 const float scale_factor = |
| 179 frame_metadata.page_scale_factor * frame_metadata.device_scale_factor; | 214 frame_metadata.page_scale_factor * frame_metadata.device_scale_factor; |
| 180 gfx::SizeF viewport_size = | 215 gfx::SizeF viewport_size = |
| 181 gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor); | 216 gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor); |
| 182 gfx::SizeF content_size = | 217 gfx::SizeF content_size = |
| 183 gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor); | 218 gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor); |
| 184 gfx::Vector2dF content_scroll_offset = | 219 gfx::Vector2dF content_scroll_offset = |
| 185 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor); | 220 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor); |
| 186 | 221 |
| 187 refresh_effect_.UpdateDisplay(viewport_size, content_scroll_offset); | 222 if (refresh_effect_) |
| 188 glow_effect_.UpdateDisplay(viewport_size, content_size, | 223 refresh_effect_->UpdateDisplay(viewport_size, content_scroll_offset); |
| 189 content_scroll_offset); | 224 |
| 225 if (glow_effect_) { |
| 226 glow_effect_->UpdateDisplay(viewport_size, content_size, |
| 227 content_scroll_offset); |
| 228 } |
| 190 } | 229 } |
| 191 | 230 |
| 192 void OverscrollControllerAndroid::Enable() { | 231 void OverscrollControllerAndroid::Enable() { |
| 193 enabled_ = true; | 232 enabled_ = true; |
| 194 } | 233 } |
| 195 | 234 |
| 196 void OverscrollControllerAndroid::Disable() { | 235 void OverscrollControllerAndroid::Disable() { |
| 197 if (!enabled_) | 236 if (!enabled_) |
| 198 return; | 237 return; |
| 199 enabled_ = false; | 238 enabled_ = false; |
| 200 if (!enabled_) { | 239 if (!enabled_) { |
| 201 refresh_effect_.Reset(); | 240 if (refresh_effect_) |
| 202 glow_effect_.Reset(); | 241 refresh_effect_->Reset(); |
| 242 if (glow_effect_) |
| 243 glow_effect_->Reset(); |
| 203 } | 244 } |
| 204 } | 245 } |
| 205 | 246 |
| 206 void OverscrollControllerAndroid::DidNavigateMainFrame( | 247 void OverscrollControllerAndroid::DidNavigateMainFrame( |
| 207 const LoadCommittedDetails& details, | 248 const LoadCommittedDetails& details, |
| 208 const FrameNavigateParams& params) { | 249 const FrameNavigateParams& params) { |
| 209 // Once the main frame has navigated, there's little need to further animate | 250 // Once the main frame has navigated, there's little need to further animate |
| 210 // the reload effect. Note that the effect will naturally time out should the | 251 // the reload effect. Note that the effect will naturally time out should the |
| 211 // reload be interruped for any reason. | 252 // reload be interruped for any reason. |
| 212 triggered_refresh_active_ = false; | 253 triggered_refresh_active_ = false; |
| 213 } | 254 } |
| 214 | 255 |
| 215 void OverscrollControllerAndroid::TriggerRefresh() { | 256 void OverscrollControllerAndroid::TriggerRefresh() { |
| 216 triggered_refresh_active_ = false; | 257 triggered_refresh_active_ = false; |
| 217 if (!web_contents()) | 258 if (!web_contents()) |
| 218 return; | 259 return; |
| 219 | 260 |
| 220 triggered_refresh_active_ = true; | 261 triggered_refresh_active_ = true; |
| 221 web_contents()->ReloadFocusedFrame(false); | 262 web_contents()->ReloadFocusedFrame(false); |
| 222 } | 263 } |
| 223 | 264 |
| 224 bool OverscrollControllerAndroid::IsStillRefreshing() const { | 265 bool OverscrollControllerAndroid::IsStillRefreshing() const { |
| 225 return triggered_refresh_active_; | 266 return triggered_refresh_active_; |
| 226 } | 267 } |
| 227 | 268 |
| 269 scoped_ptr<EdgeEffectBase> OverscrollControllerAndroid::CreateEdgeEffect() { |
| 270 return CreateGlowEdgeEffect(&compositor_->GetSystemUIResourceManager(), |
| 271 dpi_scale_); |
| 272 } |
| 273 |
| 228 void OverscrollControllerAndroid::SetNeedsAnimate() { | 274 void OverscrollControllerAndroid::SetNeedsAnimate() { |
| 229 compositor_->SetNeedsAnimate(); | 275 compositor_->SetNeedsAnimate(); |
| 230 } | 276 } |
| 231 | 277 |
| 232 } // namespace content | 278 } // namespace content |
| OLD | NEW |