| // Copyright 2018 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/renderer_host/direct_manipulation_helper_win.h" | 
 |  | 
 | #include <objbase.h> | 
 |  | 
 | #include "base/macros.h" | 
 | #include "base/test/scoped_feature_list.h" | 
 | #include "base/win/windows_version.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "ui/base/ui_base_features.h" | 
 | #include "ui/base/win/window_event_target.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | class MockDirectManipulationViewport | 
 |     : public Microsoft::WRL::RuntimeClass< | 
 |           Microsoft::WRL::RuntimeClassFlags< | 
 |               Microsoft::WRL::RuntimeClassType::ClassicCom>, | 
 |           Microsoft::WRL::Implements< | 
 |               Microsoft::WRL::RuntimeClassFlags< | 
 |                   Microsoft::WRL::RuntimeClassType::ClassicCom>, | 
 |               Microsoft::WRL::FtmBase, | 
 |               IDirectManipulationViewport>> { | 
 |  public: | 
 |   MockDirectManipulationViewport() {} | 
 |  | 
 |   ~MockDirectManipulationViewport() override {} | 
 |  | 
 |   bool IsZoomToRectCalled() { | 
 |     bool called = zoom_to_rect_called_; | 
 |     zoom_to_rect_called_ = false; | 
 |     return called; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE Enable() override { return S_OK; } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE Disable() override { return S_OK; } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE SetContact(_In_ UINT32 pointerId) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE ReleaseContact(_In_ UINT32 pointerId) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE ReleaseAllContacts() override { return S_OK; } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   GetStatus(_Out_ DIRECTMANIPULATION_STATUS* status) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE GetTag(_In_ REFIID riid, | 
 |                                    _COM_Outptr_opt_ void** object, | 
 |                                    _Out_opt_ UINT32* id) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE SetTag(_In_opt_ IUnknown* object, | 
 |                                    _In_ UINT32 id) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE GetViewportRect(_Out_ RECT* viewport) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetViewportRect(_In_ const RECT* viewport) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE ZoomToRect(_In_ const float left, | 
 |                                        _In_ const float top, | 
 |                                        _In_ const float right, | 
 |                                        _In_ const float bottom, | 
 |                                        _In_ BOOL animate) override { | 
 |     zoom_to_rect_called_ = true; | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetViewportTransform(_In_reads_(point_count) const float* matrix, | 
 |                        _In_ DWORD point_count) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SyncDisplayTransform(_In_reads_(point_count) const float* matrix, | 
 |                        _In_ DWORD point_count) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   GetPrimaryContent(_In_ REFIID riid, _COM_Outptr_ void** object) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   AddContent(_In_ IDirectManipulationContent* content) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   RemoveContent(_In_ IDirectManipulationContent* content) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE SetViewportOptions( | 
 |       _In_ DIRECTMANIPULATION_VIEWPORT_OPTIONS options) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE AddConfiguration( | 
 |       _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE RemoveConfiguration( | 
 |       _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE ActivateConfiguration( | 
 |       _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE SetManualGesture( | 
 |       _In_ DIRECTMANIPULATION_GESTURE_CONFIGURATION configuration) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetChaining(_In_ DIRECTMANIPULATION_MOTION_TYPES enabledTypes) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   AddEventHandler(_In_opt_ HWND window, | 
 |                   _In_ IDirectManipulationViewportEventHandler* eventHandler, | 
 |                   _Out_ DWORD* cookie) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE RemoveEventHandler(_In_ DWORD cookie) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetInputMode(_In_ DIRECTMANIPULATION_INPUT_MODE mode) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetUpdateMode(_In_ DIRECTMANIPULATION_INPUT_MODE mode) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE Stop() override { return S_OK; } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE Abandon() override { return S_OK; } | 
 |  | 
 |  private: | 
 |   bool zoom_to_rect_called_ = false; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationViewport); | 
 | }; | 
 |  | 
 | class MockDirectManipulationContent | 
 |     : public Microsoft::WRL::RuntimeClass< | 
 |           Microsoft::WRL::RuntimeClassFlags< | 
 |               Microsoft::WRL::RuntimeClassType::ClassicCom>, | 
 |           Microsoft::WRL::Implements< | 
 |               Microsoft::WRL::RuntimeClassFlags< | 
 |                   Microsoft::WRL::RuntimeClassType::ClassicCom>, | 
 |               Microsoft::WRL::FtmBase, | 
 |               IDirectManipulationContent>> { | 
 |  public: | 
 |   MockDirectManipulationContent() {} | 
 |  | 
 |   ~MockDirectManipulationContent() override {} | 
 |  | 
 |   void SetContentTransform(float scale, float scroll_x, float scroll_y) { | 
 |     for (int i = 0; i < 6; ++i) | 
 |       transforms_[i] = 0; | 
 |     transforms_[0] = scale; | 
 |     transforms_[4] = scroll_x; | 
 |     transforms_[5] = scroll_y; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   GetContentTransform(_Out_writes_(point_count) float* transforms, | 
 |                       _In_ DWORD point_count) override { | 
 |     for (int i = 0; i < 6; ++i) | 
 |       transforms[i] = transforms_[i]; | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   // Other Overrides | 
 |   HRESULT STDMETHODCALLTYPE GetContentRect(_Out_ RECT* contentSize) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SetContentRect(_In_ const RECT* contentSize) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE GetViewport(_In_ REFIID riid, | 
 |                                         _COM_Outptr_ void** object) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE GetTag(_In_ REFIID riid, | 
 |                                    _COM_Outptr_opt_ void** object, | 
 |                                    _Out_opt_ UINT32* id) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE SetTag(_In_opt_ IUnknown* object, | 
 |                                    _In_ UINT32 id) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   GetOutputTransform(_Out_writes_(point_count) float* matrix, | 
 |                      _In_ DWORD point_count) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   HRESULT STDMETHODCALLTYPE | 
 |   SyncContentTransform(_In_reads_(point_count) const float* matrix, | 
 |                        _In_ DWORD point_count) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |  private: | 
 |   float transforms_[6]; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationContent); | 
 | }; | 
 |  | 
 | enum class EventGesture { | 
 |   kScrollBegin, | 
 |   kScroll, | 
 |   kScrollEnd, | 
 |   kFlingBegin, | 
 |   kFling, | 
 |   kFlingEnd, | 
 |   kScaleBegin, | 
 |   kScale, | 
 |   kScaleEnd, | 
 | }; | 
 |  | 
 | struct Event { | 
 |   explicit Event(float scale) : gesture_(EventGesture::kScale), scale_(scale) {} | 
 |  | 
 |   Event(EventGesture gesture, float scroll_x, float scroll_y) | 
 |       : gesture_(gesture), scroll_x_(scroll_x), scroll_y_(scroll_y) {} | 
 |  | 
 |   explicit Event(EventGesture gesture) : gesture_(gesture) {} | 
 |  | 
 |   EventGesture gesture_; | 
 |   float scale_ = 0; | 
 |   float scroll_x_ = 0; | 
 |   float scroll_y_ = 0; | 
 | }; | 
 |  | 
 | class MockWindowEventTarget : public ui::WindowEventTarget { | 
 |  public: | 
 |   MockWindowEventTarget() {} | 
 |  | 
 |   ~MockWindowEventTarget() override {} | 
 |  | 
 |   void ApplyPinchZoomScale(float scale) override { | 
 |     events_.push_back(Event(scale)); | 
 |   } | 
 |  | 
 |   void ApplyPinchZoomBegin() override { | 
 |     events_.push_back(Event(EventGesture::kScaleBegin)); | 
 |   } | 
 |  | 
 |   void ApplyPinchZoomEnd() override { | 
 |     events_.push_back(Event(EventGesture::kScaleEnd)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureScroll(int scroll_x, int scroll_y) override { | 
 |     events_.push_back(Event(EventGesture::kScroll, scroll_x, scroll_y)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureFling(int scroll_x, int scroll_y) override { | 
 |     events_.push_back(Event(EventGesture::kFling, scroll_x, scroll_y)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) override { | 
 |     events_.push_back(Event(EventGesture::kScrollBegin, scroll_x, scroll_y)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureFlingBegin() override { | 
 |     events_.push_back(Event(EventGesture::kFlingBegin)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureFlingEnd() override { | 
 |     events_.push_back(Event(EventGesture::kFlingEnd)); | 
 |   } | 
 |  | 
 |   void ApplyPanGestureScrollEnd() override { | 
 |     events_.push_back(Event(EventGesture::kScrollEnd)); | 
 |   } | 
 |  | 
 |   std::vector<Event> GetEvents() { | 
 |     std::vector<Event> result = events_; | 
 |     events_.clear(); | 
 |     return result; | 
 |   } | 
 |  | 
 |   // Other Overrides | 
 |   LRESULT HandleMouseMessage(unsigned int message, | 
 |                              WPARAM w_param, | 
 |                              LPARAM l_param, | 
 |                              bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   LRESULT HandlePointerMessage(unsigned int message, | 
 |                                WPARAM w_param, | 
 |                                LPARAM l_param, | 
 |                                bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   LRESULT HandleKeyboardMessage(unsigned int message, | 
 |                                 WPARAM w_param, | 
 |                                 LPARAM l_param, | 
 |                                 bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   LRESULT HandleTouchMessage(unsigned int message, | 
 |                              WPARAM w_param, | 
 |                              LPARAM l_param, | 
 |                              bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   LRESULT HandleScrollMessage(unsigned int message, | 
 |                               WPARAM w_param, | 
 |                               LPARAM l_param, | 
 |                               bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   LRESULT HandleNcHitTestMessage(unsigned int message, | 
 |                                  WPARAM w_param, | 
 |                                  LPARAM l_param, | 
 |                                  bool* handled) override { | 
 |     return S_OK; | 
 |   } | 
 |  | 
 |   void HandleParentChanged() override {} | 
 |  | 
 |  private: | 
 |   std::vector<Event> events_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MockWindowEventTarget); | 
 | }; | 
 |  | 
 | }  //  namespace | 
 |  | 
 | class DirectManipulationUnitTest : public testing::Test { | 
 |  public: | 
 |   DirectManipulationUnitTest() { | 
 |     scoped_feature_list_.InitAndEnableFeature(features::kPrecisionTouchpad); | 
 |  | 
 |     viewport_ = Microsoft::WRL::Make<MockDirectManipulationViewport>(); | 
 |     content_ = Microsoft::WRL::Make<MockDirectManipulationContent>(); | 
 |     direct_manipulation_helper_ = | 
 |         DirectManipulationHelper::CreateInstanceForTesting(&event_target_, | 
 |                                                            viewport_); | 
 |   } | 
 |  | 
 |   ~DirectManipulationUnitTest() override {} | 
 |  | 
 |   DirectManipulationHelper* GetDirectManipulationHelper() { | 
 |     return direct_manipulation_helper_.get(); | 
 |   } | 
 |  | 
 |   std::vector<Event> GetEvents() { return event_target_.GetEvents(); } | 
 |  | 
 |   void ViewportStatusChanged(DIRECTMANIPULATION_STATUS current, | 
 |                              DIRECTMANIPULATION_STATUS previous) { | 
 |     direct_manipulation_helper_->event_handler_->OnViewportStatusChanged( | 
 |         viewport_.Get(), current, previous); | 
 |   } | 
 |  | 
 |   void ContentUpdated(float scale, float scroll_x, float scroll_y) { | 
 |     content_->SetContentTransform(scale, scroll_x, scroll_y); | 
 |     direct_manipulation_helper_->event_handler_->OnContentUpdated( | 
 |         viewport_.Get(), content_.Get()); | 
 |   } | 
 |  | 
 |   bool IsZoomToRectCalled() { return viewport_->IsZoomToRectCalled(); } | 
 |  | 
 |   void SetDeviceScaleFactor(float factor) { | 
 |     direct_manipulation_helper_->SetDeviceScaleFactorForTesting(factor); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::unique_ptr<DirectManipulationHelper> direct_manipulation_helper_; | 
 |   Microsoft::WRL::ComPtr<MockDirectManipulationViewport> viewport_; | 
 |   Microsoft::WRL::ComPtr<MockDirectManipulationContent> content_; | 
 |   MockWindowEventTarget event_target_; | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(DirectManipulationUnitTest); | 
 | }; | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, HelperShouldCreateForWin10) { | 
 |   // We should create DirectManipulationHelper instance when win version >= 10. | 
 |   EXPECT_EQ(GetDirectManipulationHelper() != nullptr, | 
 |             base::win::GetVersion() >= base::win::VERSION_WIN10); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, ReceiveSimplePanTransform) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); | 
 |   ContentUpdated(1, 10, 0); | 
 |  | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |   EXPECT_EQ(10, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   // For next update, should only apply the difference. | 
 |   ContentUpdated(1, 15, 0); | 
 |  | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); | 
 |   EXPECT_EQ(5, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); | 
 |  | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollEnd, events[0].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, ReceivePanFling) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); | 
 |   ContentUpdated(1, 10, 0); | 
 |  | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |   EXPECT_EQ(10, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   // For next update, should only apply the difference. | 
 |   ContentUpdated(1, 15, 0); | 
 |  | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); | 
 |   EXPECT_EQ(5, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   // Fling Begin. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); | 
 |  | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); | 
 |  | 
 |   ContentUpdated(1, 20, 0); | 
 |  | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFling, events[0].gesture_); | 
 |   EXPECT_EQ(5, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_INERTIA); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, ReceiveSimpleScaleTransform) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); | 
 |   ContentUpdated(1.1f, 0, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(2u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScale, events[1].gesture_); | 
 |   EXPECT_EQ(1.1f, events[1].scale_); | 
 |  | 
 |   // For next update, should only apply the difference. | 
 |   ContentUpdated(1.21f, 0, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScale, events[0].gesture_); | 
 |   EXPECT_EQ(1.1f, events[0].scale_); | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScaleEnd, events[0].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, ReceiveScrollTransformLessThanOne) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // Scroll offset less than 1, should not apply. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); | 
 |   ContentUpdated(1, 0.1f, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(0u, events.size()); | 
 |  | 
 |   // Scroll offset less than 1, should not apply. | 
 |   ContentUpdated(1, 0.2f, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(0u, events.size()); | 
 |  | 
 |   // Scroll offset more than 1, should only apply integer part. | 
 |   ContentUpdated(1, 1.2f, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |   EXPECT_EQ(1, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 |  | 
 |   // Scroll offset difference less than 1, should not apply. | 
 |   ContentUpdated(1, 1.5f, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(0u, events.size()); | 
 |  | 
 |   // Scroll offset difference more than 1, should only apply integer part. | 
 |   ContentUpdated(1, 3.0f, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); | 
 |   EXPECT_EQ(2, events[0].scroll_x_); | 
 |   EXPECT_EQ(0, events[0].scroll_y_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, | 
 |        ReceiveScaleTransformLessThanFloatPointError) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // Scale factor less than float point error, ignore. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); | 
 |   ContentUpdated(1.000001f, 0, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(0u, events.size()); | 
 |  | 
 |   // Scale factor more than float point error, apply. | 
 |   ContentUpdated(1.00001f, 0, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(2u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScale, events[1].gesture_); | 
 |   EXPECT_EQ(1.00001f, events[1].scale_); | 
 |  | 
 |   // Scale factor difference less than float point error, ignore. | 
 |   ContentUpdated(1.000011f, 0, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(0u, events.size()); | 
 |  | 
 |   // Scale factor difference more than float point error, apply. | 
 |   ContentUpdated(1.000021f, 0, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScale, events[0].gesture_); | 
 |   EXPECT_EQ(1.000021f / 1.00001f, events[0].scale_); | 
 |  | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScaleEnd, events[0].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveBothScrollAndScale) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // Direct Manipulation maybe give incorrect predictions. In this case, we will | 
 |   // receive scroll first then scale after. | 
 |  | 
 |   // First event is a scroll event. | 
 |   ContentUpdated(1.0f, 5, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |  | 
 |   // Second event comes with scale factor. Now the scroll offset only noise. | 
 |   ContentUpdated(1.00001f, 5, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(3u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollEnd, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScaleBegin, events[1].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScale, events[2].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveScaleAfterFling) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // Direct Manipulation maybe give pinch event after fling. In this case, we | 
 |   // should end the current sequence first. | 
 |  | 
 |   // First event is a scroll event. | 
 |   ContentUpdated(1.0f, 5, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |  | 
 |   // Fling Begin. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); | 
 |  | 
 |   ContentUpdated(1, 10, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFling, events[0].gesture_); | 
 |  | 
 |   // Event comes with scale factor. Now the scroll offset only noise. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_INERTIA); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); | 
 |  | 
 |   ContentUpdated(1.00001f, 10, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(2u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScale, events[1].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveScrollAfterFling) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // Direct Manipulation maybe give scroll event after fling. In this case, we | 
 |   // should end the current sequence first. | 
 |  | 
 |   // First event is a scroll event. | 
 |   ContentUpdated(1.0f, 5, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |  | 
 |   // Fling Begin. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); | 
 |  | 
 |   ContentUpdated(1, 10, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFling, events[0].gesture_); | 
 |  | 
 |   // Fling back to Scroll. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_INERTIA); | 
 |   ContentUpdated(1, 15, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(2u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[1].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, | 
 |        ReceiveScaleAfterFlingWithoutViewportStatusChanged) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   // We never see this when testing, but still what to test it. | 
 |  | 
 |   ContentUpdated(1.0f, 5, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |  | 
 |   // Fling Begin. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); | 
 |  | 
 |   ContentUpdated(1, 10, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFling, events[0].gesture_); | 
 |  | 
 |   // Event comes with scale factor. But no ViewportStatusChanged. | 
 |   ContentUpdated(1.00001f, 10, 0); | 
 |   events = GetEvents(); | 
 |   EXPECT_EQ(3u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScaleBegin, events[1].gesture_); | 
 |   EXPECT_EQ(EventGesture::kScale, events[2].gesture_); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, | 
 |        ZoomToRectShouldNotBeCalledInEmptyRunningReadySequence) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   ContentUpdated(1.0f, 5, 0); | 
 |  | 
 |   // Receive first ready when gesture end. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); | 
 |   EXPECT_TRUE(IsZoomToRectCalled()); | 
 |  | 
 |   // Receive second ready from ZoomToRect. | 
 |   ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); | 
 |   EXPECT_FALSE(IsZoomToRectCalled()); | 
 | } | 
 |  | 
 | TEST_F(DirectManipulationUnitTest, HiDPIScroll) { | 
 |   if (!GetDirectManipulationHelper()) | 
 |     return; | 
 |  | 
 |   SetDeviceScaleFactor(10.0); | 
 |   ContentUpdated(1.0f, 50, 0); | 
 |   std::vector<Event> events = GetEvents(); | 
 |   EXPECT_EQ(1u, events.size()); | 
 |   EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); | 
 |   EXPECT_EQ(5, events[0].scroll_x_); | 
 | } | 
 |  | 
 | }  //  namespace content |