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

Unified Diff: content/browser/web_contents/aura/window_slider_unittest.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: Renaming Delegate's methods 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/web_contents/aura/window_slider.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/web_contents/aura/window_slider_unittest.cc
diff --git a/content/browser/web_contents/aura/window_slider_unittest.cc b/content/browser/web_contents/aura/window_slider_unittest.cc
index ed41347ee4d659bdbb81ef0c3a1a4a79b0d786fb..21a8d17472352ef5d948fad4493124c3f91ca256 100644
--- a/content/browser/web_contents/aura/window_slider_unittest.cc
+++ b/content/browser/web_contents/aura/window_slider_unittest.cc
@@ -5,13 +5,19 @@
#include "content/browser/web_contents/aura/window_slider.h"
#include "base/bind.h"
+#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/compositor/test/layer_animator_test_controller.h"
#include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/frame_time.h"
namespace content {
@@ -39,6 +45,28 @@ void ChangeSliderOwnerDuringScrollCallback(scoped_ptr<aura::Window>* window,
window->reset(new_window);
}
+void ConfirmSlideDuringScrollCallback(WindowSlider* slider,
+ ui::EventType type,
+ const gfx::Vector2dF& delta) {
+ static float total_delta_x = 0;
+ if (type == ui::ET_GESTURE_SCROLL_BEGIN)
+ total_delta_x = 0;
+
+ if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
+ total_delta_x += delta.x();
+ if (total_delta_x >= 70)
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ } else {
+ EXPECT_FALSE(slider->IsSlideInProgress());
+ }
+}
+
+void ConfirmNoSlideDuringScrollCallback(WindowSlider* slider,
+ ui::EventType type,
+ const gfx::Vector2dF& delta) {
+ EXPECT_FALSE(slider->IsSlideInProgress());
+}
+
// The window delegate does not receive any events.
class NoEventWindowDelegate : public aura::test::TestWindowDelegate {
public:
@@ -59,16 +87,21 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
: can_create_layer_(true),
created_back_layer_(false),
created_front_layer_(false),
+ slide_completing_(false),
slide_completed_(false),
slide_aborted_(false),
slider_destroyed_(false) {
}
- virtual ~WindowSliderDelegateTest() {}
+ virtual ~WindowSliderDelegateTest() {
+ // Make sure slide_completed() gets called if slide_completing() was called.
+ CHECK(!slide_completing_ || slide_completed_);
+ }
void Reset() {
can_create_layer_ = true;
created_back_layer_ = false;
created_front_layer_ = false;
+ slide_completing_ = false;
slide_completed_ = false;
slide_aborted_ = false;
slider_destroyed_ = false;
@@ -80,6 +113,7 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
bool created_back_layer() const { return created_back_layer_; }
bool created_front_layer() const { return created_front_layer_; }
+ bool slide_completing() const { return slide_completing_; }
bool slide_completed() const { return slide_completed_; }
bool slide_aborted() const { return slide_aborted_; }
bool slider_destroyed() const { return slider_destroyed_; }
@@ -107,10 +141,14 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
return CreateLayerForTest();
}
- virtual void OnWindowSlideComplete() OVERRIDE {
+ virtual void OnWindowSlideCompleted() OVERRIDE {
slide_completed_ = true;
}
+ virtual void OnWindowSlideCompleting() OVERRIDE {
+ slide_completing_ = true;
+ }
+
virtual void OnWindowSlideAborted() OVERRIDE {
slide_aborted_ = true;
}
@@ -123,6 +161,7 @@ class WindowSliderDelegateTest : public WindowSlider::Delegate {
bool can_create_layer_;
bool created_back_layer_;
bool created_front_layer_;
+ bool slide_completing_;
bool slide_completed_;
bool slide_aborted_;
bool slider_destroyed_;
@@ -159,8 +198,8 @@ class WindowSliderDeleteOwnerOnComplete : public WindowSliderDelegateTest {
private:
// Overridden from WindowSlider::Delegate:
- virtual void OnWindowSlideComplete() OVERRIDE {
- WindowSliderDelegateTest::OnWindowSlideComplete();
+ virtual void OnWindowSlideCompleted() OVERRIDE {
+ WindowSliderDelegateTest::OnWindowSlideCompleted();
delete owner_;
}
@@ -180,11 +219,14 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
// Generate a horizontal overscroll.
WindowSlider* slider =
new WindowSlider(&slider_delegate, root_window(), window.get());
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(180, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -193,12 +235,15 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
slider_delegate.Reset();
window->SetTransform(gfx::Transform());
- // Generat a horizontal overscroll in the reverse direction.
- generator.GestureScrollSequence(gfx::Point(180, 10),
- gfx::Point(10, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ // Generate a horizontal overscroll in the reverse direction.
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(180, 10),
+ gfx::Point(10, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_back_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -207,11 +252,14 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
slider_delegate.Reset();
// Generate a vertical overscroll.
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(10, 80),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(10, 80),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmNoSlideDuringScrollCallback, slider));
EXPECT_FALSE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -220,13 +268,16 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
// Generate a horizontal scroll that starts overscroll, but doesn't scroll
// enough to complete it.
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(80, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(80, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
EXPECT_TRUE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.slider_destroyed());
EXPECT_FALSE(slider->IsSlideInProgress());
@@ -237,10 +288,11 @@ TEST_F(WindowSliderTest, WindowSlideUsingGesture) {
EXPECT_TRUE(slider_delegate.slider_destroyed());
}
-// Tests that the window slide is cancelled when a different type of event
+// Tests that the window slide is interrupted when a different type of event
// happens.
TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
WindowSliderDelegateTest slider_delegate;
ui::Event* events[] = {
@@ -270,6 +322,7 @@ TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
EXPECT_TRUE(slider_delegate.created_back_layer());
EXPECT_TRUE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.slider_destroyed());
slider_delegate.Reset();
@@ -278,6 +331,112 @@ TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
EXPECT_TRUE(slider_delegate.slider_destroyed());
}
+// Tests that the window slide can continue after it is interrupted by another
+// event if the user continues scrolling.
+TEST_F(WindowSliderTest, WindowSlideInterruptedThenContinues) {
+ scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
+ WindowSliderDelegateTest slider_delegate;
+
+ ui::ScopedAnimationDurationScaleMode normal_duration_(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+ ui::LayerAnimator* animator = window->layer()->GetAnimator();
+ gfx::AnimationContainerElement* element = animator;
+ animator->set_disable_timer_for_test(true);
+ ui::LayerAnimatorTestController test_controller(animator);
+
+ WindowSlider* slider =
+ new WindowSlider(&slider_delegate, root_window(), window.get());
+
+ ui::MouseEvent interrupt_event(ui::ET_MOUSE_MOVED,
+ gfx::Point(55, 10),
+ gfx::Point(55, 10),
+ 0, 0);
+
+ aura::test::EventGenerator generator(root_window());
+
+ // Start the scroll sequence. Scroll forward so that |window|'s layer is the
+ // one animating.
+ const int kTouchId = 5;
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
+ gfx::Point(10, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&press);
+
+ // First scroll event of the sequence.
+ ui::TouchEvent move1(ui::ET_TOUCH_MOVED,
+ gfx::Point(100, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move1);
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_FALSE(animator->is_animating());
+ // Dispatch the event after the first scroll and confirm it interrupts the
+ // scroll and starts the "reset slide" animation.
+ generator.Dispatch(&interrupt_event);
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(animator->is_animating());
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ // slide_aborted() should be false because the 'reset slide' animation
+ // hasn't completed yet.
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ slider_delegate.Reset();
+
+ // Second scroll event of the sequence.
+ ui::TouchEvent move2(ui::ET_TOUCH_MOVED,
+ gfx::Point(200, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move2);
+ // The second scroll should instantly cause the animation to complete.
+ EXPECT_FALSE(animator->is_animating());
+ EXPECT_FALSE(slider_delegate.created_back_layer());
+ // The ResetScroll() animation was completed, so now slide_aborted()
+ // should be true.
+ EXPECT_TRUE(slider_delegate.slide_aborted());
+
+ // Third scroll event of the sequence.
+ ui::TouchEvent move3(ui::ET_TOUCH_MOVED,
+ gfx::Point(300, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&move3);
+ // The third scroll should re-start the sliding.
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+
+ // Generate the release event, finishing the scroll sequence.
+ ui::TouchEvent release(ui::ET_TOUCH_RELEASED,
+ gfx::Point(300, 10),
+ kTouchId,
+ ui::EventTimeForNow());
+ generator.Dispatch(&release);
+ // When the scroll gesture ends, the slide animation should start.
+ EXPECT_TRUE(slider->IsSlideInProgress());
+ EXPECT_TRUE(animator->is_animating());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ // Progress the animator to complete the slide animation.
+ ui::ScopedLayerAnimationSettings settings(animator);
+ base::TimeDelta duration = settings.GetTransitionDuration();
+ test_controller.StartThreadedAnimationsIfNeeded();
+ element->Step(gfx::FrameTime::Now() + duration);
+
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
+}
+
// Tests that the slide works correctly when the owner of the window changes
// during the duration of the slide.
TEST_F(WindowSliderTest, OwnerWindowChangesDuringWindowSlide) {
@@ -308,28 +467,37 @@ TEST_F(WindowSliderTest, OwnerWindowChangesDuringWindowSlide) {
EXPECT_NE(old_window, new_window);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.slider_destroyed());
}
+// If the delegate doesn't create the layer to show while sliding, WindowSlider
+// shouldn't start the slide or change delegate's state in any way in response
+// to user input.
TEST_F(WindowSliderTest, NoSlideWhenLayerCantBeCreated) {
scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
window->SetBounds(gfx::Rect(0, 0, 400, 400));
WindowSliderDelegateTest slider_delegate;
slider_delegate.SetCanCreateLayer(false);
+ WindowSlider* slider =
+ new WindowSlider(&slider_delegate, root_window(), window.get());
aura::test::EventGenerator generator(root_window());
- // Generate a horizontal overscroll.
- scoped_ptr<WindowSlider> slider(
- new WindowSlider(&slider_delegate, root_window(), window.get()));
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ // No slide in progress should be reported during scroll since the layer
+ // wasn't created.
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 1,
+ base::Bind(&ConfirmNoSlideDuringScrollCallback, slider));
+
EXPECT_FALSE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_completing());
EXPECT_FALSE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -337,15 +505,21 @@ TEST_F(WindowSliderTest, NoSlideWhenLayerCantBeCreated) {
window->SetTransform(gfx::Transform());
slider_delegate.SetCanCreateLayer(true);
- generator.GestureScrollSequence(gfx::Point(10, 10),
- gfx::Point(160, 10),
- base::TimeDelta::FromMilliseconds(10),
- 10);
+ generator.GestureScrollSequenceWithCallback(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 10,
+ base::Bind(&ConfirmSlideDuringScrollCallback, slider));
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.slider_destroyed());
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
}
// Tests that the owner window can be destroyed from |OnWindowSliderDestroyed()|
@@ -367,6 +541,7 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSliderDestroy) {
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -396,6 +571,7 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSlideComplete) {
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
EXPECT_TRUE(slider_delegate.slide_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
@@ -406,4 +582,71 @@ TEST_F(WindowSliderTest, OwnerIsDestroyedOnSlideComplete) {
EXPECT_EQ(child_windows, root_window()->children().size());
}
+// Test the scenario when two swipe gesture occur quickly one after another so
+// that the second swipe occurs while the transition animation triggered by the
+// first swipe is in progress.
+// The second swipe is supposed to instantly complete the animation caused by
+// the first swipe, ask the delegate to create a new layer, and animate it.
+TEST_F(WindowSliderTest, SwipeDuringSwipeAnimation) {
+ scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
+ window->SetBounds(gfx::Rect(0, 0, 400, 400));
+ WindowSliderDelegateTest slider_delegate;
+ new WindowSlider(&slider_delegate, root_window(), window.get());
+
+ ui::ScopedAnimationDurationScaleMode normal_duration_(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+ ui::LayerAnimator* animator = window->layer()->GetAnimator();
+ gfx::AnimationContainerElement* element = animator;
+ animator->set_disable_timer_for_test(true);
+ ui::LayerAnimatorTestController test_controller(animator);
+
+ aura::test::EventGenerator generator(root_window());
+
+ // Swipe forward so that |window|'s layer is the one animating.
+ generator.GestureScrollSequence(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 2);
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ ui::ScopedLayerAnimationSettings settings(animator);
+ base::TimeDelta duration = settings.GetTransitionDuration();
+ test_controller.StartThreadedAnimationsIfNeeded();
+ base::TimeTicks start_time1 = gfx::FrameTime::Now();
+
+ element->Step(start_time1 + duration/2);
+ EXPECT_FALSE(slider_delegate.slide_completed());
+ slider_delegate.Reset();
+ // Generate another horizontal swipe while the animation from the previous
+ // swipe is in progress.
+ generator.GestureScrollSequence(
+ gfx::Point(10, 10),
+ gfx::Point(180, 10),
+ base::TimeDelta::FromMilliseconds(10),
+ 2);
+ // Performing the second swipe should instantly complete the slide started
+ // by the first swipe and create a new layer.
+ EXPECT_TRUE(slider_delegate.created_back_layer());
+ EXPECT_FALSE(slider_delegate.slide_aborted());
+ EXPECT_FALSE(slider_delegate.created_front_layer());
+ EXPECT_TRUE(slider_delegate.slide_completing());
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ EXPECT_FALSE(slider_delegate.slider_destroyed());
+ test_controller.StartThreadedAnimationsIfNeeded();
+ base::TimeTicks start_time2 = gfx::FrameTime::Now();
+ slider_delegate.Reset();
+ element->Step(start_time2 + duration);
+ // The animation for the second slide should now be completed.
+ EXPECT_TRUE(slider_delegate.slide_completed());
+ slider_delegate.Reset();
+
+ window.reset();
+ EXPECT_TRUE(slider_delegate.slider_destroyed());
+}
+
} // namespace content
« no previous file with comments | « content/browser/web_contents/aura/window_slider.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698