TEST_F(APZCBasicTester, FlingIntoOverscroll) { // Enable overscrolling. SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); // Scroll down by 25 px. Don't fling for simplicity. ApzcPanNoFling(apzc, 50, 25); // Now scroll back up by 20px, this time flinging after. // The fling should cover the remaining 5 px of room to scroll, then // go into overscroll, and finally snap-back to recover from overscroll. Pan(apzc, 25, 45); const TimeDuration increment = TimeDuration::FromMilliseconds(1); bool reachedOverscroll = false; bool recoveredFromOverscroll = false; while (apzc->AdvanceAnimations(mcc->Time())) { if (!reachedOverscroll && apzc->IsOverscrolled()) { reachedOverscroll = true; } if (reachedOverscroll && !apzc->IsOverscrolled()) { recoveredFromOverscroll = true; } mcc->AdvanceBy(increment); } EXPECT_TRUE(reachedOverscroll); EXPECT_TRUE(recoveredFromOverscroll); }
TEST_F(APZEventRegionsTester, Obscuration) { CreateObscuringLayerTree(); ScopedLayerTreeRegistration registration(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); TestAsyncPanZoomController* parent = ApzcOf(layers[1]); TestAsyncPanZoomController* child = ApzcOf(layers[2]); ApzcPanNoFling(parent, mcc, 75, 25); HitTestResult result; RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result); EXPECT_EQ(child, hit.get()); EXPECT_EQ(HitTestResult::HitLayer, result); }
TEST_F(APZCBasicTester, PanningTransformNotifications) { SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); // Scroll down by 25 px. Ensure we only get one set of // state change notifications. // // Then, scroll back up by 20px, this time flinging after. // The fling should cover the remaining 5 px of room to scroll, then // go into overscroll, and finally snap-back to recover from overscroll. // Again, ensure we only get one set of state change notifications for // this entire procedure. MockFunction<void(std::string checkPointName)> check; { InSequence s; EXPECT_CALL(check, Call("Simple pan")); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1); EXPECT_CALL(check, Call("Complex pan")); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1); EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1); EXPECT_CALL(check, Call("Done")); } check.Call("Simple pan"); ApzcPanNoFling(apzc, 50, 25); check.Call("Complex pan"); Pan(apzc, 25, 45); apzc->AdvanceAnimationsUntilEnd(); check.Call("Done"); }
TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) { SCOPED_GFX_PREF(TouchActionEnabled, bool, false); // The main purpose of this test is to verify that touch-start events (or anything // that starts a new input block) don't ever get untransformed. This should always // hold because the APZ code should flush repaints when we start a new input block // and the transform to gecko space should be empty. CreateSimpleScrollingLayer(); ScopedLayerTreeRegistration registration(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); TestAsyncPanZoomController* apzcroot = ApzcOf(root); // At this point, the following holds (all coordinates in screen pixels): // layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200) MockFunction<void(std::string checkPointName)> check; { InSequence s; EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); EXPECT_CALL(check, Call("post-first-touch-start")); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); EXPECT_CALL(check, Call("post-second-fling")); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); EXPECT_CALL(check, Call("post-second-touch-start")); } // This first pan will move the APZC by 50 pixels, and dispatch a paint request. ApzcPanNoFling(apzcroot, 100, 50); // Verify that a touch start doesn't get untransformed ScreenIntPoint touchPoint(50, 50); MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0)); EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr)); EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); check.Call("post-first-touch-start"); // Send a touchend to clear state mti.mType = MultiTouchInput::MULTITOUCH_END; manager->ReceiveInputEvent(mti, nullptr, nullptr); mcc->AdvanceByMillis(1000); // Now do two pans. The first of these will dispatch a repaint request, as above. // The second will get stuck in the paint throttler because the first one doesn't // get marked as "completed", so this will result in a non-empty LD transform. // (Note that any outstanding repaint requests from the first half of this test // don't impact this half because we advance the time by 1 second, which will trigger // the max-wait-exceeded codepath in the paint throttler). ApzcPanNoFling(apzcroot, 100, 50); check.Call("post-second-fling"); ApzcPanNoFling(apzcroot, 100, 50); // Ensure that a touch start again doesn't get untransformed by flushing // a repaint mti.mType = MultiTouchInput::MULTITOUCH_START; EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr)); EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); check.Call("post-second-touch-start"); mti.mType = MultiTouchInput::MULTITOUCH_END; EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr)); EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); }
// A more involved hit testing test that involves css and async transforms. TEST_F(APZHitTestingTester, HitTesting2) { SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests CreateHitTesting2LayerTree(); ScopedLayerTreeRegistration registration(manager, 0, root, mcc); manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); // At this point, the following holds (all coordinates in screen pixels): // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100) // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50) // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100) TestAsyncPanZoomController* apzcroot = ApzcOf(root); TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]); TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]); // Hit an area that's clearly on the root layer but not any of the child layers. RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25)); EXPECT_EQ(apzcroot, hit.get()); EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc * ScreenPoint(75, 25)); EXPECT_EQ(ScreenPoint(75, 25), transformToGecko * ParentLayerPoint(75, 25)); // Hit an area on the root that would be on layers[3] if layers[2] // weren't transformed. // Note that if layers[2] were scrollable, then this would hit layers[2] // because its composition bounds would be at (10,60)-(50,100) (and the // scale-only transform that we set on layers[2] would be invalid because // it would place the layer into overscroll, as its composition bounds // start at x=10 but its content at x=20). hit = GetTargetAPZC(ScreenPoint(15, 75)); EXPECT_EQ(apzcroot, hit.get()); EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc * ScreenPoint(15, 75)); EXPECT_EQ(ScreenPoint(15, 75), transformToGecko * ParentLayerPoint(15, 75)); // Hit an area on layers[1]. hit = GetTargetAPZC(ScreenPoint(25, 25)); EXPECT_EQ(apzc1, hit.get()); EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25)); EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25)); // Hit an area on layers[3]. hit = GetTargetAPZC(ScreenPoint(25, 75)); EXPECT_EQ(apzc3, hit.get()); // transformToApzc should unapply layers[2]'s transform EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc * ScreenPoint(25, 75)); // and transformToGecko should reapply it EXPECT_EQ(ScreenPoint(25, 75), transformToGecko * ParentLayerPoint(12.5, 75)); // Hit an area on layers[3] that would be on the root if layers[2] // weren't transformed. hit = GetTargetAPZC(ScreenPoint(75, 75)); EXPECT_EQ(apzc3, hit.get()); // transformToApzc should unapply layers[2]'s transform EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc * ScreenPoint(75, 75)); // and transformToGecko should reapply it EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(37.5, 75)); // Pan the root layer upward by 50 pixels. // This causes layers[1] to scroll out of view, and an async transform // of -50 to be set on the root layer. EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); // This first pan will move the APZC by 50 pixels, and dispatch a paint request. // Since this paint request is in the queue to Gecko, transformToGecko will // take it into account. ApzcPanNoFling(apzcroot, 100, 50); // Hit where layers[3] used to be. It should now hit the root. hit = GetTargetAPZC(ScreenPoint(75, 75)); EXPECT_EQ(apzcroot, hit.get()); // transformToApzc doesn't unapply the root's own async transform EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75)); // and transformToGecko unapplies it and then reapplies it, because by the // time the event being transformed reaches Gecko the new paint request will // have been handled. EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75)); // Hit where layers[1] used to be and where layers[3] should now be. hit = GetTargetAPZC(ScreenPoint(25, 25)); EXPECT_EQ(apzc3, hit.get()); // transformToApzc unapplies both layers[2]'s css transform and the root's // async transform EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc * ScreenPoint(25, 25)); // transformToGecko reapplies both the css transform and the async transform // because we have already issued a paint request with it. EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(12.5, 75)); // This second pan will move the APZC by another 50 pixels. EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); ApzcPanNoFling(apzcroot, 100, 50); // Hit where layers[3] used to be. It should now hit the root. hit = GetTargetAPZC(ScreenPoint(75, 75)); EXPECT_EQ(apzcroot, hit.get()); // transformToApzc doesn't unapply the root's own async transform EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75)); // transformToGecko unapplies the full async transform of -100 pixels EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75)); // Hit where layers[1] used to be. It should now hit the root. hit = GetTargetAPZC(ScreenPoint(25, 25)); EXPECT_EQ(apzcroot, hit.get()); // transformToApzc doesn't unapply the root's own async transform EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25)); // transformToGecko unapplies the full async transform of -100 pixels EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25)); }