Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: content/browser/web_contents/aura/window_slider.cc

Issue 202183003: Fixing race conditions in ui::content::WindowSlider which could cause the overscroll overlay to nev… (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Implementing some of the review feedback. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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/web_contents/aura/window_slider.h" 5 #include "content/browser/web_contents/aura/window_slider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "content/browser/web_contents/aura/shadow_layer_delegate.h" 11 #include "content/browser/web_contents/aura/shadow_layer_delegate.h"
12 #include "content/public/browser/overscroll_configuration.h" 12 #include "content/public/browser/overscroll_configuration.h"
13 #include "ui/aura/window.h" 13 #include "ui/aura/window.h"
14 #include "ui/compositor/layer_animation_observer.h" 14 #include "ui/compositor/layer_animation_observer.h"
15 #include "ui/compositor/scoped_layer_animation_settings.h" 15 #include "ui/compositor/scoped_layer_animation_settings.h"
16 #include "ui/events/event.h" 16 #include "ui/events/event.h"
17 17
18 namespace content { 18 namespace content {
19 19
20 namespace { 20 namespace {
21 21
22 void DeleteLayerAndShadow(ui::Layer* layer,
23 ShadowLayerDelegate* shadow) {
24 delete shadow;
25 delete layer;
26 }
27
28 // An animation observer that runs a callback at the end of the animation, and 22 // An animation observer that runs a callback at the end of the animation, and
29 // destroys itself. 23 // destroys itself.
30 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { 24 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
31 public: 25 public:
32 CallbackAnimationObserver(const base::Closure& closure) 26 CallbackAnimationObserver(const base::Closure& closure)
33 : closure_(closure) { 27 : closure_(closure) {
34 } 28 }
35 29
36 virtual ~CallbackAnimationObserver() {} 30 virtual ~CallbackAnimationObserver() {}
37 31
(...skipping 11 matching lines...) Expand all
49 }; 43 };
50 44
51 } // namespace 45 } // namespace
52 46
53 WindowSlider::WindowSlider(Delegate* delegate, 47 WindowSlider::WindowSlider(Delegate* delegate,
54 aura::Window* event_window, 48 aura::Window* event_window,
55 aura::Window* owner) 49 aura::Window* owner)
56 : delegate_(delegate), 50 : delegate_(delegate),
57 event_window_(event_window), 51 event_window_(event_window),
58 owner_(owner), 52 owner_(owner),
53 active_animator_(NULL),
59 delta_x_(0.f), 54 delta_x_(0.f),
60 weak_factory_(this), 55 weak_factory_(this),
61 active_start_threshold_(0.f), 56 active_start_threshold_(0.f),
62 start_threshold_touchscreen_(content::GetOverscrollConfig( 57 start_threshold_touchscreen_(content::GetOverscrollConfig(
63 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)), 58 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)),
64 start_threshold_touchpad_(content::GetOverscrollConfig( 59 start_threshold_touchpad_(content::GetOverscrollConfig(
65 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)), 60 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)),
66 complete_threshold_(content::GetOverscrollConfig( 61 complete_threshold_(content::GetOverscrollConfig(
67 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) { 62 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) {
68 event_window_->AddPreTargetHandler(this); 63 event_window_->AddPreTargetHandler(this);
(...skipping 17 matching lines...) Expand all
86 owner_->RemoveObserver(this); 81 owner_->RemoveObserver(this);
87 owner_ = new_owner; 82 owner_ = new_owner;
88 if (owner_) { 83 if (owner_) {
89 owner_->AddObserver(this); 84 owner_->AddObserver(this);
90 UpdateForScroll(0.f, 0.f); 85 UpdateForScroll(0.f, 0.f);
91 } 86 }
92 } 87 }
93 88
94 bool WindowSlider::IsSlideInProgress() const { 89 bool WindowSlider::IsSlideInProgress() const {
95 // if active_start_threshold_ is 0, it means that sliding hasn't been started 90 // if active_start_threshold_ is 0, it means that sliding hasn't been started
96 return active_start_threshold_ != 0 && 91 return active_start_threshold_ != 0 && (slider_.get() || active_animator_);
97 (fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
98 weak_factory_.HasWeakPtrs());
99 } 92 }
100 93
101 void WindowSlider::SetupSliderLayer() { 94 void WindowSlider::SetupSliderLayer() {
102 ui::Layer* parent = owner_->layer()->parent(); 95 ui::Layer* parent = owner_->layer()->parent();
103 parent->Add(slider_.get()); 96 parent->Add(slider_.get());
104 if (delta_x_ < 0) 97 if (delta_x_ < 0)
105 parent->StackAbove(slider_.get(), owner_->layer()); 98 parent->StackAbove(slider_.get(), owner_->layer());
106 else 99 else
107 parent->StackBelow(slider_.get(), owner_->layer()); 100 parent->StackBelow(slider_.get(), owner_->layer());
108 slider_->SetBounds(owner_->layer()->bounds()); 101 slider_->SetBounds(owner_->layer()->bounds());
109 slider_->SetVisible(true); 102 slider_->SetVisible(true);
110 } 103 }
111 104
112 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) { 105 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
106 if (active_animator_) {
107 // If there is an active animation, complete it before processing the scroll
108 // so that the callbacks that are invoked on the Delegate are consistent.
109 // Completing the animation may destroy WindowSlider through the animation
110 // callback, so we can't continue processing the scroll event here.
111 delta_x_ += x_offset;
112 CompleteActiveAnimations();
113 return;
114 }
115
113 float old_delta = delta_x_; 116 float old_delta = delta_x_;
114 delta_x_ += x_offset; 117 delta_x_ += x_offset;
115 if (fabs(delta_x_) < active_start_threshold_ && !slider_.get()) 118 if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
116 return; 119 return;
117 120
118 if ((old_delta < 0 && delta_x_ > 0) || 121 if ((old_delta < 0 && delta_x_ > 0) ||
119 (old_delta > 0 && delta_x_ < 0)) { 122 (old_delta > 0 && delta_x_ < 0)) {
120 slider_.reset(); 123 slider_.reset();
121 shadow_.reset(); 124 shadow_.reset();
122 } 125 }
123 126
124 float translate = 0.f; 127 float translate = 0.f;
125 ui::Layer* translate_layer = NULL; 128 ui::Layer* translate_layer = NULL;
126 129
127 if (!slider_.get()) { 130 if (!slider_.get()) {
128 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() : 131 slider_.reset(delta_x_ < 0 ? delegate_->OnSlideForwardStartedCreateLayer() :
129 delegate_->CreateBackLayer()); 132 delegate_->OnSlideBackStartedCreateLayer());
130 if (!slider_.get()) 133 if (!slider_.get())
131 return; 134 return;
132 SetupSliderLayer(); 135 SetupSliderLayer();
133 } 136 }
134 137
135 if (delta_x_ <= -active_start_threshold_) { 138 if (delta_x_ <= -active_start_threshold_) {
136 translate = owner_->bounds().width() + 139 translate = owner_->bounds().width() +
137 std::max(delta_x_ + active_start_threshold_, 140 std::max(delta_x_ + active_start_threshold_,
138 static_cast<float>(-owner_->bounds().width())); 141 static_cast<float>(-owner_->bounds().width()));
139 translate_layer = slider_.get(); 142 translate_layer = slider_.get();
(...skipping 13 matching lines...) Expand all
153 translate_layer->SetTransform(transform); 156 translate_layer->SetTransform(transform);
154 } 157 }
155 158
156 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { 159 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
157 if (!slider_.get()) 160 if (!slider_.get())
158 return; 161 return;
159 162
160 int width = owner_->bounds().width(); 163 int width = owner_->bounds().width();
161 float ratio = (fabs(delta_x_) - active_start_threshold_) / width; 164 float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
162 if (ratio < complete_threshold_) { 165 if (ratio < complete_threshold_) {
163 ResetScroll(); 166 ResetSlide();
164 return; 167 return;
165 } 168 }
166 169
167 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer(); 170 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
168 ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator()); 171 active_animator_ = sliding->GetAnimator();
172
173 ui::ScopedLayerAnimationSettings settings(active_animator_);
169 settings.SetPreemptionStrategy( 174 settings.SetPreemptionStrategy(
170 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 175 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
171 settings.SetTweenType(gfx::Tween::EASE_OUT); 176 settings.SetTweenType(gfx::Tween::EASE_OUT);
172 settings.AddObserver(new CallbackAnimationObserver( 177 settings.AddObserver(new CallbackAnimationObserver(
173 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation, 178 base::Bind(&WindowSlider::FlingAnimationCompleted,
174 weak_factory_.GetWeakPtr()))); 179 weak_factory_.GetWeakPtr(),
180 base::Unretained(slider_.release()),
181 base::Unretained(shadow_.release()))));
sadrul 2014/03/24 20:20:47 Use base::Passed for these two
mfomitchev 2014/03/25 15:27:32 Done.
175 182
176 gfx::Transform transform; 183 gfx::Transform transform;
177 transform.Translate(delta_x_ < 0 ? 0 : width, 0); 184 transform.Translate(delta_x_ < 0 ? 0 : width, 0);
185 delta_x_ = 0;
186 delegate_->OnWindowSlideCompleting();
178 sliding->SetTransform(transform); 187 sliding->SetTransform(transform);
179 } 188 }
180 189
181 void WindowSlider::ResetScroll() { 190 void WindowSlider::CompleteActiveAnimations() {
191 if (active_animator_)
192 active_animator_->StopAnimating();
193 }
194
195 void WindowSlider::ResetSlide() {
182 if (!slider_.get()) 196 if (!slider_.get())
183 return; 197 return;
184 198
185 // Do not trigger any callbacks if this animation replaces any in-progress
186 // animation.
187 weak_factory_.InvalidateWeakPtrs();
188
189 // Reset the state of the sliding layer. 199 // Reset the state of the sliding layer.
190 if (slider_.get()) { 200 if (slider_.get()) {
191 ui::Layer* layer = slider_.release(); 201 ui::Layer* translate_layer;
192 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); 202 gfx::Transform transform;
193 settings.SetPreemptionStrategy( 203 if (delta_x_ < 0) {
194 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 204 translate_layer = slider_.get();
195 settings.SetTweenType(gfx::Tween::EASE_OUT); 205 transform.Translate(translate_layer->bounds().width(), 0);
206 } else {
207 translate_layer = owner_->layer();
208 }
196 209
197 // Delete the layer and the shadow at the end of the animation. 210 active_animator_ = translate_layer->GetAnimator();
198 settings.AddObserver(new CallbackAnimationObserver( 211 ui::ScopedLayerAnimationSettings settings(active_animator_);
199 base::Bind(&DeleteLayerAndShadow,
200 base::Unretained(layer),
201 base::Unretained(shadow_.release()))));
202
203 gfx::Transform transform;
204 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
205 layer->SetTransform(transform);
206 }
207
208 // Reset the state of the main layer.
209 {
210 ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator());
211 settings.SetPreemptionStrategy( 212 settings.SetPreemptionStrategy(
212 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 213 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
213 settings.SetTweenType(gfx::Tween::EASE_OUT); 214 settings.SetTweenType(gfx::Tween::EASE_OUT);
214 settings.AddObserver(new CallbackAnimationObserver( 215 settings.AddObserver(new CallbackAnimationObserver(
215 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation, 216 base::Bind(&WindowSlider::ResetSlideAnimationCompleted,
216 weak_factory_.GetWeakPtr()))); 217 weak_factory_.GetWeakPtr(),
217 owner_->layer()->SetTransform(gfx::Transform()); 218 base::Unretained(slider_.release()),
218 owner_->layer()->SetLayerBrightness(0.f); 219 base::Unretained(shadow_.release()))));
sadrul 2014/03/24 20:20:47 ditto
mfomitchev 2014/03/25 15:27:32 Done.
220 translate_layer->SetTransform(transform);
219 } 221 }
220 222
221 delta_x_ = 0.f; 223 delta_x_ = 0.f;
222 } 224 }
223 225
224 void WindowSlider::CancelScroll() { 226 void WindowSlider::FlingAnimationCompleted(
225 ResetScroll(); 227 ui::Layer* layer, ShadowLayerDelegate* shadow) {
228 active_animator_ = NULL;
229 delete shadow;
230 delete layer;
231 delegate_->OnWindowSlideCompleted();
226 } 232 }
227 233
228 void WindowSlider::CompleteWindowSlideAfterAnimation() { 234 void WindowSlider::ResetSlideAnimationCompleted(
229 weak_factory_.InvalidateWeakPtrs(); 235 ui::Layer* layer, ShadowLayerDelegate* shadow) {
230 shadow_.reset(); 236 active_animator_ = NULL;
231 slider_.reset(); 237 delete shadow;
232 delta_x_ = 0.f; 238 delete layer;
233
234 delegate_->OnWindowSlideComplete();
235 }
236
237 void WindowSlider::AbortWindowSlideAfterAnimation() {
238 weak_factory_.InvalidateWeakPtrs();
239
240 delegate_->OnWindowSlideAborted(); 239 delegate_->OnWindowSlideAborted();
241 } 240 }
242 241
243 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) { 242 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
244 CancelScroll(); 243 ResetSlide();
245 } 244 }
246 245
247 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { 246 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
248 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) 247 if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
249 CancelScroll(); 248 ResetSlide();
250 } 249 }
251 250
252 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) { 251 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
253 active_start_threshold_ = start_threshold_touchpad_; 252 active_start_threshold_ = start_threshold_touchpad_;
254 if (event->type() == ui::ET_SCROLL) 253 if (event->type() == ui::ET_SCROLL)
255 UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal()); 254 UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
256 else if (event->type() == ui::ET_SCROLL_FLING_START) 255 else if (event->type() == ui::ET_SCROLL_FLING_START)
257 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal()); 256 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
258 else 257 else
259 CancelScroll(); 258 ResetSlide();
260 event->SetHandled(); 259 event->SetHandled();
261 } 260 }
262 261
263 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) { 262 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
264 active_start_threshold_ = start_threshold_touchscreen_; 263 active_start_threshold_ = start_threshold_touchscreen_;
265 const ui::GestureEventDetails& details = event->details(); 264 const ui::GestureEventDetails& details = event->details();
266 switch (event->type()) { 265 switch (event->type()) {
267 case ui::ET_GESTURE_SCROLL_BEGIN: 266 case ui::ET_GESTURE_SCROLL_BEGIN:
268 ResetScroll(); 267 CompleteActiveAnimations();
269 break; 268 break;
270 269
271 case ui::ET_GESTURE_SCROLL_UPDATE: 270 case ui::ET_GESTURE_SCROLL_UPDATE:
272 UpdateForScroll(details.scroll_x(), details.scroll_y()); 271 UpdateForScroll(details.scroll_x(), details.scroll_y());
273 break; 272 break;
274 273
275 case ui::ET_GESTURE_SCROLL_END: 274 case ui::ET_GESTURE_SCROLL_END:
276 UpdateForFling(0.f, 0.f); 275 UpdateForFling(0.f, 0.f);
277 break; 276 break;
278 277
279 case ui::ET_SCROLL_FLING_START: 278 case ui::ET_SCROLL_FLING_START:
280 UpdateForFling(details.velocity_x(), details.velocity_y()); 279 UpdateForFling(details.velocity_x(), details.velocity_y());
281 break; 280 break;
282 281
283 case ui::ET_GESTURE_PINCH_BEGIN: 282 case ui::ET_GESTURE_PINCH_BEGIN:
284 case ui::ET_GESTURE_PINCH_UPDATE: 283 case ui::ET_GESTURE_PINCH_UPDATE:
285 case ui::ET_GESTURE_PINCH_END: 284 case ui::ET_GESTURE_PINCH_END:
286 CancelScroll(); 285 ResetSlide();
287 break; 286 break;
288 287
289 default: 288 default:
290 break; 289 break;
291 } 290 }
292 291
293 event->SetHandled(); 292 event->SetHandled();
294 } 293 }
295 294
296 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) { 295 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) {
297 if (window == event_window_) { 296 if (window == event_window_) {
298 window->RemoveObserver(this); 297 window->RemoveObserver(this);
299 window->RemovePreTargetHandler(this); 298 window->RemovePreTargetHandler(this);
300 event_window_ = NULL; 299 event_window_ = NULL;
301 } else if (window == owner_) { 300 } else if (window == owner_) {
302 window->RemoveObserver(this); 301 window->RemoveObserver(this);
303 owner_ = NULL; 302 owner_ = NULL;
304 delete this; 303 delete this;
305 } else { 304 } else {
306 NOTREACHED(); 305 NOTREACHED();
307 } 306 }
308 } 307 }
309 308
310 } // namespace content 309 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698