blob: d56d1e77e01b89ef621b55ec46a1ca21aa491256 [file] [log] [blame]
// 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 "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/root_window.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"
namespace content {
void DispatchEventDuringScrollCallback(aura::WindowEventDispatcher* dispatcher,
ui::Event* event,
ui::EventType type,
const gfx::Vector2dF& delta) {
if (type != ui::ET_GESTURE_SCROLL_UPDATE)
return;
aura::RootWindowHostDelegate* delegate =
dispatcher->AsRootWindowHostDelegate();
if (event->IsMouseEvent())
delegate->OnHostMouseEvent(static_cast<ui::MouseEvent*>(event));
else if (event->IsKeyEvent())
delegate->OnHostKeyEvent(static_cast<ui::KeyEvent*>(event));
}
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);
}
// The window delegate does not receive any events.
class NoEventWindowDelegate : public aura::test::TestWindowDelegate {
public:
NoEventWindowDelegate() {
}
virtual ~NoEventWindowDelegate() {}
private:
// Overridden from aura::WindowDelegate:
virtual 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_completed_(false),
slide_aborted_(false),
slider_destroyed_(false) {
}
virtual ~WindowSliderDelegateTest() {}
void Reset() {
can_create_layer_ = true;
created_back_layer_ = false;
created_front_layer_ = 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_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:
virtual ui::Layer* CreateBackLayer() OVERRIDE {
if (!can_create_layer_)
return NULL;
created_back_layer_ = true;
return CreateLayerForTest();
}
virtual ui::Layer* CreateFrontLayer() OVERRIDE {
if (!can_create_layer_)
return NULL;
created_front_layer_ = true;
return CreateLayerForTest();
}
virtual void OnWindowSlideComplete() OVERRIDE {
slide_completed_ = true;
}
virtual void OnWindowSlideAborted() OVERRIDE {
slide_aborted_ = true;
}
virtual void OnWindowSliderDestroyed() OVERRIDE {
slider_destroyed_ = true;
}
private:
bool can_create_layer_;
bool created_back_layer_;
bool created_front_layer_;
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) {
}
virtual ~WindowSliderDeleteOwnerOnDestroy() {}
private:
// Overridden from WindowSlider::Delegate:
virtual 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) {
}
virtual ~WindowSliderDeleteOwnerOnComplete() {}
private:
// Overridden from WindowSlider::Delegate:
virtual void OnWindowSlideComplete() OVERRIDE {
WindowSliderDelegateTest::OnWindowSlideComplete();
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;
aura::test::EventGenerator generator(root_window());
// Generate a horizontal overscroll.
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);
EXPECT_TRUE(slider_delegate.created_back_layer());
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());
// Generat a horizontal overscroll in the reverse direction.
generator.GestureScrollSequence(gfx::Point(160, 10),
gfx::Point(10, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_front_layer());
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.GestureScrollSequence(gfx::Point(10, 10),
gfx::Point(10, 80),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_FALSE(slider_delegate.created_back_layer());
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.GestureScrollSequence(gfx::Point(10, 10),
gfx::Point(60, 10),
base::TimeDelta::FromMilliseconds(10),
10);
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_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 cancelled when a different type of event
// happens.
TEST_F(WindowSliderTest, WindowSlideIsCancelledOnEvent) {
scoped_ptr<aura::Window> window(CreateNormalWindow(0, root_window(), NULL));
WindowSliderDelegateTest slider_delegate;
ui::Event* events[] = {
new ui::MouseEvent(ui::ET_MOUSE_MOVED,
gfx::Point(55, 10),
gfx::Point(55, 10),
0),
new ui::KeyEvent(ui::ET_KEY_PRESSED,
ui::VKEY_A,
0,
true),
NULL
};
new WindowSlider(&slider_delegate, root_window(), window.get());
for (int i = 0; events[i]; ++i) {
// Generate a horizontal overscroll.
aura::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()->GetDispatcher(),
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_completed());
EXPECT_FALSE(slider_delegate.slider_destroyed());
slider_delegate.Reset();
}
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.
aura::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_completed());
EXPECT_FALSE(slider_delegate.created_front_layer());
EXPECT_FALSE(slider_delegate.slide_aborted());
EXPECT_FALSE(slider_delegate.slider_destroyed());
}
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);
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);
EXPECT_FALSE(slider_delegate.created_back_layer());
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.GestureScrollSequence(gfx::Point(10, 10),
gfx::Point(160, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
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());
}
// 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);
aura::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(160, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
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);
aura::test::EventGenerator generator(root_window());
// Generate a horizontal overscroll.
new WindowSlider(&slider_delegate, root_window(), window);
generator.GestureScrollSequence(gfx::Point(10, 10),
gfx::Point(160, 10),
base::TimeDelta::FromMilliseconds(10),
10);
EXPECT_TRUE(slider_delegate.created_back_layer());
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());
}
} // namespace content