Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* vim:set ts=2 sw=2 sts=2 et: */
2 /* Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
4 */
6 #include "gtest/gtest.h"
7 #include "gmock/gmock.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
11 #include "mozilla/layers/AsyncPanZoomController.h"
12 #include "mozilla/layers/LayerManagerComposite.h"
13 #include "mozilla/layers/GeckoContentController.h"
14 #include "mozilla/layers/CompositorParent.h"
15 #include "mozilla/layers/APZCTreeManager.h"
16 #include "base/task.h"
17 #include "Layers.h"
18 #include "TestLayers.h"
19 #include "gfxPrefs.h"
21 using namespace mozilla;
22 using namespace mozilla::gfx;
23 using namespace mozilla::layers;
24 using ::testing::_;
25 using ::testing::NiceMock;
26 using ::testing::AtLeast;
27 using ::testing::AtMost;
28 using ::testing::MockFunction;
29 using ::testing::InSequence;
31 class Task;
33 class AsyncPanZoomControllerTester : public ::testing::Test {
34 protected:
35 virtual void SetUp() {
36 gfxPrefs::GetSingleton();
37 }
38 virtual void TearDown() {
39 gfxPrefs::DestroySingleton();
40 }
41 };
43 class APZCTreeManagerTester : public ::testing::Test {
44 protected:
45 virtual void SetUp() {
46 gfxPrefs::GetSingleton();
47 }
48 virtual void TearDown() {
49 gfxPrefs::DestroySingleton();
50 }
51 };
53 class MockContentController : public GeckoContentController {
54 public:
55 MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
56 MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
57 MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
58 MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
59 MOCK_METHOD3(HandleLongTap, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
60 MOCK_METHOD3(HandleLongTapUp, void(const CSSPoint&, int32_t, const ScrollableLayerGuid&));
61 MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
62 MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
63 };
65 class MockContentControllerDelayed : public MockContentController {
66 public:
67 MockContentControllerDelayed()
68 {
69 }
71 void PostDelayedTask(Task* aTask, int aDelayMs) {
72 mTaskQueue.AppendElement(aTask);
73 }
75 void CheckHasDelayedTask() {
76 EXPECT_TRUE(mTaskQueue.Length() > 0);
77 }
79 void ClearDelayedTask() {
80 mTaskQueue.RemoveElementAt(0);
81 }
83 void DestroyOldestTask() {
84 delete mTaskQueue[0];
85 mTaskQueue.RemoveElementAt(0);
86 }
88 // Note that deleting mCurrentTask is important in order to
89 // release the reference to the callee object. Without this
90 // that object might be leaked. This is also why we don't
91 // expose mTaskQueue to any users of MockContentControllerDelayed.
92 void RunDelayedTask() {
93 mTaskQueue[0]->Run();
94 delete mTaskQueue[0];
95 mTaskQueue.RemoveElementAt(0);
96 }
98 private:
99 nsTArray<Task*> mTaskQueue;
100 };
103 class TestAPZCContainerLayer : public ContainerLayer {
104 public:
105 TestAPZCContainerLayer()
106 : ContainerLayer(nullptr, nullptr)
107 {}
108 bool RemoveChild(Layer* aChild) { return true; }
109 bool InsertAfter(Layer* aChild, Layer* aAfter) { return true; }
110 void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) {}
111 bool RepositionChild(Layer* aChild, Layer* aAfter) { return true; }
112 };
114 class TestAsyncPanZoomController : public AsyncPanZoomController {
115 public:
116 TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc,
117 APZCTreeManager* aTreeManager = nullptr,
118 GestureBehavior aBehavior = DEFAULT_GESTURES)
119 : AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior)
120 {}
122 // Since touch-action-enabled property is global - setting it for each test
123 // separately isn't safe from the concurrency point of view. To make tests
124 // run concurrent and independent from each other we have a member variable
125 // mTouchActionEnabled for each apzc and setter defined here.
126 void SetTouchActionEnabled(const bool touchActionEnabled) {
127 ReentrantMonitorAutoEnter lock(mMonitor);
128 mTouchActionPropertyEnabled = touchActionEnabled;
129 }
131 void SetFrameMetrics(const FrameMetrics& metrics) {
132 ReentrantMonitorAutoEnter lock(mMonitor);
133 mFrameMetrics = metrics;
134 }
136 FrameMetrics GetFrameMetrics() {
137 ReentrantMonitorAutoEnter lock(mMonitor);
138 return mFrameMetrics;
139 }
140 };
142 class TestAPZCTreeManager : public APZCTreeManager {
143 protected:
144 void AssertOnCompositorThread() MOZ_OVERRIDE { /* no-op */ }
146 public:
147 // Expose this so test code can call it directly.
148 void BuildOverscrollHandoffChain(AsyncPanZoomController* aApzc) {
149 APZCTreeManager::BuildOverscrollHandoffChain(aApzc);
150 }
151 };
153 static
154 FrameMetrics TestFrameMetrics() {
155 FrameMetrics fm;
157 fm.mDisplayPort = CSSRect(0, 0, 10, 10);
158 fm.mCompositionBounds = ParentLayerIntRect(0, 0, 10, 10);
159 fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
160 fm.mScrollableRect = CSSRect(0, 0, 100, 100);
161 fm.mViewport = CSSRect(0, 0, 10, 10);
163 return fm;
164 }
166 /*
167 * Dispatches mock touch events to the apzc and checks whether apzc properly
168 * consumed them and triggered scrolling behavior.
169 */
170 static
171 void ApzcPan(AsyncPanZoomController* apzc,
172 TestAPZCTreeManager* aTreeManager,
173 int& aTime,
174 int aTouchStartY,
175 int aTouchEndY,
176 bool expectIgnoredPan = false,
177 bool hasTouchListeners = false,
178 nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
180 const int TIME_BETWEEN_TOUCH_EVENT = 100;
181 const int OVERCOME_TOUCH_TOLERANCE = 100;
182 MultiTouchInput mti;
183 nsEventStatus status;
185 // Since we're passing inputs directly to the APZC instead of going through
186 // the tree manager, we need to build the overscroll handoff chain explicitly
187 // for panning to work correctly.
188 aTreeManager->BuildOverscrollHandoffChain(apzc);
190 nsEventStatus touchStartStatus;
191 if (hasTouchListeners) {
192 // APZC shouldn't consume the start event now, instead queueing it up
193 // waiting for content's response.
194 touchStartStatus = nsEventStatus_eIgnore;
195 } else {
196 // APZC should go into the touching state and therefore consume the event.
197 touchStartStatus = nsEventStatus_eConsumeNoDefault;
198 }
200 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
201 aTime += TIME_BETWEEN_TOUCH_EVENT;
202 // Make sure the move is large enough to not be handled as a tap
203 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY+OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
204 status = apzc->ReceiveInputEvent(mti);
205 EXPECT_EQ(touchStartStatus, status);
206 // APZC should be in TOUCHING state
208 // Allowed touch behaviours must be set after sending touch-start.
209 if (aAllowedTouchBehaviors) {
210 apzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
211 }
213 nsEventStatus touchMoveStatus;
214 if (expectIgnoredPan) {
215 // APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
216 // The same applies to all consequent touch move events.
217 touchMoveStatus = nsEventStatus_eIgnore;
218 } else {
219 // APZC should go into the panning state and therefore consume the event.
220 touchMoveStatus = nsEventStatus_eConsumeNoDefault;
221 }
223 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0);
224 aTime += TIME_BETWEEN_TOUCH_EVENT;
225 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
226 status = apzc->ReceiveInputEvent(mti);
227 EXPECT_EQ(touchMoveStatus, status);
229 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, 0);
230 aTime += TIME_BETWEEN_TOUCH_EVENT;
231 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
232 status = apzc->ReceiveInputEvent(mti);
233 EXPECT_EQ(touchMoveStatus, status);
235 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
236 aTime += TIME_BETWEEN_TOUCH_EVENT;
237 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
238 status = apzc->ReceiveInputEvent(mti);
239 }
241 static
242 void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t aBehavior)
243 {
244 TimeStamp testStartTime = TimeStamp::Now();
245 AsyncPanZoomController::SetFrameTime(testStartTime);
247 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
248 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
249 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
251 apzc->SetTouchActionEnabled(aShouldUseTouchAction);
252 apzc->SetFrameMetrics(TestFrameMetrics());
253 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
255 if (aShouldTriggerScroll) {
256 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
257 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
258 } else {
259 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
260 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
261 }
263 int time = 0;
264 int touchStart = 50;
265 int touchEnd = 10;
266 ScreenPoint pointOut;
267 ViewTransform viewTransformOut;
269 nsTArray<uint32_t> allowedTouchBehaviors;
270 allowedTouchBehaviors.AppendElement(aBehavior);
272 // Pan down
273 ApzcPan(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
274 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
276 if (aShouldTriggerScroll) {
277 EXPECT_EQ(ScreenPoint(0, -(touchEnd-touchStart)), pointOut);
278 EXPECT_NE(ViewTransform(), viewTransformOut);
279 } else {
280 EXPECT_EQ(ScreenPoint(), pointOut);
281 EXPECT_EQ(ViewTransform(), viewTransformOut);
282 }
284 // Pan back
285 ApzcPan(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
286 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
288 EXPECT_EQ(ScreenPoint(), pointOut);
289 EXPECT_EQ(ViewTransform(), viewTransformOut);
291 apzc->Destroy();
292 }
294 static void
295 ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale) {
296 aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
297 0,
298 ScreenPoint(aFocusX, aFocusY),
299 10.0,
300 10.0,
301 0));
302 aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
303 0,
304 ScreenPoint(aFocusX, aFocusY),
305 10.0 * aScale,
306 10.0,
307 0));
308 aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
309 0,
310 ScreenPoint(aFocusX, aFocusY),
311 // note: negative values here tell APZC
312 // not to turn the pinch into a pan
313 -1.0,
314 -1.0,
315 0));
316 }
318 static nsEventStatus
319 ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
320 MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
321 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
322 return apzc->ReceiveInputEvent(mti);
323 }
325 static nsEventStatus
326 ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
327 MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
328 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
329 return apzc->ReceiveInputEvent(mti);
330 }
332 static nsEventStatus
333 ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength, MockContentControllerDelayed* mcc = nullptr) {
334 nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
335 if (mcc != nullptr) {
336 // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
337 // if we were provided a non-null mcc we want to clear them.
338 mcc->CheckHasDelayedTask();
339 mcc->ClearDelayedTask();
340 mcc->CheckHasDelayedTask();
341 mcc->ClearDelayedTask();
342 }
343 EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
344 aTime += aTapLength;
345 return ApzcUp(apzc, aX, aY, aTime);
346 }
348 TEST_F(AsyncPanZoomControllerTester, Constructor) {
349 // RefCounted class can't live in the stack
350 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
351 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
352 apzc->SetFrameMetrics(TestFrameMetrics());
353 }
355 TEST_F(AsyncPanZoomControllerTester, Pinch) {
356 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
357 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
359 FrameMetrics fm;
360 fm.mViewport = CSSRect(0, 0, 980, 480);
361 fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
362 fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
363 fm.SetScrollOffset(CSSPoint(300, 300));
364 fm.SetZoom(CSSToScreenScale(2.0));
365 apzc->SetFrameMetrics(fm);
366 apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
367 // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
369 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
370 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
372 ApzcPinch(apzc, 250, 300, 1.25);
374 // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
375 fm = apzc->GetFrameMetrics();
376 EXPECT_EQ(2.5f, fm.GetZoom().scale);
377 EXPECT_EQ(305, fm.GetScrollOffset().x);
378 EXPECT_EQ(310, fm.GetScrollOffset().y);
380 // part 2 of the test, move to the top-right corner of the page and pinch and
381 // make sure we stay in the correct spot
382 fm.SetZoom(CSSToScreenScale(2.0));
383 fm.SetScrollOffset(CSSPoint(930, 5));
384 apzc->SetFrameMetrics(fm);
385 // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
387 ApzcPinch(apzc, 250, 300, 0.5);
389 // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
390 fm = apzc->GetFrameMetrics();
391 EXPECT_EQ(1.0f, fm.GetZoom().scale);
392 EXPECT_EQ(880, fm.GetScrollOffset().x);
393 EXPECT_EQ(0, fm.GetScrollOffset().y);
395 apzc->Destroy();
396 }
398 TEST_F(AsyncPanZoomControllerTester, PinchWithTouchActionNone) {
399 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
400 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
402 FrameMetrics fm;
403 fm.mViewport = CSSRect(0, 0, 980, 480);
404 fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
405 fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
406 fm.SetScrollOffset(CSSPoint(300, 300));
407 fm.SetZoom(CSSToScreenScale(2.0));
408 apzc->SetFrameMetrics(fm);
409 // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
411 // Apzc's OnScaleEnd method calls once SendAsyncScrollDOMEvent and RequestContentRepaint methods,
412 // therefore we're setting these specific values.
413 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(1));
414 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtMost(1));
416 nsTArray<uint32_t> values;
417 values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
418 values.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
419 apzc->SetTouchActionEnabled(true);
421 apzc->SetAllowedTouchBehavior(values);
422 ApzcPinch(apzc, 250, 300, 1.25);
424 // The frame metrics should stay the same since touch-action:none makes
425 // apzc ignore pinch gestures.
426 fm = apzc->GetFrameMetrics();
427 EXPECT_EQ(2.0f, fm.GetZoom().scale);
428 EXPECT_EQ(300, fm.GetScrollOffset().x);
429 EXPECT_EQ(300, fm.GetScrollOffset().y);
430 }
432 TEST_F(AsyncPanZoomControllerTester, Overzoom) {
433 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
434 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
436 FrameMetrics fm;
437 fm.mViewport = CSSRect(0, 0, 100, 100);
438 fm.mCompositionBounds = ParentLayerIntRect(0, 0, 100, 100);
439 fm.mScrollableRect = CSSRect(0, 0, 125, 150);
440 fm.SetScrollOffset(CSSPoint(10, 0));
441 fm.SetZoom(CSSToScreenScale(1.0));
442 apzc->SetFrameMetrics(fm);
443 apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
444 // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
446 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
447 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
449 ApzcPinch(apzc, 50, 50, 0.5);
451 fm = apzc->GetFrameMetrics();
452 EXPECT_EQ(0.8f, fm.GetZoom().scale);
453 // bug 936721 - PGO builds introduce rounding error so
454 // use a fuzzy match instead
455 EXPECT_LT(abs(fm.GetScrollOffset().x), 1e-5);
456 EXPECT_LT(abs(fm.GetScrollOffset().y), 1e-5);
457 }
459 TEST_F(AsyncPanZoomControllerTester, SimpleTransform) {
460 TimeStamp testStartTime = TimeStamp::Now();
461 // RefCounted class can't live in the stack
462 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
463 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
464 apzc->SetFrameMetrics(TestFrameMetrics());
466 ScreenPoint pointOut;
467 ViewTransform viewTransformOut;
468 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
470 EXPECT_EQ(ScreenPoint(), pointOut);
471 EXPECT_EQ(ViewTransform(), viewTransformOut);
472 }
475 TEST_F(AsyncPanZoomControllerTester, ComplexTransform) {
476 TimeStamp testStartTime = TimeStamp::Now();
477 AsyncPanZoomController::SetFrameTime(testStartTime);
479 // This test assumes there is a page that gets rendered to
480 // two layers. In CSS pixels, the first layer is 50x50 and
481 // the second layer is 25x50. The widget scale factor is 3.0
482 // and the presShell resolution is 2.0. Therefore, these layers
483 // end up being 300x300 and 150x300 in layer pixels.
484 //
485 // The second (child) layer has an additional CSS transform that
486 // stretches it by 2.0 on the x-axis. Therefore, after applying
487 // CSS transforms, the two layers are the same size in screen
488 // pixels.
489 //
490 // The screen itself is 24x24 in screen pixels (therefore 4x4 in
491 // CSS pixels). The displayport is 1 extra CSS pixel on all
492 // sides.
494 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
495 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
496 nsRefPtr<TestAsyncPanZoomController> childApzc = new TestAsyncPanZoomController(0, mcc);
498 const char* layerTreeSyntax = "c(c)";
499 // LayerID 0 1
500 nsIntRegion layerVisibleRegion[] = {
501 nsIntRegion(nsIntRect(0, 0, 300, 300)),
502 nsIntRegion(nsIntRect(0, 0, 150, 300)),
503 };
504 gfx3DMatrix transforms[] = {
505 gfx3DMatrix(),
506 gfx3DMatrix(),
507 };
508 transforms[0].ScalePost(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
509 transforms[1].ScalePost(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
511 nsTArray<nsRefPtr<Layer> > layers;
512 nsRefPtr<LayerManager> lm;
513 nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
515 FrameMetrics metrics;
516 metrics.mCompositionBounds = ParentLayerIntRect(0, 0, 24, 24);
517 metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
518 metrics.mViewport = CSSRect(0, 0, 4, 4);
519 metrics.SetScrollOffset(CSSPoint(10, 10));
520 metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
521 metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2);
522 metrics.mResolution = ParentLayerToLayerScale(2);
523 metrics.SetZoom(CSSToScreenScale(6));
524 metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3);
525 metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
527 FrameMetrics childMetrics = metrics;
528 childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
530 layers[0]->AsContainerLayer()->SetFrameMetrics(metrics);
531 layers[1]->AsContainerLayer()->SetFrameMetrics(childMetrics);
533 ScreenPoint pointOut;
534 ViewTransform viewTransformOut;
536 // Both the parent and child layer should behave exactly the same here, because
537 // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
539 // initial transform
540 apzc->SetFrameMetrics(metrics);
541 apzc->NotifyLayersUpdated(metrics, true);
542 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
543 EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut);
544 EXPECT_EQ(ScreenPoint(60, 60), pointOut);
546 childApzc->SetFrameMetrics(childMetrics);
547 childApzc->NotifyLayersUpdated(childMetrics, true);
548 childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
549 EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut);
550 EXPECT_EQ(ScreenPoint(60, 60), pointOut);
552 // do an async scroll by 5 pixels and check the transform
553 metrics.ScrollBy(CSSPoint(5, 0));
554 apzc->SetFrameMetrics(metrics);
555 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
556 EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
557 EXPECT_EQ(ScreenPoint(90, 60), pointOut);
559 childMetrics.ScrollBy(CSSPoint(5, 0));
560 childApzc->SetFrameMetrics(childMetrics);
561 childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
562 EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
563 EXPECT_EQ(ScreenPoint(90, 60), pointOut);
565 // do an async zoom of 1.5x and check the transform
566 metrics.ZoomBy(1.5f);
567 apzc->SetFrameMetrics(metrics);
568 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
569 EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
570 EXPECT_EQ(ScreenPoint(135, 90), pointOut);
572 childMetrics.ZoomBy(1.5f);
573 childApzc->SetFrameMetrics(childMetrics);
574 childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
575 EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
576 EXPECT_EQ(ScreenPoint(135, 90), pointOut);
577 }
579 TEST_F(AsyncPanZoomControllerTester, Pan) {
580 DoPanTest(true, false, mozilla::layers::AllowedTouchBehavior::NONE);
581 }
583 // In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
584 // to bottom and back - from bottom to top.
585 // According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
586 // scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
587 // behavior.
588 TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionAuto) {
589 DoPanTest(true, true,
590 mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
591 }
593 TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionNone) {
594 DoPanTest(false, true, 0);
595 }
597 TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionPanX) {
598 DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
599 }
601 TEST_F(AsyncPanZoomControllerTester, PanWithTouchActionPanY) {
602 DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
603 }
605 TEST_F(AsyncPanZoomControllerTester, PanWithPreventDefault) {
606 TimeStamp testStartTime = TimeStamp::Now();
607 AsyncPanZoomController::SetFrameTime(testStartTime);
609 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
610 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
611 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
613 FrameMetrics frameMetrics(TestFrameMetrics());
614 frameMetrics.mMayHaveTouchListeners = true;
616 apzc->SetFrameMetrics(frameMetrics);
617 apzc->NotifyLayersUpdated(frameMetrics, true);
619 int time = 0;
620 int touchStart = 50;
621 int touchEnd = 10;
622 ScreenPoint pointOut;
623 ViewTransform viewTransformOut;
625 // Pan down
626 nsTArray<uint32_t> allowedTouchBehaviors;
627 allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
628 apzc->SetTouchActionEnabled(true);
629 ApzcPan(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
631 // Send the signal that content has handled and preventDefaulted the touch
632 // events. This flushes the event queue.
633 apzc->ContentReceivedTouch(true);
635 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
636 EXPECT_EQ(ScreenPoint(), pointOut);
637 EXPECT_EQ(ViewTransform(), viewTransformOut);
639 apzc->Destroy();
640 }
642 TEST_F(AsyncPanZoomControllerTester, Fling) {
643 TimeStamp testStartTime = TimeStamp::Now();
644 AsyncPanZoomController::SetFrameTime(testStartTime);
646 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
647 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
648 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
650 apzc->SetFrameMetrics(TestFrameMetrics());
651 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
653 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
654 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
656 int time = 0;
657 int touchStart = 50;
658 int touchEnd = 10;
659 ScreenPoint pointOut;
660 ViewTransform viewTransformOut;
662 // Fling down. Each step scroll further down
663 ApzcPan(apzc, tm, time, touchStart, touchEnd);
664 ScreenPoint lastPoint;
665 for (int i = 1; i < 50; i+=1) {
666 apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut);
667 EXPECT_GT(pointOut.y, lastPoint.y);
668 lastPoint = pointOut;
669 }
670 }
672 TEST_F(AsyncPanZoomControllerTester, OverScrollPanning) {
673 TimeStamp testStartTime = TimeStamp::Now();
674 AsyncPanZoomController::SetFrameTime(testStartTime);
676 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
677 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
678 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
680 apzc->SetFrameMetrics(TestFrameMetrics());
681 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
683 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
684 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
686 // Pan sufficiently to hit overscroll behavior
687 int time = 0;
688 int touchStart = 500;
689 int touchEnd = 10;
690 ScreenPoint pointOut;
691 ViewTransform viewTransformOut;
693 // Pan down
694 ApzcPan(apzc, tm, time, touchStart, touchEnd);
695 apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &viewTransformOut, pointOut);
696 EXPECT_EQ(ScreenPoint(0, 90), pointOut);
697 }
699 TEST_F(AsyncPanZoomControllerTester, ShortPress) {
700 nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>();
701 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
702 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
703 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
705 apzc->SetFrameMetrics(TestFrameMetrics());
706 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
707 apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
709 int time = 0;
710 nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
711 EXPECT_EQ(nsEventStatus_eIgnore, status);
713 // This verifies that the single tap notification is sent after the
714 // touchdown is fully processed. The ordering here is important.
715 mcc->CheckHasDelayedTask();
717 EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
718 mcc->RunDelayedTask();
720 apzc->Destroy();
721 }
723 TEST_F(AsyncPanZoomControllerTester, MediumPress) {
724 nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>();
725 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
726 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
727 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
729 apzc->SetFrameMetrics(TestFrameMetrics());
730 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
731 apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
733 int time = 0;
734 nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
735 EXPECT_EQ(nsEventStatus_eIgnore, status);
737 // This verifies that the single tap notification is sent after the
738 // touchdown is fully processed. The ordering here is important.
739 mcc->CheckHasDelayedTask();
741 EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
742 mcc->RunDelayedTask();
744 apzc->Destroy();
745 }
747 void
748 DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
749 nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
750 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
751 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
752 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
754 apzc->SetFrameMetrics(TestFrameMetrics());
755 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
756 apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
758 apzc->SetTouchActionEnabled(aShouldUseTouchAction);
760 int time = 0;
762 nsEventStatus status = ApzcDown(apzc, 10, 10, time);
763 EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
765 // SetAllowedTouchBehavior() must be called after sending touch-start.
766 nsTArray<uint32_t> allowedTouchBehaviors;
767 allowedTouchBehaviors.AppendElement(aBehavior);
768 apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
770 MockFunction<void(std::string checkPointName)> check;
772 {
773 InSequence s;
775 EXPECT_CALL(check, Call("preHandleLongTap"));
776 EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
777 EXPECT_CALL(check, Call("postHandleLongTap"));
779 EXPECT_CALL(check, Call("preHandleLongTapUp"));
780 EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
781 EXPECT_CALL(check, Call("postHandleLongTapUp"));
782 }
784 mcc->CheckHasDelayedTask();
786 // Manually invoke the longpress while the touch is currently down.
787 check.Call("preHandleLongTap");
788 mcc->RunDelayedTask();
789 check.Call("postHandleLongTap");
791 // Destroy pending MAX_TAP timeout task
792 mcc->DestroyOldestTask();
793 // There should be a TimeoutContentResponse task in the queue still
794 // Clear the waiting-for-content timeout task, then send the signal that
795 // content has handled this long tap. This takes the place of the
796 // "contextmenu" event.
797 mcc->CheckHasDelayedTask();
798 mcc->ClearDelayedTask();
799 apzc->ContentReceivedTouch(true);
801 time += 1000;
803 status = ApzcUp(apzc, 10, 10, time);
804 EXPECT_EQ(nsEventStatus_eIgnore, status);
806 // To get a LongTapUp event, we must kick APZC to flush its event queue. This
807 // would normally happen if we had a (Tab|RenderFrame)(Parent|Child)
808 // mechanism.
809 check.Call("preHandleLongTapUp");
810 apzc->ContentReceivedTouch(false);
811 check.Call("postHandleLongTapUp");
813 apzc->Destroy();
814 }
816 void
817 DoLongPressPreventDefaultTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
818 // We have to initialize both an integer time and TimeStamp time because
819 // TimeStamp doesn't have any ToXXX() functions for converting back to
820 // primitives.
821 TimeStamp testStartTime = TimeStamp::Now();
822 int time = 0;
823 AsyncPanZoomController::SetFrameTime(testStartTime);
825 nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
826 nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
827 nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
828 0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
830 apzc->SetFrameMetrics(TestFrameMetrics());
831 apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
832 apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
834 apzc->SetTouchActionEnabled(aShouldUseTouchAction);
836 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
837 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
839 int touchX = 10,
840 touchStartY = 10,
841 touchEndY = 50;
843 nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
844 EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
846 // SetAllowedTouchBehavior() must be called after sending touch-start.
847 nsTArray<uint32_t> allowedTouchBehaviors;
848 allowedTouchBehaviors.AppendElement(aBehavior);
849 apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
851 MockFunction<void(std::string checkPointName)> check;
853 {
854 InSequence s;
856 EXPECT_CALL(check, Call("preHandleLongTap"));
857 EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid())).Times(1);
858 EXPECT_CALL(check, Call("postHandleLongTap"));
859 }
861 mcc->CheckHasDelayedTask();
863 // Manually invoke the longpress while the touch is currently down.
864 check.Call("preHandleLongTap");
865 mcc->RunDelayedTask();
866 check.Call("postHandleLongTap");
868 // Destroy pending MAX_TAP timeout task
869 mcc->DestroyOldestTask();
870 // Clear the waiting-for-content timeout task, then send the signal that
871 // content has handled this long tap. This takes the place of the
872 // "contextmenu" event.
873 mcc->ClearDelayedTask();
874 apzc->ContentReceivedTouch(true);
876 time += 1000;
878 MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, 0);
879 mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
880 status = apzc->ReceiveInputEvent(mti);
881 EXPECT_EQ(nsEventStatus_eIgnore, status);
883 EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(1);
884 status = ApzcUp(apzc, touchX, touchEndY, time);
885 EXPECT_EQ(nsEventStatus_eIgnore, status);
887 // Flush the event queue. Once the "contextmenu" event is handled, any touch
888 // events that come from the same series of start->n*move->end events should
889 // be discarded, even if only the "contextmenu" event is preventDefaulted.
890 apzc->ContentReceivedTouch(false);
892 ScreenPoint pointOut;
893 ViewTransform viewTransformOut;
894 apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
896 EXPECT_EQ(ScreenPoint(), pointOut);
897 EXPECT_EQ(ViewTransform(), viewTransformOut);
899 apzc->Destroy();
900 }
902 TEST_F(AsyncPanZoomControllerTester, LongPress) {
903 DoLongPressTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
904 }
906 TEST_F(AsyncPanZoomControllerTester, LongPressWithTouchAction) {
907 DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
908 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
909 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
910 }
912 TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
913 DoLongPressPreventDefaultTest(false, mozilla::layers::AllowedTouchBehavior::NONE);
914 }
916 TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefaultWithTouchAction) {
917 DoLongPressPreventDefaultTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
918 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
919 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
920 }
922 // Layer tree for HitTesting1
923 static already_AddRefed<mozilla::layers::Layer>
924 CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
925 const char* layerTreeSyntax = "c(ttcc)";
926 // LayerID 0 1234
927 nsIntRegion layerVisibleRegion[] = {
928 nsIntRegion(nsIntRect(0,0,100,100)),
929 nsIntRegion(nsIntRect(0,0,100,100)),
930 nsIntRegion(nsIntRect(10,10,20,20)),
931 nsIntRegion(nsIntRect(10,10,20,20)),
932 nsIntRegion(nsIntRect(5,5,20,20)),
933 };
934 gfx3DMatrix transforms[] = {
935 gfx3DMatrix(),
936 gfx3DMatrix(),
937 gfx3DMatrix(),
938 gfx3DMatrix(),
939 gfx3DMatrix(),
940 };
941 return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers);
942 }
944 // Layer Tree for HitTesting2
945 static already_AddRefed<mozilla::layers::Layer>
946 CreateTestLayerTree2(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
947 const char* layerTreeSyntax = "c(cc(c))";
948 // LayerID 0 12 3
949 nsIntRegion layerVisibleRegion[] = {
950 nsIntRegion(nsIntRect(0,0,100,100)),
951 nsIntRegion(nsIntRect(10,10,40,40)),
952 nsIntRegion(nsIntRect(10,60,40,40)),
953 nsIntRegion(nsIntRect(10,60,40,40)),
954 };
955 gfx3DMatrix transforms[] = {
956 gfx3DMatrix(),
957 gfx3DMatrix(),
958 gfx3DMatrix(),
959 gfx3DMatrix(),
960 };
961 return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers);
962 }
964 static void
965 SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
966 // The scrollable rect is only used in HitTesting2,
967 // HitTesting1 doesn't care about it.
968 CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1))
969 {
970 ContainerLayer* container = aLayer->AsContainerLayer();
971 FrameMetrics metrics;
972 metrics.SetScrollId(aScrollId);
973 nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
974 metrics.mCompositionBounds = ParentLayerIntRect(layerBound.x, layerBound.y,
975 layerBound.width, layerBound.height);
976 metrics.mScrollableRect = aScrollableRect;
977 metrics.SetScrollOffset(CSSPoint(0, 0));
978 container->SetFrameMetrics(metrics);
979 }
981 static already_AddRefed<AsyncPanZoomController>
982 GetTargetAPZC(APZCTreeManager* manager, const ScreenPoint& aPoint,
983 gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToGeckoOut)
984 {
985 nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint);
986 if (hit) {
987 manager->GetInputTransforms(hit.get(), aTransformToApzcOut, aTransformToGeckoOut);
988 }
989 return hit.forget();
990 }
992 // A simple hit testing test that doesn't involve any transforms on layers.
993 TEST_F(APZCTreeManagerTester, HitTesting1) {
994 nsTArray<nsRefPtr<Layer> > layers;
995 nsRefPtr<LayerManager> lm;
996 nsRefPtr<Layer> root = CreateTestLayerTree1(lm, layers);
998 TimeStamp testStartTime = TimeStamp::Now();
999 AsyncPanZoomController::SetFrameTime(testStartTime);
1000 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
1001 ScopedLayerTreeRegistration controller(0, root, mcc);
1003 nsRefPtr<APZCTreeManager> manager = new TestAPZCTreeManager();
1004 gfx3DMatrix transformToApzc;
1005 gfx3DMatrix transformToGecko;
1007 // No APZC attached so hit testing will return no APZC at (20,20)
1008 nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(manager, ScreenPoint(20, 20), transformToApzc, transformToGecko);
1009 AsyncPanZoomController* nullAPZC = nullptr;
1010 EXPECT_EQ(nullAPZC, hit.get());
1011 EXPECT_EQ(gfx3DMatrix(), transformToApzc);
1012 EXPECT_EQ(gfx3DMatrix(), transformToGecko);
1014 // Now we have a root APZC that will match the page
1015 SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
1016 manager->UpdatePanZoomControllerTree(nullptr, root, false, 0);
1017 hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko);
1018 EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
1019 // expect hit point at LayerIntPoint(15, 15)
1020 EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15)));
1021 EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15)));
1023 // Now we have a sub APZC with a better fit
1024 SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
1025 manager->UpdatePanZoomControllerTree(nullptr, root, false, 0);
1026 EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController());
1027 hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko);
1028 EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
1029 // expect hit point at LayerIntPoint(15, 15)
1030 EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15)));
1031 EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15)));
1033 // Now test hit testing when we have two scrollable layers
1034 hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko);
1035 EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
1036 SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
1037 manager->UpdatePanZoomControllerTree(nullptr, root, false, 0);
1038 hit = GetTargetAPZC(manager, ScreenPoint(15, 15), transformToApzc, transformToGecko);
1039 EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
1040 // expect hit point at LayerIntPoint(15, 15)
1041 EXPECT_EQ(gfxPoint(15, 15), transformToApzc.Transform(gfxPoint(15, 15)));
1042 EXPECT_EQ(gfxPoint(15, 15), transformToGecko.Transform(gfxPoint(15, 15)));
1044 // Hit test ouside the reach of layer[3,4] but inside root
1045 hit = GetTargetAPZC(manager, ScreenPoint(90, 90), transformToApzc, transformToGecko);
1046 EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
1047 // expect hit point at LayerIntPoint(90, 90)
1048 EXPECT_EQ(gfxPoint(90, 90), transformToApzc.Transform(gfxPoint(90, 90)));
1049 EXPECT_EQ(gfxPoint(90, 90), transformToGecko.Transform(gfxPoint(90, 90)));
1051 // Hit test ouside the reach of any layer
1052 hit = GetTargetAPZC(manager, ScreenPoint(1000, 10), transformToApzc, transformToGecko);
1053 EXPECT_EQ(nullAPZC, hit.get());
1054 EXPECT_EQ(gfx3DMatrix(), transformToApzc);
1055 EXPECT_EQ(gfx3DMatrix(), transformToGecko);
1056 hit = GetTargetAPZC(manager, ScreenPoint(-1000, 10), transformToApzc, transformToGecko);
1057 EXPECT_EQ(nullAPZC, hit.get());
1058 EXPECT_EQ(gfx3DMatrix(), transformToApzc);
1059 EXPECT_EQ(gfx3DMatrix(), transformToGecko);
1061 manager->ClearTree();
1062 }
1064 // A more involved hit testing test that involves css and async transforms.
1065 TEST_F(APZCTreeManagerTester, HitTesting2) {
1066 nsTArray<nsRefPtr<Layer> > layers;
1067 nsRefPtr<LayerManager> lm;
1068 nsRefPtr<Layer> root = CreateTestLayerTree2(lm, layers);
1070 TimeStamp testStartTime = TimeStamp::Now();
1071 AsyncPanZoomController::SetFrameTime(testStartTime);
1072 nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
1073 ScopedLayerTreeRegistration controller(0, root, mcc);
1075 nsRefPtr<TestAPZCTreeManager> manager = new TestAPZCTreeManager();
1076 nsRefPtr<AsyncPanZoomController> hit;
1077 gfx3DMatrix transformToApzc;
1078 gfx3DMatrix transformToGecko;
1080 // Set a CSS transform on one of the layers.
1081 Matrix4x4 transform;
1082 transform = transform * Matrix4x4().Scale(2, 1, 1);
1083 layers[2]->SetBaseTransform(transform);
1085 // Make some other layers scrollable.
1086 SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
1087 SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
1088 SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
1090 manager->UpdatePanZoomControllerTree(nullptr, root, false, 0);
1092 // At this point, the following holds (all coordinates in screen pixels):
1093 // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
1094 // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
1095 // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
1096 // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
1098 AsyncPanZoomController* apzcroot = root->AsContainerLayer()->GetAsyncPanZoomController();
1099 AsyncPanZoomController* apzc1 = layers[1]->AsContainerLayer()->GetAsyncPanZoomController();
1100 AsyncPanZoomController* apzc3 = layers[3]->AsContainerLayer()->GetAsyncPanZoomController();
1102 // Hit an area that's clearly on the root layer but not any of the child layers.
1103 hit = GetTargetAPZC(manager, ScreenPoint(75, 25), transformToApzc, transformToGecko);
1104 EXPECT_EQ(apzcroot, hit.get());
1105 EXPECT_EQ(gfxPoint(75, 25), transformToApzc.Transform(gfxPoint(75, 25)));
1106 EXPECT_EQ(gfxPoint(75, 25), transformToGecko.Transform(gfxPoint(75, 25)));
1108 // Hit an area on the root that would be on layers[3] if layers[2]
1109 // weren't transformed.
1110 // Note that if layers[2] were scrollable, then this would hit layers[2]
1111 // because its composition bounds would be at (10,60)-(50,100) (and the
1112 // scale-only transform that we set on layers[2] would be invalid because
1113 // it would place the layer into overscroll, as its composition bounds
1114 // start at x=10 but its content at x=20).
1115 hit = GetTargetAPZC(manager, ScreenPoint(15, 75), transformToApzc, transformToGecko);
1116 EXPECT_EQ(apzcroot, hit.get());
1117 EXPECT_EQ(gfxPoint(15, 75), transformToApzc.Transform(gfxPoint(15, 75)));
1118 EXPECT_EQ(gfxPoint(15, 75), transformToGecko.Transform(gfxPoint(15, 75)));
1120 // Hit an area on layers[1].
1121 hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko);
1122 EXPECT_EQ(apzc1, hit.get());
1123 EXPECT_EQ(gfxPoint(25, 25), transformToApzc.Transform(gfxPoint(25, 25)));
1124 EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(25, 25)));
1126 // Hit an area on layers[3].
1127 hit = GetTargetAPZC(manager, ScreenPoint(25, 75), transformToApzc, transformToGecko);
1128 EXPECT_EQ(apzc3, hit.get());
1129 // transformToApzc should unapply layers[2]'s transform
1130 EXPECT_EQ(gfxPoint(12.5, 75), transformToApzc.Transform(gfxPoint(25, 75)));
1131 // and transformToGecko should reapply it
1132 EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(12.5, 75)));
1134 // Hit an area on layers[3] that would be on the root if layers[2]
1135 // weren't transformed.
1136 hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
1137 EXPECT_EQ(apzc3, hit.get());
1138 // transformToApzc should unapply layers[2]'s transform
1139 EXPECT_EQ(gfxPoint(37.5, 75), transformToApzc.Transform(gfxPoint(75, 75)));
1140 // and transformToGecko should reapply it
1141 EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(37.5, 75)));
1143 // Pan the root layer upward by 50 pixels.
1144 // This causes layers[1] to scroll out of view, and an async transform
1145 // of -50 to be set on the root layer.
1146 int time = 0;
1147 // Silence GMock warnings about "uninteresting mock function calls".
1148 EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(AtLeast(1));
1149 EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
1150 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
1152 // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
1153 // Since this paint request is in the queue to Gecko, transformToGecko will
1154 // take it into account.
1155 ApzcPan(apzcroot, manager, time, 100, 50);
1157 // Hit where layers[3] used to be. It should now hit the root.
1158 hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
1159 EXPECT_EQ(apzcroot, hit.get());
1160 // transformToApzc doesn't unapply the root's own async transform
1161 EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
1162 // and transformToGecko unapplies it and then reapplies it, because by the
1163 // time the event being transformed reaches Gecko the new paint request will
1164 // have been handled.
1165 EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(75, 75)));
1167 // Hit where layers[1] used to be and where layers[3] should now be.
1168 hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko);
1169 EXPECT_EQ(apzc3, hit.get());
1170 // transformToApzc unapplies both layers[2]'s css transform and the root's
1171 // async transform
1172 EXPECT_EQ(gfxPoint(12.5, 75), transformToApzc.Transform(gfxPoint(25, 25)));
1173 // transformToGecko reapplies both the css transform and the async transform
1174 // because we have already issued a paint request with it.
1175 EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(12.5, 75)));
1177 // This second pan will move the APZC by another 50 pixels but since the paint
1178 // request dispatched above has not "completed", we will not dispatch another
1179 // one yet. Now we have an async transform on top of the pending paint request
1180 // transform.
1181 ApzcPan(apzcroot, manager, time, 100, 50);
1183 // Hit where layers[3] used to be. It should now hit the root.
1184 hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
1185 EXPECT_EQ(apzcroot, hit.get());
1186 // transformToApzc doesn't unapply the root's own async transform
1187 EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
1188 // transformToGecko unapplies the full async transform of -100 pixels, and then
1189 // reapplies the "D" transform of -50 leading to an overall adjustment of +50
1190 EXPECT_EQ(gfxPoint(75, 125), transformToGecko.Transform(gfxPoint(75, 75)));
1192 // Hit where layers[1] used to be. It should now hit the root.
1193 hit = GetTargetAPZC(manager, ScreenPoint(25, 25), transformToApzc, transformToGecko);
1194 EXPECT_EQ(apzcroot, hit.get());
1195 // transformToApzc doesn't unapply the root's own async transform
1196 EXPECT_EQ(gfxPoint(25, 25), transformToApzc.Transform(gfxPoint(25, 25)));
1197 // transformToGecko unapplies the full async transform of -100 pixels, and then
1198 // reapplies the "D" transform of -50 leading to an overall adjustment of +50
1199 EXPECT_EQ(gfxPoint(25, 75), transformToGecko.Transform(gfxPoint(25, 25)));
1201 manager->ClearTree();
1202 }