1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1202 @@ 1.4 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.5 +/* Any copyright is dedicated to the Public Domain. 1.6 + * http://creativecommons.org/publicdomain/zero/1.0/ 1.7 + */ 1.8 + 1.9 +#include "gtest/gtest.h" 1.10 +#include "gmock/gmock.h" 1.11 + 1.12 +#include "mozilla/Attributes.h" 1.13 +#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform 1.14 +#include "mozilla/layers/AsyncPanZoomController.h" 1.15 +#include "mozilla/layers/LayerManagerComposite.h" 1.16 +#include "mozilla/layers/GeckoContentController.h" 1.17 +#include "mozilla/layers/CompositorParent.h" 1.18 +#include "mozilla/layers/APZCTreeManager.h" 1.19 +#include "base/task.h" 1.20 +#include "Layers.h" 1.21 +#include "TestLayers.h" 1.22 +#include "gfxPrefs.h" 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::gfx; 1.26 +using namespace mozilla::layers; 1.27 +using ::testing::_; 1.28 +using ::testing::NiceMock; 1.29 +using ::testing::AtLeast; 1.30 +using ::testing::AtMost; 1.31 +using ::testing::MockFunction; 1.32 +using ::testing::InSequence; 1.33 + 1.34 +class Task; 1.35 + 1.36 +class AsyncPanZoomControllerTester : public ::testing::Test { 1.37 +protected: 1.38 + virtual void SetUp() { 1.39 + gfxPrefs::GetSingleton(); 1.40 + } 1.41 + virtual void TearDown() { 1.42 + gfxPrefs::DestroySingleton(); 1.43 + } 1.44 +}; 1.45 + 1.46 +class APZCTreeManagerTester : public ::testing::Test { 1.47 +protected: 1.48 + virtual void SetUp() { 1.49 + gfxPrefs::GetSingleton(); 1.50 + } 1.51 + virtual void TearDown() { 1.52 + gfxPrefs::DestroySingleton(); 1.53 + } 1.54 +}; 1.55 + 1.56 +class MockContentController : public GeckoContentController { 1.57 +public: 1.58 + MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&)); 1.59 + MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration)); 1.60 + MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); 1.61 + MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); 1.62 + MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); 1.63 + MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&)); 1.64 + MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize)); 1.65 + MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs)); 1.66 +}; 1.67 + 1.68 +class MockContentControllerDelayed : public MockContentController { 1.69 +public: 1.70 + MockContentControllerDelayed() 1.71 + { 1.72 + } 1.73 + 1.74 + void PostDelayedTask(Task* aTask, int aDelayMs) { 1.75 + mTaskQueue.AppendElement(aTask); 1.76 + } 1.77 + 1.78 + void CheckHasDelayedTask() { 1.79 + EXPECT_TRUE(mTaskQueue.Length() > 0); 1.80 + } 1.81 + 1.82 + void ClearDelayedTask() { 1.83 + mTaskQueue.RemoveElementAt(0); 1.84 + } 1.85 + 1.86 + void DestroyOldestTask() { 1.87 + delete mTaskQueue[0]; 1.88 + mTaskQueue.RemoveElementAt(0); 1.89 + } 1.90 + 1.91 + // Note that deleting mCurrentTask is important in order to 1.92 + // release the reference to the callee object. Without this 1.93 + // that object might be leaked. This is also why we don't 1.94 + // expose mTaskQueue to any users of MockContentControllerDelayed. 1.95 + void RunDelayedTask() { 1.96 + mTaskQueue[0]->Run(); 1.97 + delete mTaskQueue[0]; 1.98 + mTaskQueue.RemoveElementAt(0); 1.99 + } 1.100 + 1.101 +private: 1.102 + nsTArray<Task*> mTaskQueue; 1.103 +}; 1.104 + 1.105 + 1.106 +class TestAPZCContainerLayer : public ContainerLayer { 1.107 + public: 1.108 + TestAPZCContainerLayer() 1.109 + : ContainerLayer(nullptr, nullptr) 1.110 + {} 1.111 + bool RemoveChild(Layer* aChild) { return true; } 1.112 + bool InsertAfter(Layer* aChild, Layer* aAfter) { return true; } 1.113 + void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) {} 1.114 + bool RepositionChild(Layer* aChild, Layer* aAfter) { return true; } 1.115 +}; 1.116 + 1.117 +class TestAsyncPanZoomController : public AsyncPanZoomController { 1.118 +public: 1.119 + TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc, 1.120 + APZCTreeManager* aTreeManager = nullptr, 1.121 + GestureBehavior aBehavior = DEFAULT_GESTURES) 1.122 + : AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior) 1.123 + {} 1.124 + 1.125 + // Since touch-action-enabled property is global - setting it for each test 1.126 + // separately isn't safe from the concurrency point of view. To make tests 1.127 + // run concurrent and independent from each other we have a member variable 1.128 + // mTouchActionEnabled for each apzc and setter defined here. 1.129 + void SetTouchActionEnabled(const bool touchActionEnabled) { 1.130 + ReentrantMonitorAutoEnter lock(mMonitor); 1.131 + mTouchActionPropertyEnabled = touchActionEnabled; 1.132 + } 1.133 + 1.134 + void SetFrameMetrics(const FrameMetrics& metrics) { 1.135 + ReentrantMonitorAutoEnter lock(mMonitor); 1.136 + mFrameMetrics = metrics; 1.137 + } 1.138 + 1.139 + FrameMetrics GetFrameMetrics() { 1.140 + ReentrantMonitorAutoEnter lock(mMonitor); 1.141 + return mFrameMetrics; 1.142 + } 1.143 +}; 1.144 + 1.145 +class TestAPZCTreeManager : public APZCTreeManager { 1.146 +protected: 1.147 + void AssertOnCompositorThread() MOZ_OVERRIDE { /* no-op */ } 1.148 + 1.149 +public: 1.150 + // Expose this so test code can call it directly. 1.151 + void BuildOverscrollHandoffChain(AsyncPanZoomController* aApzc) { 1.152 + APZCTreeManager::BuildOverscrollHandoffChain(aApzc); 1.153 + } 1.154 +}; 1.155 + 1.156 +static 1.157 +FrameMetrics TestFrameMetrics() { 1.158 + FrameMetrics fm; 1.159 + 1.160 + fm.mDisplayPort = CSSRect(0, 0, 10, 10); 1.161 + fm.mCompositionBounds = ParentLayerIntRect(0, 0, 10, 10); 1.162 + fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10); 1.163 + fm.mScrollableRect = CSSRect(0, 0, 100, 100); 1.164 + fm.mViewport = CSSRect(0, 0, 10, 10); 1.165 + 1.166 + return fm; 1.167 +} 1.168 + 1.169 +/* 1.170 + * Dispatches mock touch events to the apzc and checks whether apzc properly 1.171 + * consumed them and triggered scrolling behavior. 1.172 + */ 1.173 +static 1.174 +void ApzcPan(AsyncPanZoomController* apzc, 1.175 + TestAPZCTreeManager* aTreeManager, 1.176 + int& aTime, 1.177 + int aTouchStartY, 1.178 + int aTouchEndY, 1.179 + bool expectIgnoredPan = false, 1.180 + bool hasTouchListeners = false, 1.181 + nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) { 1.182 + 1.183 + const int TIME_BETWEEN_TOUCH_EVENT = 100; 1.184 + const int OVERCOME_TOUCH_TOLERANCE = 100; 1.185 + MultiTouchInput mti; 1.186 + nsEventStatus status; 1.187 + 1.188 + // Since we're passing inputs directly to the APZC instead of going through 1.189 + // the tree manager, we need to build the overscroll handoff chain explicitly 1.190 + // for panning to work correctly. 1.191 + aTreeManager->BuildOverscrollHandoffChain(apzc); 1.192 + 1.193 + nsEventStatus touchStartStatus; 1.194 + if (hasTouchListeners) { 1.195 + // APZC shouldn't consume the start event now, instead queueing it up 1.196 + // waiting for content's response. 1.197 + touchStartStatus = nsEventStatus_eIgnore; 1.198 + } else { 1.199 + // APZC should go into the touching state and therefore consume the event. 1.200 + touchStartStatus = nsEventStatus_eConsumeNoDefault; 1.201 + } 1.202 + 1.203 + mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0); 1.204 + aTime += TIME_BETWEEN_TOUCH_EVENT; 1.205 + // Make sure the move is large enough to not be handled as a tap 1.206 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY+OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0)); 1.207 + status = apzc->ReceiveInputEvent(mti); 1.208 + EXPECT_EQ(touchStartStatus, status); 1.209 + // APZC should be in TOUCHING state 1.210 + 1.211 + // Allowed touch behaviours must be set after sending touch-start. 1.212 + if (aAllowedTouchBehaviors) { 1.213 + apzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors); 1.214 + } 1.215 + 1.216 + nsEventStatus touchMoveStatus; 1.217 + if (expectIgnoredPan) { 1.218 + // APZC should ignore panning, be in TOUCHING state and therefore return eIgnore. 1.219 + // The same applies to all consequent touch move events. 1.220 + touchMoveStatus = nsEventStatus_eIgnore; 1.221 + } else { 1.222 + // APZC should go into the panning state and therefore consume the event. 1.223 + touchMoveStatus = nsEventStatus_eConsumeNoDefault; 1.224 + } 1.225 + 1.226 + mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0); 1.227 + aTime += TIME_BETWEEN_TOUCH_EVENT; 1.228 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0)); 1.229 + status = apzc->ReceiveInputEvent(mti); 1.230 + EXPECT_EQ(touchMoveStatus, status); 1.231 + 1.232 + mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0); 1.233 + aTime += TIME_BETWEEN_TOUCH_EVENT; 1.234 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0)); 1.235 + status = apzc->ReceiveInputEvent(mti); 1.236 + EXPECT_EQ(touchMoveStatus, status); 1.237 + 1.238 + mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0); 1.239 + aTime += TIME_BETWEEN_TOUCH_EVENT; 1.240 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0)); 1.241 + status = apzc->ReceiveInputEvent(mti); 1.242 +} 1.243 + 1.244 +static 1.245 +void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t aBehavior) 1.246 +{ 1.247 + TimeStamp testStartTime = TimeStamp::Now(); 1.248 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.249 + 1.250 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.251 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.252 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm); 1.253 + 1.254 + apzc->SetTouchActionEnabled(aShouldUseTouchAction); 1.255 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.256 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.257 + 1.258 + if (aShouldTriggerScroll) { 1.259 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.260 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.261 + } else { 1.262 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0); 1.263 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); 1.264 + } 1.265 + 1.266 + int time = 0; 1.267 + int touchStart = 50; 1.268 + int touchEnd = 10; 1.269 + ScreenPoint pointOut; 1.270 + ViewTransform viewTransformOut; 1.271 + 1.272 + nsTArray<uint32_t> allowedTouchBehaviors; 1.273 + allowedTouchBehaviors.AppendElement(aBehavior); 1.274 + 1.275 + // Pan down 1.276 + ApzcPan(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors); 1.277 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.278 + 1.279 + if (aShouldTriggerScroll) { 1.280 + EXPECT_EQ(ScreenPoint(0, -(touchEnd-touchStart)), pointOut); 1.281 + EXPECT_NE(ViewTransform(), viewTransformOut); 1.282 + } else { 1.283 + EXPECT_EQ(ScreenPoint(), pointOut); 1.284 + EXPECT_EQ(ViewTransform(), viewTransformOut); 1.285 + } 1.286 + 1.287 + // Pan back 1.288 + ApzcPan(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors); 1.289 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.290 + 1.291 + EXPECT_EQ(ScreenPoint(), pointOut); 1.292 + EXPECT_EQ(ViewTransform(), viewTransformOut); 1.293 + 1.294 + apzc->Destroy(); 1.295 +} 1.296 + 1.297 +static void 1.298 +ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale) { 1.299 + aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START, 1.300 + 0, 1.301 + ScreenPoint(aFocusX, aFocusY), 1.302 + 10.0, 1.303 + 10.0, 1.304 + 0)); 1.305 + aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, 1.306 + 0, 1.307 + ScreenPoint(aFocusX, aFocusY), 1.308 + 10.0 * aScale, 1.309 + 10.0, 1.310 + 0)); 1.311 + aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END, 1.312 + 0, 1.313 + ScreenPoint(aFocusX, aFocusY), 1.314 + // note: negative values here tell APZC 1.315 + // not to turn the pinch into a pan 1.316 + -1.0, 1.317 + -1.0, 1.318 + 0)); 1.319 +} 1.320 + 1.321 +static nsEventStatus 1.322 +ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) { 1.323 + MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0); 1.324 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0)); 1.325 + return apzc->ReceiveInputEvent(mti); 1.326 +} 1.327 + 1.328 +static nsEventStatus 1.329 +ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) { 1.330 + MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0); 1.331 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0)); 1.332 + return apzc->ReceiveInputEvent(mti); 1.333 +} 1.334 + 1.335 +static nsEventStatus 1.336 +ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength, MockContentControllerDelayed* mcc = nullptr) { 1.337 + nsEventStatus status = ApzcDown(apzc, aX, aY, aTime); 1.338 + if (mcc != nullptr) { 1.339 + // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but 1.340 + // if we were provided a non-null mcc we want to clear them. 1.341 + mcc->CheckHasDelayedTask(); 1.342 + mcc->ClearDelayedTask(); 1.343 + mcc->CheckHasDelayedTask(); 1.344 + mcc->ClearDelayedTask(); 1.345 + } 1.346 + EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status); 1.347 + aTime += aTapLength; 1.348 + return ApzcUp(apzc, aX, aY, aTime); 1.349 +} 1.350 + 1.351 +TEST_F(AsyncPanZoomControllerTester, Constructor) { 1.352 + // RefCounted class can't live in the stack 1.353 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.354 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.355 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.356 +} 1.357 + 1.358 +TEST_F(AsyncPanZoomControllerTester, Pinch) { 1.359 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.360 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.361 + 1.362 + FrameMetrics fm; 1.363 + fm.mViewport = CSSRect(0, 0, 980, 480); 1.364 + fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200); 1.365 + fm.mScrollableRect = CSSRect(0, 0, 980, 1000); 1.366 + fm.SetScrollOffset(CSSPoint(300, 300)); 1.367 + fm.SetZoom(CSSToScreenScale(2.0)); 1.368 + apzc->SetFrameMetrics(fm); 1.369 + apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0))); 1.370 + // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 1.371 + 1.372 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.373 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.374 + 1.375 + ApzcPinch(apzc, 250, 300, 1.25); 1.376 + 1.377 + // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80 1.378 + fm = apzc->GetFrameMetrics(); 1.379 + EXPECT_EQ(2.5f, fm.GetZoom().scale); 1.380 + EXPECT_EQ(305, fm.GetScrollOffset().x); 1.381 + EXPECT_EQ(310, fm.GetScrollOffset().y); 1.382 + 1.383 + // part 2 of the test, move to the top-right corner of the page and pinch and 1.384 + // make sure we stay in the correct spot 1.385 + fm.SetZoom(CSSToScreenScale(2.0)); 1.386 + fm.SetScrollOffset(CSSPoint(930, 5)); 1.387 + apzc->SetFrameMetrics(fm); 1.388 + // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100 1.389 + 1.390 + ApzcPinch(apzc, 250, 300, 0.5); 1.391 + 1.392 + // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200 1.393 + fm = apzc->GetFrameMetrics(); 1.394 + EXPECT_EQ(1.0f, fm.GetZoom().scale); 1.395 + EXPECT_EQ(880, fm.GetScrollOffset().x); 1.396 + EXPECT_EQ(0, fm.GetScrollOffset().y); 1.397 + 1.398 + apzc->Destroy(); 1.399 +} 1.400 + 1.401 +TEST_F(AsyncPanZoomControllerTester, PinchWithTouchActionNone) { 1.402 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.403 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.404 + 1.405 + FrameMetrics fm; 1.406 + fm.mViewport = CSSRect(0, 0, 980, 480); 1.407 + fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200); 1.408 + fm.mScrollableRect = CSSRect(0, 0, 980, 1000); 1.409 + fm.SetScrollOffset(CSSPoint(300, 300)); 1.410 + fm.SetZoom(CSSToScreenScale(2.0)); 1.411 + apzc->SetFrameMetrics(fm); 1.412 + // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 1.413 + 1.414 + // Apzc's OnScaleEnd method calls once SendAsyncScrollDOMEvent and RequestContentRepaint methods, 1.415 + // therefore we're setting these specific values. 1.416 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(1)); 1.417 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtMost(1)); 1.418 + 1.419 + nsTArray<uint32_t> values; 1.420 + values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 1.421 + values.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); 1.422 + apzc->SetTouchActionEnabled(true); 1.423 + 1.424 + apzc->SetAllowedTouchBehavior(values); 1.425 + ApzcPinch(apzc, 250, 300, 1.25); 1.426 + 1.427 + // The frame metrics should stay the same since touch-action:none makes 1.428 + // apzc ignore pinch gestures. 1.429 + fm = apzc->GetFrameMetrics(); 1.430 + EXPECT_EQ(2.0f, fm.GetZoom().scale); 1.431 + EXPECT_EQ(300, fm.GetScrollOffset().x); 1.432 + EXPECT_EQ(300, fm.GetScrollOffset().y); 1.433 +} 1.434 + 1.435 +TEST_F(AsyncPanZoomControllerTester, Overzoom) { 1.436 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.437 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.438 + 1.439 + FrameMetrics fm; 1.440 + fm.mViewport = CSSRect(0, 0, 100, 100); 1.441 + fm.mCompositionBounds = ParentLayerIntRect(0, 0, 100, 100); 1.442 + fm.mScrollableRect = CSSRect(0, 0, 125, 150); 1.443 + fm.SetScrollOffset(CSSPoint(10, 0)); 1.444 + fm.SetZoom(CSSToScreenScale(1.0)); 1.445 + apzc->SetFrameMetrics(fm); 1.446 + apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0))); 1.447 + // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100 1.448 + 1.449 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.450 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.451 + 1.452 + ApzcPinch(apzc, 50, 50, 0.5); 1.453 + 1.454 + fm = apzc->GetFrameMetrics(); 1.455 + EXPECT_EQ(0.8f, fm.GetZoom().scale); 1.456 + // bug 936721 - PGO builds introduce rounding error so 1.457 + // use a fuzzy match instead 1.458 + EXPECT_LT(abs(fm.GetScrollOffset().x), 1e-5); 1.459 + EXPECT_LT(abs(fm.GetScrollOffset().y), 1e-5); 1.460 +} 1.461 + 1.462 +TEST_F(AsyncPanZoomControllerTester, SimpleTransform) { 1.463 + TimeStamp testStartTime = TimeStamp::Now(); 1.464 + // RefCounted class can't live in the stack 1.465 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.466 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.467 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.468 + 1.469 + ScreenPoint pointOut; 1.470 + ViewTransform viewTransformOut; 1.471 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.472 + 1.473 + EXPECT_EQ(ScreenPoint(), pointOut); 1.474 + EXPECT_EQ(ViewTransform(), viewTransformOut); 1.475 +} 1.476 + 1.477 + 1.478 +TEST_F(AsyncPanZoomControllerTester, ComplexTransform) { 1.479 + TimeStamp testStartTime = TimeStamp::Now(); 1.480 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.481 + 1.482 + // This test assumes there is a page that gets rendered to 1.483 + // two layers. In CSS pixels, the first layer is 50x50 and 1.484 + // the second layer is 25x50. The widget scale factor is 3.0 1.485 + // and the presShell resolution is 2.0. Therefore, these layers 1.486 + // end up being 300x300 and 150x300 in layer pixels. 1.487 + // 1.488 + // The second (child) layer has an additional CSS transform that 1.489 + // stretches it by 2.0 on the x-axis. Therefore, after applying 1.490 + // CSS transforms, the two layers are the same size in screen 1.491 + // pixels. 1.492 + // 1.493 + // The screen itself is 24x24 in screen pixels (therefore 4x4 in 1.494 + // CSS pixels). The displayport is 1 extra CSS pixel on all 1.495 + // sides. 1.496 + 1.497 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.498 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc); 1.499 + nsRefPtr<TestAsyncPanZoomController> childApzc = new TestAsyncPanZoomController(0, mcc); 1.500 + 1.501 + const char* layerTreeSyntax = "c(c)"; 1.502 + // LayerID 0 1 1.503 + nsIntRegion layerVisibleRegion[] = { 1.504 + nsIntRegion(nsIntRect(0, 0, 300, 300)), 1.505 + nsIntRegion(nsIntRect(0, 0, 150, 300)), 1.506 + }; 1.507 + gfx3DMatrix transforms[] = { 1.508 + gfx3DMatrix(), 1.509 + gfx3DMatrix(), 1.510 + }; 1.511 + transforms[0].ScalePost(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer 1.512 + transforms[1].ScalePost(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer 1.513 + 1.514 + nsTArray<nsRefPtr<Layer> > layers; 1.515 + nsRefPtr<LayerManager> lm; 1.516 + nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers); 1.517 + 1.518 + FrameMetrics metrics; 1.519 + metrics.mCompositionBounds = ParentLayerIntRect(0, 0, 24, 24); 1.520 + metrics.mDisplayPort = CSSRect(-1, -1, 6, 6); 1.521 + metrics.mViewport = CSSRect(0, 0, 4, 4); 1.522 + metrics.SetScrollOffset(CSSPoint(10, 10)); 1.523 + metrics.mScrollableRect = CSSRect(0, 0, 50, 50); 1.524 + metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2); 1.525 + metrics.mResolution = ParentLayerToLayerScale(2); 1.526 + metrics.SetZoom(CSSToScreenScale(6)); 1.527 + metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3); 1.528 + metrics.SetScrollId(FrameMetrics::START_SCROLL_ID); 1.529 + 1.530 + FrameMetrics childMetrics = metrics; 1.531 + childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1); 1.532 + 1.533 + layers[0]->AsContainerLayer()->SetFrameMetrics(metrics); 1.534 + layers[1]->AsContainerLayer()->SetFrameMetrics(childMetrics); 1.535 + 1.536 + ScreenPoint pointOut; 1.537 + ViewTransform viewTransformOut; 1.538 + 1.539 + // Both the parent and child layer should behave exactly the same here, because 1.540 + // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code 1.541 + 1.542 + // initial transform 1.543 + apzc->SetFrameMetrics(metrics); 1.544 + apzc->NotifyLayersUpdated(metrics, true); 1.545 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.546 + EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut); 1.547 + EXPECT_EQ(ScreenPoint(60, 60), pointOut); 1.548 + 1.549 + childApzc->SetFrameMetrics(childMetrics); 1.550 + childApzc->NotifyLayersUpdated(childMetrics, true); 1.551 + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.552 + EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut); 1.553 + EXPECT_EQ(ScreenPoint(60, 60), pointOut); 1.554 + 1.555 + // do an async scroll by 5 pixels and check the transform 1.556 + metrics.ScrollBy(CSSPoint(5, 0)); 1.557 + apzc->SetFrameMetrics(metrics); 1.558 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.559 + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut); 1.560 + EXPECT_EQ(ScreenPoint(90, 60), pointOut); 1.561 + 1.562 + childMetrics.ScrollBy(CSSPoint(5, 0)); 1.563 + childApzc->SetFrameMetrics(childMetrics); 1.564 + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.565 + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut); 1.566 + EXPECT_EQ(ScreenPoint(90, 60), pointOut); 1.567 + 1.568 + // do an async zoom of 1.5x and check the transform 1.569 + metrics.ZoomBy(1.5f); 1.570 + apzc->SetFrameMetrics(metrics); 1.571 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.572 + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut); 1.573 + EXPECT_EQ(ScreenPoint(135, 90), pointOut); 1.574 + 1.575 + childMetrics.ZoomBy(1.5f); 1.576 + childApzc->SetFrameMetrics(childMetrics); 1.577 + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.578 + EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut); 1.579 + EXPECT_EQ(ScreenPoint(135, 90), pointOut); 1.580 +} 1.581 + 1.582 +TEST_F(AsyncPanZoomControllerTester, Pan) { 1.583 + DoPanTest(true, false, mozilla::layers::AllowedTouchBehavior::NONE); 1.584 +} 1.585 + 1.586 +// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top 1.587 +// to bottom and back - from bottom to top. 1.588 +// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical 1.589 +// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this 1.590 +// behavior. 1.591 +TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionAuto) { 1.592 + DoPanTest(true, true, 1.593 + mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 1.594 +} 1.595 + 1.596 +TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionNone) { 1.597 + DoPanTest(false, true, 0); 1.598 +} 1.599 + 1.600 +TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionPanX) { 1.601 + DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN); 1.602 +} 1.603 + 1.604 +TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionPanY) { 1.605 + DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 1.606 +} 1.607 + 1.608 +TEST_F(AsyncPanZoomControllerTester, PanWithPreventDefault) { 1.609 + TimeStamp testStartTime = TimeStamp::Now(); 1.610 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.611 + 1.612 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.613 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.614 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm); 1.615 + 1.616 + FrameMetrics frameMetrics(TestFrameMetrics()); 1.617 + frameMetrics.mMayHaveTouchListeners = true; 1.618 + 1.619 + apzc->SetFrameMetrics(frameMetrics); 1.620 + apzc->NotifyLayersUpdated(frameMetrics, true); 1.621 + 1.622 + int time = 0; 1.623 + int touchStart = 50; 1.624 + int touchEnd = 10; 1.625 + ScreenPoint pointOut; 1.626 + ViewTransform viewTransformOut; 1.627 + 1.628 + // Pan down 1.629 + nsTArray<uint32_t> allowedTouchBehaviors; 1.630 + allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 1.631 + apzc->SetTouchActionEnabled(true); 1.632 + ApzcPan(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors); 1.633 + 1.634 + // Send the signal that content has handled and preventDefaulted the touch 1.635 + // events. This flushes the event queue. 1.636 + apzc->ContentReceivedTouch(true); 1.637 + 1.638 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.639 + EXPECT_EQ(ScreenPoint(), pointOut); 1.640 + EXPECT_EQ(ViewTransform(), viewTransformOut); 1.641 + 1.642 + apzc->Destroy(); 1.643 +} 1.644 + 1.645 +TEST_F(AsyncPanZoomControllerTester, Fling) { 1.646 + TimeStamp testStartTime = TimeStamp::Now(); 1.647 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.648 + 1.649 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.650 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.651 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm); 1.652 + 1.653 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.654 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.655 + 1.656 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.657 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.658 + 1.659 + int time = 0; 1.660 + int touchStart = 50; 1.661 + int touchEnd = 10; 1.662 + ScreenPoint pointOut; 1.663 + ViewTransform viewTransformOut; 1.664 + 1.665 + // Fling down. Each step scroll further down 1.666 + ApzcPan(apzc, tm, time, touchStart, touchEnd); 1.667 + ScreenPoint lastPoint; 1.668 + for (int i = 1; i < 50; i+=1) { 1.669 + apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut); 1.670 + EXPECT_GT(pointOut.y, lastPoint.y); 1.671 + lastPoint = pointOut; 1.672 + } 1.673 +} 1.674 + 1.675 +TEST_F(AsyncPanZoomControllerTester, OverScrollPanning) { 1.676 + TimeStamp testStartTime = TimeStamp::Now(); 1.677 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.678 + 1.679 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.680 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.681 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm); 1.682 + 1.683 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.684 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.685 + 1.686 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.687 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.688 + 1.689 + // Pan sufficiently to hit overscroll behavior 1.690 + int time = 0; 1.691 + int touchStart = 500; 1.692 + int touchEnd = 10; 1.693 + ScreenPoint pointOut; 1.694 + ViewTransform viewTransformOut; 1.695 + 1.696 + // Pan down 1.697 + ApzcPan(apzc, tm, time, touchStart, touchEnd); 1.698 + apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &viewTransformOut, pointOut); 1.699 + EXPECT_EQ(ScreenPoint(0, 90), pointOut); 1.700 +} 1.701 + 1.702 +TEST_F(AsyncPanZoomControllerTester, ShortPress) { 1.703 + nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>(); 1.704 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.705 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController( 1.706 + 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR); 1.707 + 1.708 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.709 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.710 + apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0))); 1.711 + 1.712 + int time = 0; 1.713 + nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get()); 1.714 + EXPECT_EQ(nsEventStatus_eIgnore, status); 1.715 + 1.716 + // This verifies that the single tap notification is sent after the 1.717 + // touchdown is fully processed. The ordering here is important. 1.718 + mcc->CheckHasDelayedTask(); 1.719 + 1.720 + EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); 1.721 + mcc->RunDelayedTask(); 1.722 + 1.723 + apzc->Destroy(); 1.724 +} 1.725 + 1.726 +TEST_F(AsyncPanZoomControllerTester, MediumPress) { 1.727 + nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>(); 1.728 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.729 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController( 1.730 + 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR); 1.731 + 1.732 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.733 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.734 + apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0))); 1.735 + 1.736 + int time = 0; 1.737 + nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get()); 1.738 + EXPECT_EQ(nsEventStatus_eIgnore, status); 1.739 + 1.740 + // This verifies that the single tap notification is sent after the 1.741 + // touchdown is fully processed. The ordering here is important. 1.742 + mcc->CheckHasDelayedTask(); 1.743 + 1.744 + EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); 1.745 + mcc->RunDelayedTask(); 1.746 + 1.747 + apzc->Destroy(); 1.748 +} 1.749 + 1.750 +void 1.751 +DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) { 1.752 + nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed(); 1.753 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.754 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController( 1.755 + 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR); 1.756 + 1.757 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.758 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.759 + apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0))); 1.760 + 1.761 + apzc->SetTouchActionEnabled(aShouldUseTouchAction); 1.762 + 1.763 + int time = 0; 1.764 + 1.765 + nsEventStatus status = ApzcDown(apzc, 10, 10, time); 1.766 + EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status); 1.767 + 1.768 + // SetAllowedTouchBehavior() must be called after sending touch-start. 1.769 + nsTArray<uint32_t> allowedTouchBehaviors; 1.770 + allowedTouchBehaviors.AppendElement(aBehavior); 1.771 + apzc->SetAllowedTouchBehavior(allowedTouchBehaviors); 1.772 + 1.773 + MockFunction<void(std::string checkPointName)> check; 1.774 + 1.775 + { 1.776 + InSequence s; 1.777 + 1.778 + EXPECT_CALL(check, Call("preHandleLongTap")); 1.779 + EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); 1.780 + EXPECT_CALL(check, Call("postHandleLongTap")); 1.781 + 1.782 + EXPECT_CALL(check, Call("preHandleLongTapUp")); 1.783 + EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1); 1.784 + EXPECT_CALL(check, Call("postHandleLongTapUp")); 1.785 + } 1.786 + 1.787 + mcc->CheckHasDelayedTask(); 1.788 + 1.789 + // Manually invoke the longpress while the touch is currently down. 1.790 + check.Call("preHandleLongTap"); 1.791 + mcc->RunDelayedTask(); 1.792 + check.Call("postHandleLongTap"); 1.793 + 1.794 + // Destroy pending MAX_TAP timeout task 1.795 + mcc->DestroyOldestTask(); 1.796 + // There should be a TimeoutContentResponse task in the queue still 1.797 + // Clear the waiting-for-content timeout task, then send the signal that 1.798 + // content has handled this long tap. This takes the place of the 1.799 + // "contextmenu" event. 1.800 + mcc->CheckHasDelayedTask(); 1.801 + mcc->ClearDelayedTask(); 1.802 + apzc->ContentReceivedTouch(true); 1.803 + 1.804 + time += 1000; 1.805 + 1.806 + status = ApzcUp(apzc, 10, 10, time); 1.807 + EXPECT_EQ(nsEventStatus_eIgnore, status); 1.808 + 1.809 + // To get a LongTapUp event, we must kick APZC to flush its event queue. This 1.810 + // would normally happen if we had a (Tab|RenderFrame)(Parent|Child) 1.811 + // mechanism. 1.812 + check.Call("preHandleLongTapUp"); 1.813 + apzc->ContentReceivedTouch(false); 1.814 + check.Call("postHandleLongTapUp"); 1.815 + 1.816 + apzc->Destroy(); 1.817 +} 1.818 + 1.819 +void 1.820 +DoLongPressPreventDefaultTest(bool aShouldUseTouchAction, uint32_t aBehavior) { 1.821 + // We have to initialize both an integer time and TimeStamp time because 1.822 + // TimeStamp doesn't have any ToXXX() functions for converting back to 1.823 + // primitives. 1.824 + TimeStamp testStartTime = TimeStamp::Now(); 1.825 + int time = 0; 1.826 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.827 + 1.828 + nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed(); 1.829 + nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager(); 1.830 + nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController( 1.831 + 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR); 1.832 + 1.833 + apzc->SetFrameMetrics(TestFrameMetrics()); 1.834 + apzc->NotifyLayersUpdated(TestFrameMetrics(), true); 1.835 + apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0))); 1.836 + 1.837 + apzc->SetTouchActionEnabled(aShouldUseTouchAction); 1.838 + 1.839 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0); 1.840 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); 1.841 + 1.842 + int touchX = 10, 1.843 + touchStartY = 10, 1.844 + touchEndY = 50; 1.845 + 1.846 + nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time); 1.847 + EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status); 1.848 + 1.849 + // SetAllowedTouchBehavior() must be called after sending touch-start. 1.850 + nsTArray<uint32_t> allowedTouchBehaviors; 1.851 + allowedTouchBehaviors.AppendElement(aBehavior); 1.852 + apzc->SetAllowedTouchBehavior(allowedTouchBehaviors); 1.853 + 1.854 + MockFunction<void(std::string checkPointName)> check; 1.855 + 1.856 + { 1.857 + InSequence s; 1.858 + 1.859 + EXPECT_CALL(check, Call("preHandleLongTap")); 1.860 + EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1); 1.861 + EXPECT_CALL(check, Call("postHandleLongTap")); 1.862 + } 1.863 + 1.864 + mcc->CheckHasDelayedTask(); 1.865 + 1.866 + // Manually invoke the longpress while the touch is currently down. 1.867 + check.Call("preHandleLongTap"); 1.868 + mcc->RunDelayedTask(); 1.869 + check.Call("postHandleLongTap"); 1.870 + 1.871 + // Destroy pending MAX_TAP timeout task 1.872 + mcc->DestroyOldestTask(); 1.873 + // Clear the waiting-for-content timeout task, then send the signal that 1.874 + // content has handled this long tap. This takes the place of the 1.875 + // "contextmenu" event. 1.876 + mcc->ClearDelayedTask(); 1.877 + apzc->ContentReceivedTouch(true); 1.878 + 1.879 + time += 1000; 1.880 + 1.881 + MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, 0); 1.882 + mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0)); 1.883 + status = apzc->ReceiveInputEvent(mti); 1.884 + EXPECT_EQ(nsEventStatus_eIgnore, status); 1.885 + 1.886 + EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(1); 1.887 + status = ApzcUp(apzc, touchX, touchEndY, time); 1.888 + EXPECT_EQ(nsEventStatus_eIgnore, status); 1.889 + 1.890 + // Flush the event queue. Once the "contextmenu" event is handled, any touch 1.891 + // events that come from the same series of start->n*move->end events should 1.892 + // be discarded, even if only the "contextmenu" event is preventDefaulted. 1.893 + apzc->ContentReceivedTouch(false); 1.894 + 1.895 + ScreenPoint pointOut; 1.896 + ViewTransform viewTransformOut; 1.897 + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); 1.898 + 1.899 + EXPECT_EQ(ScreenPoint(), pointOut); 1.900 + EXPECT_EQ(ViewTransform(), viewTransformOut); 1.901 + 1.902 + apzc->Destroy(); 1.903 +} 1.904 + 1.905 +TEST_F(AsyncPanZoomControllerTester, LongPress) { 1.906 + DoLongPressTest(false, mozilla::layers::AllowedTouchBehavior::NONE); 1.907 +} 1.908 + 1.909 +TEST_F(AsyncPanZoomControllerTester, LongPressWithTouchAction) { 1.910 + DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN 1.911 + | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN 1.912 + | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); 1.913 +} 1.914 + 1.915 +TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) { 1.916 + DoLongPressPreventDefaultTest(false, mozilla::layers::AllowedTouchBehavior::NONE); 1.917 +} 1.918 + 1.919 +TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefaultWithTouchAction) { 1.920 + DoLongPressPreventDefaultTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN 1.921 + | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN 1.922 + | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); 1.923 +} 1.924 + 1.925 +// Layer tree for HitTesting1 1.926 +static already_AddRefed<mozilla::layers::Layer> 1.927 +CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) { 1.928 + const char* layerTreeSyntax = "c(ttcc)"; 1.929 + // LayerID 0 1234 1.930 + nsIntRegion layerVisibleRegion[] = { 1.931 + nsIntRegion(nsIntRect(0,0,100,100)), 1.932 + nsIntRegion(nsIntRect(0,0,100,100)), 1.933 + nsIntRegion(nsIntRect(10,10,20,20)), 1.934 + nsIntRegion(nsIntRect(10,10,20,20)), 1.935 + nsIntRegion(nsIntRect(5,5,20,20)), 1.936 + }; 1.937 + gfx3DMatrix transforms[] = { 1.938 + gfx3DMatrix(), 1.939 + gfx3DMatrix(), 1.940 + gfx3DMatrix(), 1.941 + gfx3DMatrix(), 1.942 + gfx3DMatrix(), 1.943 + }; 1.944 + return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers); 1.945 +} 1.946 + 1.947 +// Layer Tree for HitTesting2 1.948 +static already_AddRefed<mozilla::layers::Layer> 1.949 +CreateTestLayerTree2(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) { 1.950 + const char* layerTreeSyntax = "c(cc(c))"; 1.951 + // LayerID 0 12 3 1.952 + nsIntRegion layerVisibleRegion[] = { 1.953 + nsIntRegion(nsIntRect(0,0,100,100)), 1.954 + nsIntRegion(nsIntRect(10,10,40,40)), 1.955 + nsIntRegion(nsIntRect(10,60,40,40)), 1.956 + nsIntRegion(nsIntRect(10,60,40,40)), 1.957 + }; 1.958 + gfx3DMatrix transforms[] = { 1.959 + gfx3DMatrix(), 1.960 + gfx3DMatrix(), 1.961 + gfx3DMatrix(), 1.962 + gfx3DMatrix(), 1.963 + }; 1.964 + return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers); 1.965 +} 1.966 + 1.967 +static void 1.968 +SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId, 1.969 + // The scrollable rect is only used in HitTesting2, 1.970 + // HitTesting1 doesn't care about it. 1.971 + CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) 1.972 +{ 1.973 + ContainerLayer* container = aLayer->AsContainerLayer(); 1.974 + FrameMetrics metrics; 1.975 + metrics.SetScrollId(aScrollId); 1.976 + nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds(); 1.977 + metrics.mCompositionBounds = ParentLayerIntRect(layerBound.x, layerBound.y, 1.978 + layerBound.width, layerBound.height); 1.979 + metrics.mScrollableRect = aScrollableRect; 1.980 + metrics.SetScrollOffset(CSSPoint(0, 0)); 1.981 + container->SetFrameMetrics(metrics); 1.982 +} 1.983 + 1.984 +static already_AddRefed<AsyncPanZoomController> 1.985 +GetTargetAPZC(APZCTreeManager* manager, const ScreenPoint& aPoint, 1.986 + gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToGeckoOut) 1.987 +{ 1.988 + nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint); 1.989 + if (hit) { 1.990 + manager->GetInputTransforms(hit.get(), aTransformToApzcOut, aTransformToGeckoOut); 1.991 + } 1.992 + return hit.forget(); 1.993 +} 1.994 + 1.995 +// A simple hit testing test that doesn't involve any transforms on layers. 1.996 +TEST_F(APZCTreeManagerTester, HitTesting1) { 1.997 + nsTArray<nsRefPtr<Layer> > layers; 1.998 + nsRefPtr<LayerManager> lm; 1.999 + nsRefPtr<Layer> root = CreateTestLayerTree1(lm, layers); 1.1000 + 1.1001 + TimeStamp testStartTime = TimeStamp::Now(); 1.1002 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.1003 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.1004 + ScopedLayerTreeRegistration controller(0, root, mcc); 1.1005 + 1.1006 + nsRefPtr<APZCTreeManager> manager = new TestAPZCTreeManager(); 1.1007 + gfx3DMatrix transformToApzc; 1.1008 + gfx3DMatrix transformToGecko; 1.1009 + 1.1010 + // No APZC attached so hit testing will return no APZC at (20,20) 1.1011 + nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(manager, ScreenPoint(20, 20), transformToApzc, transformToGecko); 1.1012 + AsyncPanZoomController* nullAPZC = nullptr; 1.1013 + EXPECT_EQ(nullAPZC, hit.get()); 1.1014 + EXPECT_EQ(gfx3DMatrix(), transformToApzc); 1.1015 + EXPECT_EQ(gfx3DMatrix(), transformToGecko); 1.1016 + 1.1017 + // Now we have a root APZC that will match the page 1.1018 + SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); 1.1019 + manager->UpdatePanZoomControllerTree(nullptr, root, false, 0); 1.1020 + hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko); 1.1021 + EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); 1.1022 + // expect hit point at LayerIntPoint(15, 15) 1.1023 + EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15))); 1.1024 + EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15))); 1.1025 + 1.1026 + // Now we have a sub APZC with a better fit 1.1027 + SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1); 1.1028 + manager->UpdatePanZoomControllerTree(nullptr, root, false, 0); 1.1029 + EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController()); 1.1030 + hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko); 1.1031 + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); 1.1032 + // expect hit point at LayerIntPoint(15, 15) 1.1033 + EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15))); 1.1034 + EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15))); 1.1035 + 1.1036 + // Now test hit testing when we have two scrollable layers 1.1037 + hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko); 1.1038 + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); 1.1039 + SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2); 1.1040 + manager->UpdatePanZoomControllerTree(nullptr, root, false, 0); 1.1041 + hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko); 1.1042 + EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); 1.1043 + // expect hit point at LayerIntPoint(15, 15) 1.1044 + EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15))); 1.1045 + EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15))); 1.1046 + 1.1047 + // Hit test ouside the reach of layer[3,4] but inside root 1.1048 + hit = GetTargetAPZC(manager, ScreenPoint(90, 90), transformToApzc, transformToGecko); 1.1049 + EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); 1.1050 + // expect hit point at LayerIntPoint(90, 90) 1.1051 + EXPECT_EQ(gfxPoint(90, 90), transformToApzc.Transform(gfxPoint(90, 90))); 1.1052 + EXPECT_EQ(gfxPoint(90, 90), transformToGecko.Transform(gfxPoint(90, 90))); 1.1053 + 1.1054 + // Hit test ouside the reach of any layer 1.1055 + hit = GetTargetAPZC(manager, ScreenPoint(1000, 10), transformToApzc, transformToGecko); 1.1056 + EXPECT_EQ(nullAPZC, hit.get()); 1.1057 + EXPECT_EQ(gfx3DMatrix(), transformToApzc); 1.1058 + EXPECT_EQ(gfx3DMatrix(), transformToGecko); 1.1059 + hit = GetTargetAPZC(manager, ScreenPoint(-1000, 10), transformToApzc, transformToGecko); 1.1060 + EXPECT_EQ(nullAPZC, hit.get()); 1.1061 + EXPECT_EQ(gfx3DMatrix(), transformToApzc); 1.1062 + EXPECT_EQ(gfx3DMatrix(), transformToGecko); 1.1063 + 1.1064 + manager->ClearTree(); 1.1065 +} 1.1066 + 1.1067 +// A more involved hit testing test that involves css and async transforms. 1.1068 +TEST_F(APZCTreeManagerTester, HitTesting2) { 1.1069 + nsTArray<nsRefPtr<Layer> > layers; 1.1070 + nsRefPtr<LayerManager> lm; 1.1071 + nsRefPtr<Layer> root = CreateTestLayerTree2(lm, layers); 1.1072 + 1.1073 + TimeStamp testStartTime = TimeStamp::Now(); 1.1074 + AsyncPanZoomController::SetFrameTime(testStartTime); 1.1075 + nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>(); 1.1076 + ScopedLayerTreeRegistration controller(0, root, mcc); 1.1077 + 1.1078 + nsRefPtr<TestAPZCTreeManager> manager = new TestAPZCTreeManager(); 1.1079 + nsRefPtr<AsyncPanZoomController> hit; 1.1080 + gfx3DMatrix transformToApzc; 1.1081 + gfx3DMatrix transformToGecko; 1.1082 + 1.1083 + // Set a CSS transform on one of the layers. 1.1084 + Matrix4x4 transform; 1.1085 + transform = transform * Matrix4x4().Scale(2, 1, 1); 1.1086 + layers[2]->SetBaseTransform(transform); 1.1087 + 1.1088 + // Make some other layers scrollable. 1.1089 + SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); 1.1090 + SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80)); 1.1091 + SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80)); 1.1092 + 1.1093 + manager->UpdatePanZoomControllerTree(nullptr, root, false, 0); 1.1094 + 1.1095 + // At this point, the following holds (all coordinates in screen pixels): 1.1096 + // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100) 1.1097 + // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50) 1.1098 + // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer 1.1099 + // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100) 1.1100 + 1.1101 + AsyncPanZoomController* apzcroot = root->AsContainerLayer()->GetAsyncPanZoomController(); 1.1102 + AsyncPanZoomController* apzc1 = layers[1]->AsContainerLayer()->GetAsyncPanZoomController(); 1.1103 + AsyncPanZoomController* apzc3 = layers[3]->AsContainerLayer()->GetAsyncPanZoomController(); 1.1104 + 1.1105 + // Hit an area that's clearly on the root layer but not any of the child layers. 1.1106 + hit = GetTargetAPZC(manager, ScreenPoint(75, 25), transformToApzc, transformToGecko); 1.1107 + EXPECT_EQ(apzcroot, hit.get()); 1.1108 + EXPECT_EQ(gfxPoint(75, 25), transformToApzc.Transform(gfxPoint(75, 25))); 1.1109 + EXPECT_EQ(gfxPoint(75, 25), transformToGecko.Transform(gfxPoint(75, 25))); 1.1110 + 1.1111 + // Hit an area on the root that would be on layers[3] if layers[2] 1.1112 + // weren't transformed. 1.1113 + // Note that if layers[2] were scrollable, then this would hit layers[2] 1.1114 + // because its composition bounds would be at (10,60)-(50,100) (and the 1.1115 + // scale-only transform that we set on layers[2] would be invalid because 1.1116 + // it would place the layer into overscroll, as its composition bounds 1.1117 + // start at x=10 but its content at x=20). 1.1118 + hit = GetTargetAPZC(manager, ScreenPoint(15, 75), transformToApzc, transformToGecko); 1.1119 + EXPECT_EQ(apzcroot, hit.get()); 1.1120 + EXPECT_EQ(gfxPoint(15, 75), transformToApzc.Transform(gfxPoint(15, 75))); 1.1121 + EXPECT_EQ(gfxPoint(15, 75), transformToGecko.Transform(gfxPoint(15, 75))); 1.1122 + 1.1123 + // Hit an area on layers[1]. 1.1124 + hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko); 1.1125 + EXPECT_EQ(apzc1, hit.get()); 1.1126 + EXPECT_EQ(gfxPoint(25, 25), transformToApzc.Transform(gfxPoint(25, 25))); 1.1127 + EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(25, 25))); 1.1128 + 1.1129 + // Hit an area on layers[3]. 1.1130 + hit = GetTargetAPZC(manager, ScreenPoint(25, 75), transformToApzc, transformToGecko); 1.1131 + EXPECT_EQ(apzc3, hit.get()); 1.1132 + // transformToApzc should unapply layers[2]'s transform 1.1133 + EXPECT_EQ(gfxPoint(12.5, 75), transformToApzc.Transform(gfxPoint(25, 75))); 1.1134 + // and transformToGecko should reapply it 1.1135 + EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(12.5, 75))); 1.1136 + 1.1137 + // Hit an area on layers[3] that would be on the root if layers[2] 1.1138 + // weren't transformed. 1.1139 + hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko); 1.1140 + EXPECT_EQ(apzc3, hit.get()); 1.1141 + // transformToApzc should unapply layers[2]'s transform 1.1142 + EXPECT_EQ(gfxPoint(37.5, 75), transformToApzc.Transform(gfxPoint(75, 75))); 1.1143 + // and transformToGecko should reapply it 1.1144 + EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(37.5, 75))); 1.1145 + 1.1146 + // Pan the root layer upward by 50 pixels. 1.1147 + // This causes layers[1] to scroll out of view, and an async transform 1.1148 + // of -50 to be set on the root layer. 1.1149 + int time = 0; 1.1150 + // Silence GMock warnings about "uninteresting mock function calls". 1.1151 + EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(AtLeast(1)); 1.1152 + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1)); 1.1153 + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 1.1154 + 1.1155 + // This first pan will move the APZC by 50 pixels, and dispatch a paint request. 1.1156 + // Since this paint request is in the queue to Gecko, transformToGecko will 1.1157 + // take it into account. 1.1158 + ApzcPan(apzcroot, manager, time, 100, 50); 1.1159 + 1.1160 + // Hit where layers[3] used to be. It should now hit the root. 1.1161 + hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko); 1.1162 + EXPECT_EQ(apzcroot, hit.get()); 1.1163 + // transformToApzc doesn't unapply the root's own async transform 1.1164 + EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75))); 1.1165 + // and transformToGecko unapplies it and then reapplies it, because by the 1.1166 + // time the event being transformed reaches Gecko the new paint request will 1.1167 + // have been handled. 1.1168 + EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(75, 75))); 1.1169 + 1.1170 + // Hit where layers[1] used to be and where layers[3] should now be. 1.1171 + hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko); 1.1172 + EXPECT_EQ(apzc3, hit.get()); 1.1173 + // transformToApzc unapplies both layers[2]'s css transform and the root's 1.1174 + // async transform 1.1175 + EXPECT_EQ(gfxPoint(12.5, 75), transformToApzc.Transform(gfxPoint(25, 25))); 1.1176 + // transformToGecko reapplies both the css transform and the async transform 1.1177 + // because we have already issued a paint request with it. 1.1178 + EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(12.5, 75))); 1.1179 + 1.1180 + // This second pan will move the APZC by another 50 pixels but since the paint 1.1181 + // request dispatched above has not "completed", we will not dispatch another 1.1182 + // one yet. Now we have an async transform on top of the pending paint request 1.1183 + // transform. 1.1184 + ApzcPan(apzcroot, manager, time, 100, 50); 1.1185 + 1.1186 + // Hit where layers[3] used to be. It should now hit the root. 1.1187 + hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko); 1.1188 + EXPECT_EQ(apzcroot, hit.get()); 1.1189 + // transformToApzc doesn't unapply the root's own async transform 1.1190 + EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75))); 1.1191 + // transformToGecko unapplies the full async transform of -100 pixels, and then 1.1192 + // reapplies the "D" transform of -50 leading to an overall adjustment of +50 1.1193 + EXPECT_EQ(gfxPoint(75, 125), transformToGecko.Transform(gfxPoint(75, 75))); 1.1194 + 1.1195 + // Hit where layers[1] used to be. It should now hit the root. 1.1196 + hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko); 1.1197 + EXPECT_EQ(apzcroot, hit.get()); 1.1198 + // transformToApzc doesn't unapply the root's own async transform 1.1199 + EXPECT_EQ(gfxPoint(25, 25), transformToApzc.Transform(gfxPoint(25, 25))); 1.1200 + // transformToGecko unapplies the full async transform of -100 pixels, and then 1.1201 + // reapplies the "D" transform of -50 leading to an overall adjustment of +50 1.1202 + EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(25, 25))); 1.1203 + 1.1204 + manager->ClearTree(); 1.1205 +}