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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a029dd8feb2fc2fbfdfa0a4cfe27cd050a7caadc |
--- /dev/null |
+++ b/content/browser/web_contents/aura/window_slider_unittest.cc |
@@ -0,0 +1,639 @@ |
+// Copyright (c) 2013 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/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/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/events/test/event_generator.h" |
+#include "ui/gfx/frame_time.h" |
+ |
+namespace content { |
+ |
+void DispatchEventDuringScrollCallback(ui::EventProcessor* dispatcher, |
+ ui::Event* event, |
+ ui::EventType type, |
+ const gfx::Vector2dF& delta) { |
+ if (type != ui::ET_GESTURE_SCROLL_UPDATE) |
+ return; |
+ ui::EventDispatchDetails details = dispatcher->OnEventFromSource(event); |
+ CHECK(!details.dispatcher_destroyed); |
+} |
+ |
+void ChangeSliderOwnerDuringScrollCallback(scoped_ptr<aura::Window>* window, |
+ WindowSlider* slider, |
+ ui::EventType type, |
+ const gfx::Vector2dF& delta) { |
+ if (type != ui::ET_GESTURE_SCROLL_UPDATE) |
+ return; |
+ aura::Window* new_window = new aura::Window(NULL); |
+ new_window->Init(ui::LAYER_TEXTURED); |
+ new_window->Show(); |
+ slider->ChangeOwner(new_window); |
+ (*window)->parent()->AddChild(new_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: |
+ NoEventWindowDelegate() { |
+ } |
+ ~NoEventWindowDelegate() override {} |
+ |
+ private: |
+ // Overridden from aura::WindowDelegate: |
+ bool HasHitTestMask() const override { return true; } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(NoEventWindowDelegate); |
+}; |
+ |
+class WindowSliderDelegateTest : public WindowSlider::Delegate { |
+ public: |
+ WindowSliderDelegateTest() |
+ : can_create_layer_(true), |
+ created_back_layer_(false), |
+ created_front_layer_(false), |
+ slide_completing_(false), |
+ slide_completed_(false), |
+ slide_aborted_(false), |
+ slider_destroyed_(false) { |
+ } |
+ ~WindowSliderDelegateTest() override { |
+ // 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; |
+ } |
+ |
+ void SetCanCreateLayer(bool can_create_layer) { |
+ can_create_layer_ = can_create_layer; |
+ } |
+ |
+ 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_; } |
+ |
+ protected: |
+ ui::Layer* CreateLayerForTest() { |
+ CHECK(can_create_layer_); |
+ ui::Layer* layer = new ui::Layer(ui::LAYER_SOLID_COLOR); |
+ layer->SetColor(SK_ColorRED); |
+ return layer; |
+ } |
+ |
+ // Overridden from WindowSlider::Delegate: |
+ ui::Layer* CreateBackLayer() override { |
+ if (!can_create_layer_) |
+ return NULL; |
+ created_back_layer_ = true; |
+ return CreateLayerForTest(); |
+ } |
+ |
+ ui::Layer* CreateFrontLayer() override { |
+ if (!can_create_layer_) |
+ return NULL; |
+ created_front_layer_ = true; |
+ return CreateLayerForTest(); |
+ } |
+ |
+ void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) override { |
+ slide_completed_ = true; |
+ } |
+ |
+ void OnWindowSlideCompleting() override { slide_completing_ = true; } |
+ |
+ void OnWindowSlideAborted() override { slide_aborted_ = true; } |
+ |
+ void OnWindowSliderDestroyed() override { slider_destroyed_ = true; } |
+ |
+ private: |
+ bool can_create_layer_; |
+ bool created_back_layer_; |
+ bool created_front_layer_; |
+ bool slide_completing_; |
+ bool slide_completed_; |
+ bool slide_aborted_; |
+ bool slider_destroyed_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WindowSliderDelegateTest); |
+}; |
+ |
+// This delegate destroys the owner window when the slider is destroyed. |
+class WindowSliderDeleteOwnerOnDestroy : public WindowSliderDelegateTest { |
+ public: |
+ explicit WindowSliderDeleteOwnerOnDestroy(aura::Window* owner) |
+ : owner_(owner) { |
+ } |
+ ~WindowSliderDeleteOwnerOnDestroy() override {} |
+ |
+ private: |
+ // Overridden from WindowSlider::Delegate: |
+ void OnWindowSliderDestroyed() override { |
+ WindowSliderDelegateTest::OnWindowSliderDestroyed(); |
+ delete owner_; |
+ } |
+ |
+ aura::Window* owner_; |
+ DISALLOW_COPY_AND_ASSIGN(WindowSliderDeleteOwnerOnDestroy); |
+}; |
+ |
+// This delegate destroyes the owner window when a slide is completed. |
+class WindowSliderDeleteOwnerOnComplete : public WindowSliderDelegateTest { |
+ public: |
+ explicit WindowSliderDeleteOwnerOnComplete(aura::Window* owner) |
+ : owner_(owner) { |
+ } |
+ ~WindowSliderDeleteOwnerOnComplete() override {} |
+ |
+ private: |
+ // Overridden from WindowSlider::Delegate: |
+ void OnWindowSlideCompleted(scoped_ptr<ui::Layer> layer) override { |
+ WindowSliderDelegateTest::OnWindowSlideCompleted(layer.Pass()); |
+ delete owner_; |
+ } |
+ |
+ aura::Window* owner_; |
+ DISALLOW_COPY_AND_ASSIGN(WindowSliderDeleteOwnerOnComplete); |
+}; |
+ |
+typedef aura::test::AuraTestBase WindowSliderTest; |
+ |
+TEST_F(WindowSliderTest, WindowSlideUsingGesture) { |
+ scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL)); |
+ window->SetBounds(gfx::Rect(0, 0, 400, 400)); |
+ WindowSliderDelegateTest slider_delegate; |
+ |
+ ui::test::EventGenerator generator(root_window()); |
+ |
+ // Generate a horizontal overscroll. |
+ WindowSlider* slider = |
+ new WindowSlider(&slider_delegate, root_window(), window.get()); |
+ 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()); |
+ EXPECT_FALSE(slider->IsSlideInProgress()); |
+ slider_delegate.Reset(); |
+ window->SetTransform(gfx::Transform()); |
+ |
+ // 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()); |
+ EXPECT_FALSE(slider_delegate.slider_destroyed()); |
+ EXPECT_FALSE(slider->IsSlideInProgress()); |
+ slider_delegate.Reset(); |
+ |
+ // Generate a vertical overscroll. |
+ 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()); |
+ EXPECT_FALSE(slider->IsSlideInProgress()); |
+ slider_delegate.Reset(); |
+ |
+ // Generate a horizontal scroll that starts overscroll, but doesn't scroll |
+ // enough to complete it. |
+ 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()); |
+ slider_delegate.Reset(); |
+ |
+ // Destroy the window. This should destroy the slider. |
+ window.reset(); |
+ EXPECT_TRUE(slider_delegate.slider_destroyed()); |
+} |
+ |
+// 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[] = { |
+ new ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(55, 10), |
+ gfx::Point(55, 10), ui::EventTimeForNow(), 0, 0), |
+ new ui::KeyEvent('a', ui::VKEY_A, ui::EF_NONE), |
+ nullptr |
+ }; |
+ |
+ new WindowSlider(&slider_delegate, root_window(), window.get()); |
+ for (int i = 0; events[i]; ++i) { |
+ // Generate a horizontal overscroll. |
+ ui::test::EventGenerator generator(root_window()); |
+ generator.GestureScrollSequenceWithCallback( |
+ gfx::Point(10, 10), |
+ gfx::Point(80, 10), |
+ base::TimeDelta::FromMilliseconds(10), |
+ 1, |
+ base::Bind(&DispatchEventDuringScrollCallback, |
+ root_window()->GetHost()->event_processor(), |
+ base::Owned(events[i]))); |
+ 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(); |
+ } |
+ window.reset(); |
+ 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(); |
+ 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), ui::EventTimeForNow(), 0, |
+ 0); |
+ |
+ ui::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(); |
+ animator->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) { |
+ scoped_ptr<aura::Window> parent(CreateNormalWindow(0, root_window(), NULL)); |
+ |
+ NoEventWindowDelegate window_delegate; |
+ window_delegate.set_window_component(HTNOWHERE); |
+ scoped_ptr<aura::Window> window(CreateNormalWindow(1, parent.get(), |
+ &window_delegate)); |
+ |
+ WindowSliderDelegateTest slider_delegate; |
+ scoped_ptr<WindowSlider> slider( |
+ new WindowSlider(&slider_delegate, parent.get(), window.get())); |
+ |
+ // Generate a horizontal scroll, and change the owner in the middle of the |
+ // scroll. |
+ ui::test::EventGenerator generator(root_window()); |
+ aura::Window* old_window = window.get(); |
+ generator.GestureScrollSequenceWithCallback( |
+ gfx::Point(10, 10), |
+ gfx::Point(80, 10), |
+ base::TimeDelta::FromMilliseconds(10), |
+ 1, |
+ base::Bind(&ChangeSliderOwnerDuringScrollCallback, |
+ base::Unretained(&window), |
+ slider.get())); |
+ aura::Window* new_window = window.get(); |
+ 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()); |
+ |
+ ui::test::EventGenerator generator(root_window()); |
+ |
+ // 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()); |
+ EXPECT_FALSE(slider_delegate.slider_destroyed()); |
+ window->SetTransform(gfx::Transform()); |
+ |
+ slider_delegate.SetCanCreateLayer(true); |
+ 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()| |
+// delegate callback without causing a crash. |
+TEST_F(WindowSliderTest, OwnerIsDestroyedOnSliderDestroy) { |
+ size_t child_windows = root_window()->children().size(); |
+ aura::Window* window = CreateNormalWindow(0, root_window(), NULL); |
+ window->SetBounds(gfx::Rect(0, 0, 400, 400)); |
+ EXPECT_EQ(child_windows + 1, root_window()->children().size()); |
+ |
+ WindowSliderDeleteOwnerOnDestroy slider_delegate(window); |
+ ui::test::EventGenerator generator(root_window()); |
+ |
+ // Generate a horizontal overscroll. |
+ scoped_ptr<WindowSlider> slider( |
+ new WindowSlider(&slider_delegate, root_window(), window)); |
+ generator.GestureScrollSequence(gfx::Point(10, 10), |
+ gfx::Point(180, 10), |
+ 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()); |
+ EXPECT_FALSE(slider_delegate.slider_destroyed()); |
+ |
+ slider.reset(); |
+ // Destroying the slider would have destroyed |window| too. So |window| should |
+ // not need to be destroyed here. |
+ EXPECT_EQ(child_windows, root_window()->children().size()); |
+} |
+ |
+// Tests that the owner window can be destroyed from |OnWindowSlideComplete()| |
+// delegate callback without causing a crash. |
+TEST_F(WindowSliderTest, OwnerIsDestroyedOnSlideComplete) { |
+ size_t child_windows = root_window()->children().size(); |
+ aura::Window* window = CreateNormalWindow(0, root_window(), NULL); |
+ window->SetBounds(gfx::Rect(0, 0, 400, 400)); |
+ EXPECT_EQ(child_windows + 1, root_window()->children().size()); |
+ |
+ WindowSliderDeleteOwnerOnComplete slider_delegate(window); |
+ ui::test::EventGenerator generator(root_window()); |
+ |
+ // Generate a horizontal overscroll. |
+ new WindowSlider(&slider_delegate, root_window(), window); |
+ generator.GestureScrollSequence(gfx::Point(10, 10), |
+ gfx::Point(180, 10), |
+ 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()); |
+ EXPECT_TRUE(slider_delegate.slider_destroyed()); |
+ |
+ // Destroying the slider would have destroyed |window| too. So |window| should |
+ // not need to be destroyed here. |
+ 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()); |
+ |
+ // This test uses explicit durations so needs a normal duration. |
+ ui::ScopedAnimationDurationScaleMode normal_duration( |
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); |
+ ui::LayerAnimator* animator = window->layer()->GetAnimator(); |
+ animator->set_disable_timer_for_test(true); |
+ ui::LayerAnimatorTestController test_controller(animator); |
+ |
+ ui::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(); |
+ |
+ animator->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(); |
+ animator->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 |