summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc')
-rw-r--r--chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc1899
1 files changed, 1735 insertions, 164 deletions
diff --git a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 120956067df..d05bd44da8b 100644
--- a/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -5,20 +5,28 @@
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "base/basictypes.h"
+#include "base/command_line.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_metadata.h"
-#include "cc/output/gl_frame_data.h"
-#include "content/browser/aura/resize_lock.h"
+#include "cc/output/copy_output_request.h"
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/compositor/resize_lock.h"
+#include "content/browser/renderer_host/overscroll_controller.h"
+#include "content/browser/renderer_host/overscroll_controller_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/host_shared_bitmap_manager.h"
+#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "ipc/ipc_test_sink.h"
@@ -29,21 +37,32 @@
#include "ui/aura/client/window_tree_client.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
-#include "ui/aura/root_window.h"
#include "ui/aura/test/aura_test_helper.h"
+#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_cursor_client.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/compositor.h"
-#include "ui/compositor/test/test_context_factory.h"
+#include "ui/compositor/test/draw_waiter_for_test.h"
+#include "ui/compositor/test/in_process_context_factory.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
+#include "ui/events/gestures/gesture_configuration.h"
+#include "ui/wm/core/default_activation_client.h"
using testing::_;
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
namespace content {
namespace {
@@ -76,6 +95,61 @@ class TestScreenPositionClient
}
};
+class TestOverscrollDelegate : public OverscrollControllerDelegate {
+ public:
+ explicit TestOverscrollDelegate(RenderWidgetHostView* view)
+ : view_(view),
+ current_mode_(OVERSCROLL_NONE),
+ completed_mode_(OVERSCROLL_NONE),
+ delta_x_(0.f),
+ delta_y_(0.f) {}
+
+ virtual ~TestOverscrollDelegate() {}
+
+ OverscrollMode current_mode() const { return current_mode_; }
+ OverscrollMode completed_mode() const { return completed_mode_; }
+ float delta_x() const { return delta_x_; }
+ float delta_y() const { return delta_y_; }
+
+ void Reset() {
+ current_mode_ = OVERSCROLL_NONE;
+ completed_mode_ = OVERSCROLL_NONE;
+ delta_x_ = delta_y_ = 0.f;
+ }
+
+ private:
+ // Overridden from OverscrollControllerDelegate:
+ virtual gfx::Rect GetVisibleBounds() const OVERRIDE {
+ return view_->IsShowing() ? view_->GetViewBounds() : gfx::Rect();
+ }
+
+ virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE {
+ delta_x_ = delta_x;
+ delta_y_ = delta_y;
+ }
+
+ virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE {
+ EXPECT_EQ(current_mode_, overscroll_mode);
+ completed_mode_ = overscroll_mode;
+ current_mode_ = OVERSCROLL_NONE;
+ }
+
+ virtual void OnOverscrollModeChange(OverscrollMode old_mode,
+ OverscrollMode new_mode) OVERRIDE {
+ EXPECT_EQ(current_mode_, old_mode);
+ current_mode_ = new_mode;
+ delta_x_ = delta_y_ = 0.f;
+ }
+
+ RenderWidgetHostView* view_;
+ OverscrollMode current_mode_;
+ OverscrollMode completed_mode_;
+ float delta_x_;
+ float delta_y_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate);
+};
+
class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
public:
MockRenderWidgetHostDelegate() {}
@@ -113,6 +187,34 @@ class TestWindowObserver : public aura::WindowObserver {
DISALLOW_COPY_AND_ASSIGN(TestWindowObserver);
};
+class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber {
+ public:
+ FakeFrameSubscriber(gfx::Size size, base::Callback<void(bool)> callback)
+ : size_(size), callback_(callback) {}
+
+ virtual bool ShouldCaptureFrame(base::TimeTicks present_time,
+ scoped_refptr<media::VideoFrame>* storage,
+ DeliverFrameCallback* callback) OVERRIDE {
+ *storage = media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
+ size_,
+ gfx::Rect(size_),
+ size_,
+ base::TimeDelta());
+ *callback = base::Bind(&FakeFrameSubscriber::CallbackMethod, callback_);
+ return true;
+ }
+
+ static void CallbackMethod(base::Callback<void(bool)> callback,
+ base::TimeTicks timestamp,
+ bool success) {
+ callback.Run(success);
+ }
+
+ private:
+ gfx::Size size_;
+ base::Callback<void(bool)> callback_;
+};
+
class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
public:
FakeRenderWidgetHostViewAura(RenderWidgetHost* widget)
@@ -120,20 +222,38 @@ class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
virtual ~FakeRenderWidgetHostViewAura() {}
- virtual bool ShouldCreateResizeLock() OVERRIDE {
- gfx::Size desired_size = window()->bounds().size();
- return desired_size != current_frame_size();
- }
-
- virtual scoped_ptr<ResizeLock> CreateResizeLock(bool defer_compositor_lock)
- OVERRIDE {
+ virtual scoped_ptr<ResizeLock> CreateResizeLock(
+ bool defer_compositor_lock) OVERRIDE {
gfx::Size desired_size = window()->bounds().size();
return scoped_ptr<ResizeLock>(
new FakeResizeLock(desired_size, defer_compositor_lock));
}
void RunOnCompositingDidCommit() {
- OnCompositingDidCommit(window()->GetDispatcher()->compositor());
+ GetDelegatedFrameHost()->OnCompositingDidCommitForTesting(
+ window()->GetHost()->compositor());
+ }
+
+ virtual bool ShouldCreateResizeLock() OVERRIDE {
+ return GetDelegatedFrameHost()->ShouldCreateResizeLockForTesting();
+ }
+
+ virtual void RequestCopyOfOutput(scoped_ptr<cc::CopyOutputRequest> request)
+ OVERRIDE {
+ last_copy_request_ = request.Pass();
+ if (last_copy_request_->has_texture_mailbox()) {
+ // Give the resulting texture a size.
+ GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
+ GLuint texture = gl_helper->ConsumeMailboxToTexture(
+ last_copy_request_->texture_mailbox().mailbox(),
+ last_copy_request_->texture_mailbox().sync_point());
+ gl_helper->ResizeTexture(texture, window()->bounds().size());
+ gl_helper->DeleteTexture(texture);
+ }
+ }
+
+ cc::DelegatedFrameProvider* frame_provider() const {
+ return GetDelegatedFrameHost()->FrameProviderForTesting();
}
// A lock that doesn't actually do anything to the compositor, and does not
@@ -146,18 +266,58 @@ class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
bool has_resize_lock_;
gfx::Size last_frame_size_;
+ scoped_ptr<cc::CopyOutputRequest> last_copy_request_;
+};
+
+// A layout manager that always resizes a child to the root window size.
+class FullscreenLayoutManager : public aura::LayoutManager {
+ public:
+ explicit FullscreenLayoutManager(aura::Window* owner) : owner_(owner) {}
+ virtual ~FullscreenLayoutManager() {}
+
+ // Overridden from aura::LayoutManager:
+ virtual void OnWindowResized() OVERRIDE {
+ aura::Window::Windows::const_iterator i;
+ for (i = owner_->children().begin(); i != owner_->children().end(); ++i) {
+ (*i)->SetBounds(gfx::Rect());
+ }
+ }
+ virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
+ child->SetBounds(gfx::Rect());
+ }
+ virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
+ virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
+ virtual void OnChildWindowVisibilityChanged(aura::Window* child,
+ bool visible) OVERRIDE {}
+ virtual void SetChildBounds(aura::Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size()));
+ }
+
+ private:
+ aura::Window* owner_;
+ DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager);
+};
+
+class MockWindowObserver : public aura::WindowObserver {
+ public:
+ MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&));
};
+} // namespace
+
class RenderWidgetHostViewAuraTest : public testing::Test {
public:
RenderWidgetHostViewAuraTest()
: browser_thread_for_ui_(BrowserThread::UI, &message_loop_) {}
- virtual void SetUp() {
+ void SetUpEnvironment() {
+ ui::ContextFactory* context_factory = new ui::InProcessContextFactory;
ImageTransportFactory::InitializeForUnitTests(
- scoped_ptr<ui::ContextFactory>(new ui::TestContextFactory));
+ scoped_ptr<ui::ContextFactory>(context_factory));
aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
- aura_test_helper_->SetUp();
+ aura_test_helper_->SetUp(context_factory);
+ new wm::DefaultActivationClient(aura_test_helper_->root_window());
browser_context_.reset(new TestBrowserContext);
process_host_ = new MockRenderProcessHost(browser_context_.get());
@@ -166,8 +326,7 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
parent_host_ = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
- parent_view_ = static_cast<RenderWidgetHostViewAura*>(
- RenderWidgetHostView::CreateViewForWidget(parent_host_));
+ parent_view_ = new RenderWidgetHostViewAura(parent_host_);
parent_view_->InitAsChild(NULL);
aura::client::ParentWindowWithContext(parent_view_->GetNativeView(),
aura_test_helper_->root_window(),
@@ -176,12 +335,10 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
widget_host_ = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
widget_host_->Init();
- widget_host_->OnMessageReceived(
- ViewHostMsg_DidActivateAcceleratedCompositing(0, true));
view_ = new FakeRenderWidgetHostViewAura(widget_host_);
}
- virtual void TearDown() {
+ void TearDownEnvironment() {
sink_ = NULL;
process_host_ = NULL;
if (view_)
@@ -199,6 +356,10 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
ImageTransportFactory::Terminate();
}
+ virtual void SetUp() { SetUpEnvironment(); }
+
+ virtual void TearDown() { TearDownEnvironment(); }
+
protected:
base::MessageLoopForUI message_loop_;
BrowserThreadImpl browser_thread_for_ui_;
@@ -223,46 +384,239 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraTest);
};
-// A layout manager that always resizes a child to the root window size.
-class FullscreenLayoutManager : public aura::LayoutManager {
+class RenderWidgetHostViewAuraOverscrollTest
+ : public RenderWidgetHostViewAuraTest {
public:
- explicit FullscreenLayoutManager(aura::Window* owner)
- : owner_(owner) {}
- virtual ~FullscreenLayoutManager() {}
+ RenderWidgetHostViewAuraOverscrollTest() {}
- // Overridden from aura::LayoutManager:
- virtual void OnWindowResized() OVERRIDE {
- aura::Window::Windows::const_iterator i;
- for (i = owner_->children().begin(); i != owner_->children().end(); ++i) {
- (*i)->SetBounds(gfx::Rect());
- }
+ // We explicitly invoke SetUp to allow gesture debounce customization.
+ virtual void SetUp() {}
+
+ protected:
+ void SetUpOverscrollEnvironmentWithDebounce(int debounce_interval_in_ms) {
+ SetUpOverscrollEnvironmentImpl(debounce_interval_in_ms);
}
- virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
- child->SetBounds(gfx::Rect());
+
+ void SetUpOverscrollEnvironment() { SetUpOverscrollEnvironmentImpl(0); }
+
+ void SetUpOverscrollEnvironmentImpl(int debounce_interval_in_ms) {
+ ui::GestureConfiguration::set_scroll_debounce_interval_in_ms(
+ debounce_interval_in_ms);
+
+ RenderWidgetHostViewAuraTest::SetUp();
+
+ view_->SetOverscrollControllerEnabled(true);
+ overscroll_delegate_.reset(new TestOverscrollDelegate(view_));
+ view_->overscroll_controller()->set_delegate(overscroll_delegate_.get());
+
+ view_->InitAsChild(NULL);
+ view_->SetBounds(gfx::Rect(0, 0, 400, 200));
+ view_->Show();
+
+ sink_->ClearMessages();
}
- virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {
+
+ // TODO(jdduke): Simulate ui::Events, injecting through the view.
+ void SimulateMouseEvent(WebInputEvent::Type type) {
+ widget_host_->ForwardMouseEvent(SyntheticWebMouseEventBuilder::Build(type));
}
- virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {
+
+ void SimulateMouseEventWithLatencyInfo(WebInputEvent::Type type,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardMouseEventWithLatencyInfo(
+ SyntheticWebMouseEventBuilder::Build(type), ui_latency);
}
- virtual void OnChildWindowVisibilityChanged(aura::Window* child,
- bool visible) OVERRIDE {
+
+ void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) {
+ widget_host_->ForwardWheelEvent(
+ SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise));
}
- virtual void SetChildBounds(aura::Window* child,
- const gfx::Rect& requested_bounds) OVERRIDE {
- SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size()));
+
+ void SimulateWheelEventWithLatencyInfo(float dX,
+ float dY,
+ int modifiers,
+ bool precise,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardWheelEventWithLatencyInfo(
+ SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise),
+ ui_latency);
+ }
+
+ void SimulateMouseMove(int x, int y, int modifiers) {
+ SimulateMouseEvent(WebInputEvent::MouseMove, x, y, modifiers, false);
+ }
+
+ void SimulateMouseEvent(WebInputEvent::Type type,
+ int x,
+ int y,
+ int modifiers,
+ bool pressed) {
+ WebMouseEvent event =
+ SyntheticWebMouseEventBuilder::Build(type, x, y, modifiers);
+ if (pressed)
+ event.button = WebMouseEvent::ButtonLeft;
+ widget_host_->ForwardMouseEvent(event);
+ }
+
+ void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) {
+ widget_host_->ForwardWheelEvent(
+ SyntheticWebMouseWheelEventBuilder::Build(phase));
+ }
+
+ // Inject provided synthetic WebGestureEvent instance.
+ void SimulateGestureEventCore(const WebGestureEvent& gesture_event) {
+ widget_host_->ForwardGestureEvent(gesture_event);
+ }
+
+ void SimulateGestureEventCoreWithLatencyInfo(
+ const WebGestureEvent& gesture_event,
+ const ui::LatencyInfo& ui_latency) {
+ widget_host_->ForwardGestureEventWithLatencyInfo(gesture_event, ui_latency);
}
+ // Inject simple synthetic WebGestureEvent instances.
+ void SimulateGestureEvent(WebInputEvent::Type type,
+ blink::WebGestureDevice sourceDevice) {
+ SimulateGestureEventCore(
+ SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
+ }
+
+ void SimulateGestureEventWithLatencyInfo(WebInputEvent::Type type,
+ blink::WebGestureDevice sourceDevice,
+ const ui::LatencyInfo& ui_latency) {
+ SimulateGestureEventCoreWithLatencyInfo(
+ SyntheticWebGestureEventBuilder::Build(type, sourceDevice), ui_latency);
+ }
+
+ void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) {
+ SimulateGestureEventCore(
+ SyntheticWebGestureEventBuilder::BuildScrollUpdate(dX, dY, modifiers));
+ }
+
+ void SimulateGesturePinchUpdateEvent(float scale,
+ float anchorX,
+ float anchorY,
+ int modifiers) {
+ SimulateGestureEventCore(SyntheticWebGestureEventBuilder::BuildPinchUpdate(
+ scale,
+ anchorX,
+ anchorY,
+ modifiers,
+ blink::WebGestureDeviceTouchscreen));
+ }
+
+ // Inject synthetic GestureFlingStart events.
+ void SimulateGestureFlingStartEvent(float velocityX,
+ float velocityY,
+ blink::WebGestureDevice sourceDevice) {
+ SimulateGestureEventCore(SyntheticWebGestureEventBuilder::BuildFling(
+ velocityX, velocityY, sourceDevice));
+ }
+
+ void SendInputEventACK(WebInputEvent::Type type,
+ InputEventAckState ack_result) {
+ InputHostMsg_HandleInputEvent_ACK_Params ack;
+ ack.type = type;
+ ack.state = ack_result;
+ InputHostMsg_HandleInputEvent_ACK response(0, ack);
+ widget_host_->OnMessageReceived(response);
+ }
+
+ bool ScrollStateIsContentScrolling() const {
+ return scroll_state() == OverscrollController::STATE_CONTENT_SCROLLING;
+ }
+
+ bool ScrollStateIsOverscrolling() const {
+ return scroll_state() == OverscrollController::STATE_OVERSCROLLING;
+ }
+
+ bool ScrollStateIsUnknown() const {
+ return scroll_state() == OverscrollController::STATE_UNKNOWN;
+ }
+
+ OverscrollController::ScrollState scroll_state() const {
+ return view_->overscroll_controller()->scroll_state_;
+ }
+
+ OverscrollMode overscroll_mode() const {
+ return view_->overscroll_controller()->overscroll_mode_;
+ }
+
+ float overscroll_delta_x() const {
+ return view_->overscroll_controller()->overscroll_delta_x_;
+ }
+
+ float overscroll_delta_y() const {
+ return view_->overscroll_controller()->overscroll_delta_y_;
+ }
+
+ TestOverscrollDelegate* overscroll_delegate() {
+ return overscroll_delegate_.get();
+ }
+
+ void SendTouchEvent() {
+ widget_host_->ForwardTouchEventWithLatencyInfo(touch_event_,
+ ui::LatencyInfo());
+ touch_event_.ResetPoints();
+ }
+
+ void PressTouchPoint(int x, int y) {
+ touch_event_.PressPoint(x, y);
+ SendTouchEvent();
+ }
+
+ void MoveTouchPoint(int index, int x, int y) {
+ touch_event_.MovePoint(index, x, y);
+ SendTouchEvent();
+ }
+
+ void ReleaseTouchPoint(int index) {
+ touch_event_.ReleasePoint(index);
+ SendTouchEvent();
+ }
+
+ size_t GetSentMessageCountAndResetSink() {
+ size_t count = sink_->message_count();
+ sink_->ClearMessages();
+ return count;
+ }
+
+ void AckLastSentInputEventIfNecessary(InputEventAckState ack_result) {
+ if (!sink_->message_count())
+ return;
+
+ InputMsg_HandleInputEvent::Param params;
+ if (!InputMsg_HandleInputEvent::Read(
+ sink_->GetMessageAt(sink_->message_count() - 1), &params)) {
+ return;
+ }
+
+ if (WebInputEventTraits::IgnoresAckDisposition(*params.a))
+ return;
+
+ SendInputEventACK(params.a->type, ack_result);
+ }
+
+ SyntheticWebTouchEvent touch_event_;
+
+ scoped_ptr<TestOverscrollDelegate> overscroll_delegate_;
+
private:
- aura::Window* owner_;
- DISALLOW_COPY_AND_ASSIGN(FullscreenLayoutManager);
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraOverscrollTest);
};
-class MockWindowObserver : public aura::WindowObserver {
+class RenderWidgetHostViewAuraShutdownTest
+ : public RenderWidgetHostViewAuraTest {
public:
- MOCK_METHOD2(OnWindowPaintScheduled, void(aura::Window*, const gfx::Rect&));
-};
+ RenderWidgetHostViewAuraShutdownTest() {}
-} // namespace
+ virtual void TearDown() OVERRIDE {
+ // No TearDownEnvironment here, we do this explicitly during the test.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraShutdownTest);
+};
// Checks that a fullscreen view has the correct show-state and receives the
// focus.
@@ -331,7 +685,7 @@ TEST_F(RenderWidgetHostViewAuraTest, DestroyFullscreenOnBlur) {
TestWindowObserver observer(window);
aura::test::TestWindowDelegate delegate;
scoped_ptr<aura::Window> sibling(new aura::Window(&delegate));
- sibling->Init(ui::LAYER_TEXTURED);
+ sibling->Init(aura::WINDOW_LAYER_TEXTURED);
sibling->Show();
window->parent()->AddChild(sibling.get());
sibling->Focus();
@@ -342,21 +696,75 @@ TEST_F(RenderWidgetHostViewAuraTest, DestroyFullscreenOnBlur) {
view_ = NULL;
}
+// Checks that a popup view is destroyed when a user clicks outside of the popup
+// view and focus does not change. This is the case when the user clicks on the
+// desktop background on Chrome OS.
+TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupClickOutsidePopup) {
+ parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
+ parent_view_->Focus();
+ EXPECT_TRUE(parent_view_->HasFocus());
+
+ view_->InitAsPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
+ aura::Window* window = view_->GetNativeView();
+ ASSERT_TRUE(window != NULL);
+
+ gfx::Point click_point;
+ EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(click_point));
+ aura::Window* parent_window = parent_view_->GetNativeView();
+ EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(click_point));
+
+ TestWindowObserver observer(window);
+ aura::test::EventGenerator generator(window->GetRootWindow(), click_point);
+ generator.ClickLeftButton();
+ ASSERT_TRUE(parent_view_->HasFocus());
+ ASSERT_TRUE(observer.destroyed());
+
+ widget_host_ = NULL;
+ view_ = NULL;
+}
+
+// Checks that a popup view is destroyed when a user taps outside of the popup
+// view and focus does not change. This is the case when the user taps the
+// desktop background on Chrome OS.
+TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupTapOutsidePopup) {
+ parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
+ parent_view_->Focus();
+ EXPECT_TRUE(parent_view_->HasFocus());
+
+ view_->InitAsPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
+ aura::Window* window = view_->GetNativeView();
+ ASSERT_TRUE(window != NULL);
+
+ gfx::Point tap_point;
+ EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(tap_point));
+ aura::Window* parent_window = parent_view_->GetNativeView();
+ EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(tap_point));
+
+ TestWindowObserver observer(window);
+ aura::test::EventGenerator generator(window->GetRootWindow(), tap_point);
+ generator.GestureTapAt(tap_point);
+ ASSERT_TRUE(parent_view_->HasFocus());
+ ASSERT_TRUE(observer.destroyed());
+
+ widget_host_ = NULL;
+ view_ = NULL;
+}
+
// Checks that IME-composition-event state is maintained correctly.
TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) {
view_->InitAsChild(NULL);
view_->Show();
ui::CompositionText composition_text;
- composition_text.text = ASCIIToUTF16("|a|b");
+ composition_text.text = base::ASCIIToUTF16("|a|b");
// Focused segment
composition_text.underlines.push_back(
- ui::CompositionUnderline(0, 3, 0xff000000, true));
+ ui::CompositionUnderline(0, 3, 0xff000000, true, 0x78563412));
- // Non-focused segment
+ // Non-focused segment, with different background color.
composition_text.underlines.push_back(
- ui::CompositionUnderline(3, 4, 0xff000000, false));
+ ui::CompositionUnderline(3, 4, 0xff000000, false, 0xefcdab90));
const ui::CompositionUnderlines& underlines = composition_text.underlines;
@@ -382,6 +790,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) {
EXPECT_EQ(underlines[i].end_offset, params.b[i].endOffset);
EXPECT_EQ(underlines[i].color, params.b[i].color);
EXPECT_EQ(underlines[i].thick, params.b[i].thick);
+ EXPECT_EQ(underlines[i].background_color, params.b[i].backgroundColor);
}
// highlighted range
EXPECT_EQ(4, params.c) << "Should be the same to the caret pos";
@@ -417,6 +826,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&press);
EXPECT_FALSE(press.handled());
EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StatePressed,
view_->touch_event_.touches[0].state);
@@ -424,6 +834,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&move);
EXPECT_FALSE(move.handled());
EXPECT_EQ(blink::WebInputEvent::TouchMove, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StateMoved,
view_->touch_event_.touches[0].state);
@@ -431,6 +842,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&release);
EXPECT_FALSE(release.handled());
EXPECT_EQ(blink::WebInputEvent::TouchEnd, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(0U, view_->touch_event_.touchesLength);
// Now install some touch-event handlers and do the same steps. The touch
@@ -442,6 +854,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&press);
EXPECT_TRUE(press.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StatePressed,
view_->touch_event_.touches[0].state);
@@ -449,6 +862,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&move);
EXPECT_TRUE(move.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchMove, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(1U, view_->touch_event_.touchesLength);
EXPECT_EQ(blink::WebTouchPoint::StateMoved,
view_->touch_event_.touches[0].state);
@@ -456,6 +870,7 @@ TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {
view_->OnTouchEvent(&release);
EXPECT_TRUE(release.stopped_propagation());
EXPECT_EQ(blink::WebInputEvent::TouchEnd, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
EXPECT_EQ(0U, view_->touch_event_.touchesLength);
// Now start a touch event, and remove the event-handlers before the release.
@@ -678,33 +1093,49 @@ TEST_F(RenderWidgetHostViewAuraTest, CursorVisibilityChange) {
cursor_client.RemoveObserver(view_);
}
-scoped_ptr<cc::CompositorFrame> MakeGLFrame(float scale_factor,
- gfx::Size size,
- gfx::Rect damage) {
- scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
- frame->metadata.device_scale_factor = scale_factor;
- frame->gl_frame_data.reset(new cc::GLFrameData);
- frame->gl_frame_data->sync_point = 1;
- memset(frame->gl_frame_data->mailbox.name, '1', 64);
- frame->gl_frame_data->size = size;
- frame->gl_frame_data->sub_buffer_rect = damage;
- return frame.Pass();
-}
+TEST_F(RenderWidgetHostViewAuraTest, UpdateCursorIfOverSelf) {
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
-scoped_ptr<cc::CompositorFrame> MakeSoftwareFrame(float scale_factor,
- gfx::Size size,
- gfx::Rect damage) {
- scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
- frame->metadata.device_scale_factor = scale_factor;
- frame->software_frame_data.reset(new cc::SoftwareFrameData);
- frame->software_frame_data->id = 1;
- frame->software_frame_data->size = size;
- frame->software_frame_data->damage_rect = damage;
- base::SharedMemory shm;
- shm.CreateAndMapAnonymous(size.GetArea() * 4);
- shm.GiveToProcess(base::GetCurrentProcessHandle(),
- &frame->software_frame_data->handle);
- return frame.Pass();
+ // Note that all coordinates in this test are screen coordinates.
+ view_->SetBounds(gfx::Rect(60, 60, 100, 100));
+ view_->Show();
+
+ aura::test::TestCursorClient cursor_client(
+ parent_view_->GetNativeView()->GetRootWindow());
+
+ // Cursor is in the middle of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(110, 110));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is near the top of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(80, 65));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is near the bottom of the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(159, 159));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(1, cursor_client.calls_to_set_cursor());
+
+ // Cursor is above the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(67, 59));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(0, cursor_client.calls_to_set_cursor());
+
+ // Cursor is below the window.
+ cursor_client.reset_calls_to_set_cursor();
+ aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(161, 161));
+ view_->UpdateCursorIfOverSelf();
+ EXPECT_EQ(0, cursor_client.calls_to_set_cursor());
}
scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor,
@@ -715,10 +1146,8 @@ scoped_ptr<cc::CompositorFrame> MakeDelegatedFrame(float scale_factor,
frame->delegated_frame_data.reset(new cc::DelegatedFrameData);
scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create();
- pass->SetNew(cc::RenderPass::Id(1, 1),
- gfx::Rect(size),
- gfx::RectF(damage),
- gfx::Transform());
+ pass->SetNew(
+ cc::RenderPass::Id(1, 1), gfx::Rect(size), damage, gfx::Transform());
frame->delegated_frame_data->render_pass_list.push_back(pass.Pass());
return frame.Pass();
}
@@ -746,9 +1175,11 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) {
// Resizes are blocked until we swapped a frame of the correct size, and
// we've committed it.
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
+ 0,
+ MakeDelegatedFrame(
+ 1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
ui::DrawWaiterForTest::WaitForCommit(
- root_window->GetDispatcher()->compositor());
+ root_window->GetHost()->compositor());
}
widget_host_->ResetSizeAndRepaintPendingFlags();
@@ -767,9 +1198,11 @@ TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) {
gfx::Rect(params.a.screen_info.availableRect).ToString());
EXPECT_EQ("1600x1200", params.a.new_size.ToString());
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
+ 0,
+ MakeDelegatedFrame(
+ 1.f, params.a.new_size, gfx::Rect(params.a.new_size)));
ui::DrawWaiterForTest::WaitForCommit(
- root_window->GetDispatcher()->compositor());
+ root_window->GetHost()->compositor());
}
}
@@ -789,78 +1222,105 @@ TEST_F(RenderWidgetHostViewAuraTest, SwapNotifiesWindow) {
MockWindowObserver observer;
view_->window_->AddObserver(&observer);
- // Swap a frame through the GPU path.
- GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
- params.surface_id = widget_host_->surface_id();
- params.route_id = widget_host_->GetRoutingID();
- params.mailbox_name = std::string(64, '1');
- params.size = view_size;
- params.scale_factor = 1.f;
-
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->AcceleratedSurfaceBuffersSwapped(params, 0);
- testing::Mock::VerifyAndClearExpectations(&observer);
-
- // DSF = 2
- params.size = gfx::Size(200, 200);
- params.scale_factor = 2.f;
+ // Delegated renderer path
EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->AcceleratedSurfaceBuffersSwapped(params, 0);
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_size, view_rect));
testing::Mock::VerifyAndClearExpectations(&observer);
- // Partial frames though GPU path
- GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params post_params;
- post_params.surface_id = widget_host_->surface_id();
- post_params.route_id = widget_host_->GetRoutingID();
- post_params.mailbox_name = std::string(64, '1');
- post_params.surface_size = gfx::Size(200, 200);
- post_params.surface_scale_factor = 2.f;
- post_params.x = 40;
- post_params.y = 40;
- post_params.width = 80;
- post_params.height = 80;
- // rect from params is upside down, and is inflated in RWHVA, just because.
EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(19, 39, 42, 42)));
- view_->AcceleratedSurfacePostSubBuffer(post_params, 0);
+ gfx::Rect(5, 5, 5, 5)));
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
testing::Mock::VerifyAndClearExpectations(&observer);
- // Composite-to-mailbox path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->OnSwapCompositorFrame(0, MakeGLFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ view_->window_->RemoveObserver(&observer);
+}
- // rect from GL frame is upside down, and is inflated in RWHVA, just because.
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(4, 89, 7, 7)));
+TEST_F(RenderWidgetHostViewAuraTest, Resize) {
+ gfx::Size size1(100, 100);
+ gfx::Size size2(200, 200);
+ gfx::Size size3(300, 300);
+
+ aura::Window* root_window = parent_view_->GetNativeView()->GetRootWindow();
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(), root_window, gfx::Rect(size1));
+ view_->WasShown();
+ view_->SetSize(size1);
view_->OnSwapCompositorFrame(
- 0, MakeGLFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ 0, MakeDelegatedFrame(1.f, size1, gfx::Rect(size1)));
+ ui::DrawWaiterForTest::WaitForCommit(
+ root_window->GetHost()->compositor());
+ ViewHostMsg_UpdateRect_Params update_params;
+ update_params.view_size = size1;
+ update_params.scale_factor = 1.f;
+ update_params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
+ // Resize logic is idle (no pending resize, no pending commit).
+ EXPECT_EQ(size1.ToString(), view_->GetRequestedRendererSize().ToString());
- // Software path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
- view_->OnSwapCompositorFrame(0, MakeSoftwareFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ // Resize renderer, should produce a Resize message
+ view_->SetSize(size2);
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(1u, sink_->message_count());
+ {
+ const IPC::Message* msg = sink_->GetMessageAt(0);
+ EXPECT_EQ(ViewMsg_Resize::ID, msg->type());
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(msg, &params);
+ EXPECT_EQ(size2.ToString(), params.a.new_size.ToString());
+ }
+ // Send resize ack to observe new Resize messages.
+ update_params.view_size = size2;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(5, 5, 5, 5)));
- view_->OnSwapCompositorFrame(
- 0, MakeSoftwareFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ // Resize renderer again, before receiving a frame. Should not produce a
+ // Resize message.
+ view_->SetSize(size3);
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(0u, sink_->message_count());
- // Delegated renderer path
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
+ // Receive a frame of the new size, should be skipped and not produce a Resize
+ // message.
view_->OnSwapCompositorFrame(
- 0, MakeDelegatedFrame(1.f, view_size, view_rect));
- testing::Mock::VerifyAndClearExpectations(&observer);
+ 0, MakeDelegatedFrame(1.f, size3, gfx::Rect(size3)));
+ // Expect the frame ack;
+ EXPECT_EQ(1u, sink_->message_count());
+ EXPECT_EQ(ViewMsg_SwapCompositorFrameAck::ID, sink_->GetMessageAt(0)->type());
+ sink_->ClearMessages();
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
- EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_,
- gfx::Rect(5, 5, 5, 5)));
+ // Receive a frame of the correct size, should not be skipped and, and should
+ // produce a Resize message after the commit.
view_->OnSwapCompositorFrame(
- 0, MakeDelegatedFrame(1.f, view_size, gfx::Rect(5, 5, 5, 5)));
- testing::Mock::VerifyAndClearExpectations(&observer);
-
- view_->window_->RemoveObserver(&observer);
+ 0, MakeDelegatedFrame(1.f, size2, gfx::Rect(size2)));
+ // No frame ack yet.
+ EXPECT_EQ(0u, sink_->message_count());
+ EXPECT_EQ(size2.ToString(), view_->GetRequestedRendererSize().ToString());
+
+ // Wait for commit, then we should unlock the compositor and send a Resize
+ // message (and a frame ack)
+ ui::DrawWaiterForTest::WaitForCommit(
+ root_window->GetHost()->compositor());
+ EXPECT_EQ(size3.ToString(), view_->GetRequestedRendererSize().ToString());
+ EXPECT_EQ(2u, sink_->message_count());
+ EXPECT_EQ(ViewMsg_SwapCompositorFrameAck::ID, sink_->GetMessageAt(0)->type());
+ {
+ const IPC::Message* msg = sink_->GetMessageAt(1);
+ EXPECT_EQ(ViewMsg_Resize::ID, msg->type());
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(msg, &params);
+ EXPECT_EQ(size3.ToString(), params.a.new_size.ToString());
+ }
+ update_params.view_size = size3;
+ widget_host_->OnMessageReceived(
+ ViewHostMsg_UpdateRect(widget_host_->GetRoutingID(), update_params));
+ sink_->ClearMessages();
}
// Skipped frames should not drop their damage.
@@ -897,7 +1357,6 @@ TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) {
// Lock the compositor. Now we should drop frames.
view_rect = gfx::Rect(150, 150);
view_->SetSize(view_rect.size());
- view_->MaybeCreateResizeLock();
// This frame is dropped.
gfx::Rect dropped_damage_rect_1(10, 20, 30, 40);
@@ -934,6 +1393,21 @@ TEST_F(RenderWidgetHostViewAuraTest, SkippedDelegatedFrames) {
view_->RunOnCompositingDidCommit();
+ // Resize to something empty.
+ view_rect = gfx::Rect(100, 0);
+ view_->SetSize(view_rect.size());
+
+ // We're never expecting empty frames, resize to something non-empty.
+ view_rect = gfx::Rect(100, 100);
+ view_->SetSize(view_rect.size());
+
+ // This frame should not be dropped.
+ EXPECT_CALL(observer, OnWindowPaintScheduled(view_->window_, view_rect));
+ view_->OnSwapCompositorFrame(
+ 0, MakeDelegatedFrame(1.f, view_rect.size(), view_rect));
+ testing::Mock::VerifyAndClearExpectations(&observer);
+ view_->RunOnCompositingDidCommit();
+
view_->window_->RemoveObserver(&observer);
}
@@ -988,6 +1462,7 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
size_t renderer_count = max_renderer_frames + 1;
gfx::Rect view_rect(100, 100);
gfx::Size frame_size = view_rect.size();
+ DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
scoped_ptr<RenderWidgetHostImpl * []> hosts(
new RenderWidgetHostImpl* [renderer_count]);
@@ -999,8 +1474,6 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
hosts[i] = new RenderWidgetHostImpl(
&delegate_, process_host_, MSG_ROUTING_NONE, false);
hosts[i]->Init();
- hosts[i]->OnMessageReceived(
- ViewHostMsg_DidActivateAcceleratedCompositing(0, true));
views[i] = new FakeRenderWidgetHostViewAura(hosts[i]);
views[i]->InitAsChild(NULL);
aura::client::ParentWindowWithContext(
@@ -1015,37 +1488,37 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[i]->WasShown();
views[i]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
views[i]->WasHidden();
}
// There should be max_renderer_frames with a frame in it, and one without it.
// Since the logic is LRU eviction, the first one should be without.
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
for (size_t i = 1; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// LRU renderer is [0], make it visible, it shouldn't evict anything yet.
views[0]->WasShown();
- EXPECT_FALSE(views[0]->frame_provider_);
- EXPECT_TRUE(views[1]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
+ EXPECT_TRUE(views[1]->frame_provider());
// Swap a frame on it, it should evict the next LRU [1].
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[0]->frame_provider_);
- EXPECT_FALSE(views[1]->frame_provider_);
+ EXPECT_TRUE(views[0]->frame_provider());
+ EXPECT_FALSE(views[1]->frame_provider());
views[0]->WasHidden();
// LRU renderer is [1], still hidden. Swap a frame on it, it should evict
// the next LRU [2].
views[1]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[0]->frame_provider_);
- EXPECT_TRUE(views[1]->frame_provider_);
- EXPECT_FALSE(views[2]->frame_provider_);
+ EXPECT_TRUE(views[0]->frame_provider());
+ EXPECT_TRUE(views[1]->frame_provider());
+ EXPECT_FALSE(views[2]->frame_provider());
for (size_t i = 3; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// Make all renderers but [0] visible and swap a frame on them, keep [0]
// hidden, it becomes the LRU.
@@ -1053,14 +1526,14 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[i]->WasShown();
views[i]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
}
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
// Swap a frame on [0], it should be evicted immediately.
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
// Make [0] visible, and swap a frame on it. Nothing should be evicted
// although we're above the limit.
@@ -1068,11 +1541,100 @@ TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {
views[0]->OnSwapCompositorFrame(
1, MakeDelegatedFrame(1.f, frame_size, view_rect));
for (size_t i = 0; i < renderer_count; ++i)
- EXPECT_TRUE(views[i]->frame_provider_);
+ EXPECT_TRUE(views[i]->frame_provider());
// Make [0] hidden, it should evict its frame.
views[0]->WasHidden();
- EXPECT_FALSE(views[0]->frame_provider_);
+ EXPECT_FALSE(views[0]->frame_provider());
+
+ for (size_t i = 0; i < renderer_count - 1; ++i)
+ views[i]->WasHidden();
+
+ // Allocate enough bitmaps so that two frames (proportionally) would be
+ // enough hit the handle limit.
+ int handles_per_frame = 5;
+ RendererFrameManager::GetInstance()->set_max_handles(handles_per_frame * 2);
+
+ for (size_t i = 0; i < (renderer_count - 1) * handles_per_frame; i++) {
+ HostSharedBitmapManager::current()->ChildAllocatedSharedBitmap(
+ 1,
+ base::SharedMemory::NULLHandle(),
+ base::GetCurrentProcessHandle(),
+ cc::SharedBitmap::GenerateId());
+ }
+
+ // Hiding this last bitmap should evict all but two frames.
+ views[renderer_count - 1]->WasHidden();
+ for (size_t i = 0; i < renderer_count; ++i) {
+ if (i + 2 < renderer_count)
+ EXPECT_FALSE(views[i]->frame_provider());
+ else
+ EXPECT_TRUE(views[i]->frame_provider());
+ }
+ HostSharedBitmapManager::current()->ProcessRemoved(
+ base::GetCurrentProcessHandle());
+ RendererFrameManager::GetInstance()->set_max_handles(
+ base::SharedMemory::GetHandleLimit());
+
+ for (size_t i = 0; i < renderer_count; ++i) {
+ views[i]->Destroy();
+ delete hosts[i];
+ }
+}
+
+TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithLocking) {
+ size_t max_renderer_frames =
+ RendererFrameManager::GetInstance()->max_number_of_saved_frames();
+ ASSERT_LE(2u, max_renderer_frames);
+ size_t renderer_count = max_renderer_frames + 1;
+ gfx::Rect view_rect(100, 100);
+ gfx::Size frame_size = view_rect.size();
+ DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
+
+ scoped_ptr<RenderWidgetHostImpl * []> hosts(
+ new RenderWidgetHostImpl* [renderer_count]);
+ scoped_ptr<FakeRenderWidgetHostViewAura * []> views(
+ new FakeRenderWidgetHostViewAura* [renderer_count]);
+
+ // Create a bunch of renderers.
+ for (size_t i = 0; i < renderer_count; ++i) {
+ hosts[i] = new RenderWidgetHostImpl(
+ &delegate_, process_host_, MSG_ROUTING_NONE, false);
+ hosts[i]->Init();
+ views[i] = new FakeRenderWidgetHostViewAura(hosts[i]);
+ views[i]->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ views[i]->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ views[i]->SetSize(view_rect.size());
+ }
+
+ // Make each renderer visible and swap a frame on it. No eviction should
+ // occur because all frames are visible.
+ for (size_t i = 0; i < renderer_count; ++i) {
+ views[i]->WasShown();
+ views[i]->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, frame_size, view_rect));
+ EXPECT_TRUE(views[i]->frame_provider());
+ }
+
+ // If we hide [0], then [0] should be evicted.
+ views[0]->WasHidden();
+ EXPECT_FALSE(views[0]->frame_provider());
+
+ // If we lock [0] before hiding it, then [0] should not be evicted.
+ views[0]->WasShown();
+ views[0]->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, frame_size, view_rect));
+ EXPECT_TRUE(views[0]->frame_provider());
+ views[0]->GetDelegatedFrameHost()->LockResources();
+ views[0]->WasHidden();
+ EXPECT_TRUE(views[0]->frame_provider());
+
+ // If we unlock [0] now, then [0] should be evicted.
+ views[0]->GetDelegatedFrameHost()->UnlockResources();
+ EXPECT_FALSE(views[0]->frame_provider());
for (size_t i = 0; i < renderer_count; ++i) {
views[i]->Destroy();
@@ -1098,7 +1660,7 @@ TEST_F(RenderWidgetHostViewAuraTest, SoftwareDPIChange) {
// Save the frame provider.
scoped_refptr<cc::DelegatedFrameProvider> frame_provider =
- view_->frame_provider_;
+ view_->frame_provider();
// This frame will have the same number of physical pixels, but has a new
// scale on it.
@@ -1108,7 +1670,1016 @@ TEST_F(RenderWidgetHostViewAuraTest, SoftwareDPIChange) {
// When we get a new frame with the same frame size in physical pixels, but a
// different scale, we should generate a new frame provider, as the final
// result will need to be scaled differently to the screen.
- EXPECT_NE(frame_provider.get(), view_->frame_provider_.get());
+ EXPECT_NE(frame_provider.get(), view_->frame_provider());
+}
+
+class RenderWidgetHostViewAuraCopyRequestTest
+ : public RenderWidgetHostViewAuraShutdownTest {
+ public:
+ RenderWidgetHostViewAuraCopyRequestTest()
+ : callback_count_(0), result_(false) {}
+
+ void CallbackMethod(const base::Closure& quit_closure, bool result) {
+ result_ = result;
+ callback_count_++;
+ quit_closure.Run();
+ }
+
+ int callback_count_;
+ bool result_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraCopyRequestTest);
+};
+
+TEST_F(RenderWidgetHostViewAuraCopyRequestTest, DestroyedAfterCopyRequest) {
+ base::RunLoop run_loop;
+
+ gfx::Rect view_rect(100, 100);
+ scoped_ptr<cc::CopyOutputRequest> request;
+
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ view_->SetSize(view_rect.size());
+ view_->WasShown();
+
+ scoped_ptr<FakeFrameSubscriber> frame_subscriber(new FakeFrameSubscriber(
+ view_rect.size(),
+ base::Bind(&RenderWidgetHostViewAuraCopyRequestTest::CallbackMethod,
+ base::Unretained(this),
+ run_loop.QuitClosure())));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_FALSE(view_->last_copy_request_);
+
+ view_->BeginFrameSubscription(
+ frame_subscriber.PassAs<RenderWidgetHostViewFrameSubscriber>());
+ view_->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, view_rect.size(), gfx::Rect(view_rect)));
+
+ EXPECT_EQ(0, callback_count_);
+ EXPECT_TRUE(view_->last_copy_request_);
+ EXPECT_TRUE(view_->last_copy_request_->has_texture_mailbox());
+ request = view_->last_copy_request_.Pass();
+
+ // Send back the mailbox included in the request. There's no release callback
+ // since the mailbox came from the RWHVA originally.
+ request->SendTextureResult(view_rect.size(),
+ request->texture_mailbox(),
+ scoped_ptr<cc::SingleReleaseCallback>());
+
+ // This runs until the callback happens.
+ run_loop.Run();
+
+ // The callback should succeed.
+ EXPECT_EQ(1, callback_count_);
+ EXPECT_TRUE(result_);
+
+ view_->OnSwapCompositorFrame(
+ 1, MakeDelegatedFrame(1.f, view_rect.size(), gfx::Rect(view_rect)));
+
+ EXPECT_EQ(1, callback_count_);
+ request = view_->last_copy_request_.Pass();
+
+ // Destroy the RenderWidgetHostViewAura and ImageTransportFactory.
+ TearDownEnvironment();
+
+ // Send back the mailbox included in the request. There's no release callback
+ // since the mailbox came from the RWHVA originally.
+ request->SendTextureResult(view_rect.size(),
+ request->texture_mailbox(),
+ scoped_ptr<cc::SingleReleaseCallback>());
+
+ // Because the copy request callback may be holding state within it, that
+ // state must handle the RWHVA and ImageTransportFactory going away before the
+ // callback is called. This test passes if it does not crash as a result of
+ // these things being destroyed.
+ EXPECT_EQ(2, callback_count_);
+ EXPECT_FALSE(result_);
+}
+
+TEST_F(RenderWidgetHostViewAuraTest, VisibleViewportTest) {
+ gfx::Rect view_rect(100, 100);
+
+ view_->InitAsChild(NULL);
+ aura::client::ParentWindowWithContext(
+ view_->GetNativeView(),
+ parent_view_->GetNativeView()->GetRootWindow(),
+ gfx::Rect());
+ view_->SetSize(view_rect.size());
+ view_->WasShown();
+
+ // Defaults to full height of the view.
+ EXPECT_EQ(100, view_->GetVisibleViewportSize().height());
+
+ widget_host_->ResetSizeAndRepaintPendingFlags();
+ sink_->ClearMessages();
+ view_->SetInsets(gfx::Insets(0, 0, 40, 0));
+
+ EXPECT_EQ(60, view_->GetVisibleViewportSize().height());
+
+ const IPC::Message *message = sink_->GetFirstMessageMatching(
+ ViewMsg_Resize::ID);
+ ASSERT_TRUE(message != NULL);
+
+ ViewMsg_Resize::Param params;
+ ViewMsg_Resize::Read(message, &params);
+ EXPECT_EQ(60, params.a.visible_viewport_size.height());
+}
+
+// Ensures that touch event positions are never truncated to integers.
+TEST_F(RenderWidgetHostViewAuraTest, TouchEventPositionsArentRounded) {
+ const float kX = 30.58f;
+ const float kY = 50.23f;
+
+ view_->InitAsChild(NULL);
+ view_->Show();
+
+ ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
+ gfx::PointF(kX, kY),
+ 0,
+ ui::EventTimeForNow());
+
+ view_->OnTouchEvent(&press);
+ EXPECT_EQ(blink::WebInputEvent::TouchStart, view_->touch_event_.type);
+ EXPECT_TRUE(view_->touch_event_.cancelable);
+ EXPECT_EQ(1U, view_->touch_event_.touchesLength);
+ EXPECT_EQ(blink::WebTouchPoint::StatePressed,
+ view_->touch_event_.touches[0].state);
+ EXPECT_EQ(kX, view_->touch_event_.touches[0].screenPosition.x);
+ EXPECT_EQ(kX, view_->touch_event_.touches[0].position.x);
+ EXPECT_EQ(kY, view_->touch_event_.touches[0].screenPosition.y);
+ EXPECT_EQ(kY, view_->touch_event_.touches[0].position.y);
+}
+
+// Tests that scroll ACKs are correctly handled by the overscroll-navigation
+// controller.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollEventOverscrolls) {
+ SetUpOverscrollEnvironment();
+
+ // Simulate wheel events.
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, 1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as not processed.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This will
+ // start a back navigation. However, this will also cause the queued next
+ // event to be sent to the renderer. But since overscroll navigation has
+ // started, that event will also be included in the overscroll computation
+ // instead of being sent to the renderer. So the result will be an overscroll
+ // back navigation, and no event will be sent to the renderer.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(-81.f, overscroll_delta_x());
+ EXPECT_EQ(-31.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send a mouse-move event. This should cancel the overscroll navigation.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that if some scroll events are consumed towards the start, then
+// subsequent scrolls do not horizontal overscroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ WheelScrollConsumedDoNotHorizOverscroll) {
+ SetUpOverscrollEnvironment();
+
+ // Simulate wheel events.
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, -1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as processed.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This should
+ // not initiate overscroll, since the beginning of the scroll has been
+ // consumed. The queued event with different modifiers should be sent to the
+ // renderer.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+
+ // Indicate the end of the scrolling from the touchpad.
+ SimulateGestureFlingStartEvent(-1200.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start another scroll. This time, do not consume any scroll events.
+ SimulateWheelEvent(0, -5, 0, true); // sent directly
+ SimulateWheelEvent(0, -1, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the first wheel and the subsequent coalesced event as not
+ // processed. This should start a back-overscroll.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+}
+
+// Tests that wheel-scrolling correctly turns overscroll on and off.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollOverscrollToggle) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(40, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll in the reverse direction enough to abort the overscroll.
+ SimulateWheelEvent(-20, 0, 0, true);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Continue to scroll in the reverse direction.
+ SimulateWheelEvent(-20, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Continue to scroll in the reverse direction enough to initiate overscroll
+ // in that direction.
+ SimulateWheelEvent(-55, 0, 0, true);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(-75.f, overscroll_delta_x());
+ EXPECT_EQ(-25.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ ScrollEventsOverscrollWithFling) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(20, 0, 0, true);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(30, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send a fling start, but with a small velocity, so that the overscroll is
+ // aborted. The fling should proceed to the renderer, through the gesture
+ // event filter.
+ SimulateGestureFlingStartEvent(0.f, 0.1f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that
+// the zero-velocity fling does not reach the renderer.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ ScrollEventsOverscrollWithZeroFling) {
+ SetUpOverscrollEnvironment();
+
+ // Send a wheel event. ACK the event as not processed. This should not
+ // initiate an overscroll gesture since it doesn't cross the threshold yet.
+ SimulateWheelEvent(10, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Scroll some more so as to not overscroll.
+ SimulateWheelEvent(20, 0, 0, true);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Scroll some more to initiate an overscroll.
+ SimulateWheelEvent(30, 0, 0, true);
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(60.f, overscroll_delta_x());
+ EXPECT_EQ(10.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send a fling start, but with a small velocity, so that the overscroll is
+ // aborted. The fling should proceed to the renderer, through the gesture
+ // event filter.
+ SimulateGestureFlingStartEvent(10.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that a fling in the opposite direction of the overscroll cancels the
+// overscroll nav instead of completing it.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, ReverseFlingCancelsOverscroll) {
+ SetUpOverscrollEnvironment();
+
+ {
+ // Start and end a gesture in the same direction without processing the
+ // gesture events in the renderer. This should initiate and complete an
+ // overscroll navigation.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+ }
+
+ {
+ // Start over, except instead of ending the gesture with ScrollEnd, end it
+ // with a FlingStart, with velocity in the reverse direction. This should
+ // initiate an overscroll navigation, but it should be cancelled because of
+ // the fling in the opposite direction.
+ overscroll_delegate()->Reset();
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(-300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ SimulateGestureFlingStartEvent(100, 0, blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+ }
+}
+
+// Tests that touch-scroll events are handled correctly by the overscroll
+// controller. This also tests that the overscroll controller and the
+// gesture-event filter play nice with each other.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, GestureScrollOverscrolls) {
+ SetUpOverscrollEnvironment();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another gesture event and ACK as not being processed. This should
+ // initiate the navigation gesture.
+ SimulateGestureScrollUpdateEvent(55, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(-5.f, overscroll_delta_y());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another gesture update event. This event should be consumed by the
+ // controller, and not be forwarded to the renderer. The gesture-event filter
+ // should not also receive this event.
+ SimulateGestureScrollUpdateEvent(10, -5, 0);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(-10.f, overscroll_delta_y());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-10.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Now send a scroll end. This should cancel the overscroll gesture, and send
+ // the event to the renderer. The gesture-event filter should receive this
+ // event.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that if the page is scrolled because of a scroll-gesture, then that
+// particular scroll sequence never generates overscroll if the scroll direction
+// is horizontal.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollConsumedHorizontal) {
+ SetUpOverscrollEnvironment();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(10, 0, 0);
+
+ // Start scrolling on content. ACK both events as being processed.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Send another gesture event and ACK as not being processed. This should
+ // not initiate overscroll because the beginning of the scroll event did
+ // scroll some content on the page. Since there was no overscroll, the event
+ // should reach the renderer.
+ SimulateGestureScrollUpdateEvent(55, 0, 0);
+ EXPECT_EQ(1U, sink_->message_count());
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+}
+
+// Tests that the overscroll controller plays nice with touch-scrolls and the
+// gesture event filter with debounce filtering turned on.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollDebounceOverscrolls) {
+ SetUpOverscrollEnvironmentWithDebounce(100);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(25, 0, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Quickly end and restart the scroll gesture. These two events should get
+ // discarded.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send another update event. This should get into the queue.
+ SimulateGestureScrollUpdateEvent(30, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive an ACK for the first scroll-update event as not being processed.
+ // This will contribute to the overscroll gesture, but not enough for the
+ // overscroll controller to start consuming gesture events. This also cause
+ // the queued gesture event to be forwarded to the renderer.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another update event. This should get into the queue.
+ SimulateGestureScrollUpdateEvent(10, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive an ACK for the second scroll-update event as not being processed.
+ // This will now initiate an overscroll. This will also cause the queued
+ // gesture event to be released. But instead of going to the renderer, it will
+ // be consumed by the overscroll controller.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+}
+
+// Tests that the gesture debounce timer plays nice with the overscroll
+// controller.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ GestureScrollDebounceTimerOverscroll) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(55, 0, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send an end event. This should get in the debounce queue.
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Receive ACK for the scroll-update event.
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Let the timer for the debounce queue fire. That should release the queued
+ // scroll-end event. Since overscroll has started, but there hasn't been
+ // enough overscroll to complete the gesture, the overscroll controller
+ // will reset the state. The scroll-end should therefore be dispatched to the
+ // renderer, and the gesture-event-filter should await an ACK for it.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(15));
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+// Tests that when touch-events are dispatched to the renderer, the overscroll
+// gesture deals with them correctly.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollWithTouchEvents) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+ widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ sink_->ClearMessages();
+
+ // The test sends an intermingled sequence of touch and gesture events.
+ PressTouchPoint(0, 1);
+ SendInputEventACK(WebInputEvent::TouchStart,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ MoveTouchPoint(0, 20, 5);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::TouchMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SimulateGestureScrollUpdateEvent(20, 0, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Another touch move event should reach the renderer since overscroll hasn't
+ // started yet. Note that touch events sent during the scroll period may
+ // not require an ack (having been marked uncancelable).
+ MoveTouchPoint(0, 65, 10);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(45, 0, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send another touch event. The page should get the touch-move event, even
+ // though overscroll has started.
+ MoveTouchPoint(0, 55, 5);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(65.f, overscroll_delta_x());
+ EXPECT_EQ(15.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(-10, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+
+ PressTouchPoint(255, 5);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureScrollUpdateEvent(200, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(255.f, overscroll_delta_x());
+ EXPECT_EQ(205.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+
+ // The touch-end/cancel event should always reach the renderer if the page has
+ // touch handlers.
+ ReleaseTouchPoint(1);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ ReleaseTouchPoint(0);
+ AckLastSentInputEventIfNecessary(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(1U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+}
+
+// Tests that touch-gesture end is dispatched to the renderer at the end of a
+// touch-gesture initiated overscroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ TouchGestureEndDispatchedAfterOverscrollComplete) {
+ SetUpOverscrollEnvironmentWithDebounce(10);
+ widget_host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
+ sink_->ClearMessages();
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ // The scroll begin event will have received a synthetic ack from the input
+ // router.
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(55, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(55.f, overscroll_delta_x());
+ EXPECT_EQ(5.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+
+ // Send end event.
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ // Send update events.
+ SimulateGestureScrollUpdateEvent(235, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(235.f, overscroll_delta_x());
+ EXPECT_EQ(185.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(-5.f, overscroll_delegate()->delta_y());
+
+ // Send end event.
+ SimulateGestureEvent(blink::WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(0U, sink_->message_count());
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(10));
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(1U, sink_->message_count());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) {
+ SetUpOverscrollEnvironmentWithDebounce(100);
+
+ // Start scrolling. Receive ACK as it being processed.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Send update events and receive ack as not consumed.
+ SimulateGestureScrollUpdateEvent(125, -5, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send another update event, but in the reverse direction. The overscroll
+ // controller will consume the event, and reset the overscroll mode.
+ SimulateGestureScrollUpdateEvent(-260, 0, 0);
+ EXPECT_EQ(0U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+
+ // Since the overscroll mode has been reset, the next scroll update events
+ // should reach the renderer.
+ SimulateGestureScrollUpdateEvent(-20, 0, 0);
+ EXPECT_EQ(1U, sink_->message_count());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+}
+
+// Tests that if a mouse-move event completes the overscroll gesture, future
+// move events do reach the renderer.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) {
+ SetUpOverscrollEnvironment();
+
+ SimulateWheelEvent(5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-1, 0, 0, true); // enqueued
+ SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event
+ SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK the first wheel event as not processed.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Receive ACK for the second (coalesced) event as not processed. This will
+ // start an overcroll gesture.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ // Send a mouse-move event. This should cancel the overscroll navigation
+ // (since the amount overscrolled is not above the threshold), and so the
+ // mouse-move should reach the renderer.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ // Moving the mouse more should continue to send the events to the renderer.
+ SimulateMouseMove(5, 10, 0);
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Now try with gestures.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ sink_->ClearMessages();
+
+ // Overscroll gesture is in progress. Send a mouse-move now. This should
+ // complete the gesture (because the amount overscrolled is above the
+ // threshold).
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Move mouse some more. The mouse-move events should reach the renderer.
+ SimulateMouseMove(5, 10, 0);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseMove,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+}
+
+// Tests that if a page scrolled, then the overscroll controller's states are
+// reset after the end of the scroll.
+TEST_F(RenderWidgetHostViewAuraOverscrollTest,
+ OverscrollStateResetsAfterScroll) {
+ SetUpOverscrollEnvironment();
+
+ SimulateWheelEvent(0, 5, 0, true); // sent directly
+ SimulateWheelEvent(0, 30, 0, true); // enqueued
+ SimulateWheelEvent(0, 40, 0, true); // coalesced into previous event
+ SimulateWheelEvent(0, 10, 0, true); // coalesced into previous event
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The first wheel event is consumed. Dispatches the queued wheel event.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsContentScrolling());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The second wheel event is consumed.
+ SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsContentScrolling());
+
+ // Touchpad scroll can end with a zero-velocity fling. But it is not
+ // dispatched, but it should still reset the overscroll controller state.
+ SimulateGestureFlingStartEvent(0.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateWheelEvent(-5, 0, 0, true); // sent directly
+ SimulateWheelEvent(-60, 0, 0, true); // enqueued
+ SimulateWheelEvent(-100, 0, 0, true); // coalesced into previous event
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // The first wheel scroll did not scroll content. Overscroll should not start
+ // yet, since enough hasn't been scrolled.
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ SendInputEventACK(WebInputEvent::MouseWheel,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode());
+ EXPECT_TRUE(ScrollStateIsOverscrolling());
+ EXPECT_EQ(0U, sink_->message_count());
+
+ SimulateGestureFlingStartEvent(0.f, 0.f, blink::WebGestureDeviceTouchpad);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->completed_mode());
+ EXPECT_TRUE(ScrollStateIsUnknown());
+ EXPECT_EQ(0U, sink_->message_count());
+}
+
+TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollResetsOnBlur) {
+ SetUpOverscrollEnvironment();
+
+ // Start an overscroll with gesture scroll. In the middle of the scroll, blur
+ // the host.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
+
+ view_->OnWindowFocused(NULL, view_->GetAttachedWindow());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_x());
+ EXPECT_EQ(0.f, overscroll_delegate()->delta_y());
+ sink_->ClearMessages();
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
+
+ // Start a scroll gesture again. This should correctly start the overscroll
+ // after the threshold.
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ blink::WebGestureDeviceTouchscreen);
+ SimulateGestureScrollUpdateEvent(300, -5, 0);
+ SendInputEventACK(WebInputEvent::GestureScrollUpdate,
+ INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
+ blink::WebGestureDeviceTouchscreen);
+ EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode());
+ EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode());
+ EXPECT_EQ(3U, sink_->message_count());
}
} // namespace content