Bug 1383365 - Add a test to assert async key scrolling happens. r=botond
MozReview-Commit-ID: 13XydDOHXUE
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -847,16 +847,18 @@ APZCTreeManager::PrepareNodeForLayer(con
}
if (aMetrics.IsRootContent()) {
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
"isRootContent", true);
}
// Note that the async scroll offset is in ParentLayer pixels
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "asyncScrollOffset",
apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForHitTesting));
+ aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "hasAsyncKeyScrolled",
+ apzc->TestHasAsyncKeyScrolled());
}
if (newApzc) {
auto it = mZoomConstraints.find(guid);
if (it != mZoomConstraints.end()) {
// We have a zoomconstraints for this guid, apply it.
apzc->UpdateZoomConstraints(it->second);
} else if (!apzc->HasNoParentWithSameLayersId()) {
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -732,16 +732,17 @@ AsyncPanZoomController::AsyncPanZoomCont
mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
mState(NOTHING),
mNotificationBlockers(0),
mInputQueue(aInputQueue),
mPinchPaintTimerSet(false),
mAPZCId(sAsyncPanZoomControllerCount++),
mSharedLock(nullptr),
mAsyncTransformAppliedToContent(false),
+ mTestHasAsyncKeyScrolled(false),
mCheckerboardEventLock("APZCBELock")
{
if (aGestures == USE_GESTURE_DETECTOR) {
mGestureEventListener = new GestureEventListener(this);
}
}
AsyncPanZoomController::~AsyncPanZoomController()
@@ -1698,16 +1699,19 @@ void ReportKeyboardScrollAction(const Ke
}
nsEventStatus
AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent)
{
// Report the type of scroll action to telemetry
ReportKeyboardScrollAction(aEvent.mAction);
+ // Mark that this APZC has async key scrolled
+ mTestHasAsyncKeyScrolled = true;
+
// Calculate the destination for this keyboard scroll action
CSSPoint destination = GetKeyboardDestination(aEvent.mAction);
nsIScrollableFrame::ScrollUnit scrollUnit = KeyboardScrollAction::GetScrollUnit(aEvent.mAction.mType);
// The lock must be held across the entire update operation, so the
// compositor doesn't end the animation before we get a chance to
// update it.
ReentrantMonitorAutoEnter lock(mMonitor);
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1208,16 +1208,24 @@ private:
/* ===================================================================
* The functions and members in this section are used for testing
* and assertion purposes only.
*/
public:
/**
+ * Gets whether this APZC has performed async key scrolling.
+ */
+ bool TestHasAsyncKeyScrolled() const
+ {
+ return mTestHasAsyncKeyScrolled;
+ }
+
+ /**
* Set an extra offset for testing async scrolling.
*/
void SetTestAsyncScrollOffset(const CSSPoint& aPoint)
{
mTestAsyncScrollOffset = aPoint;
}
/**
* Set an extra offset for testing async scrolling.
@@ -1244,18 +1252,19 @@ public:
private:
// Extra offset to add to the async scroll position for testing
CSSPoint mTestAsyncScrollOffset;
// Extra zoom to include in the aync zoom for testing
LayerToParentLayerScale mTestAsyncZoom;
// Flag to track whether or not the APZ transform is not used. This
// flag is recomputed for every composition frame.
- bool mAsyncTransformAppliedToContent;
-
+ bool mAsyncTransformAppliedToContent : 1;
+ // Flag to track whether or not this APZC has ever async key scrolled.
+ bool mTestHasAsyncKeyScrolled : 1;
/* ===================================================================
* The functions and members in this section are used for checkerboard
* recording.
*/
private:
// Helper function to update the in-progress checkerboard event, if any.
void UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
--- a/gfx/layers/apz/test/mochitest/apz_test_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -270,16 +270,20 @@ function isApzEnabled() {
// All tests are required to have at least one assertion. Since APZ is
// disabled, and the main test is presumably not going to run, we stick in
// a dummy assertion here to keep the test passing.
SimpleTest.ok(true, "APZ is not enabled; this test will be skipped");
}
return enabled;
}
+function isKeyApzEnabled() {
+ return isApzEnabled() && SpecialPowers.getBoolPref("apz.keyboard.enabled");
+}
+
// Despite what this function name says, this does not *directly* run the
// provided continuation testFunction. Instead, it returns a function that
// can be used to run the continuation. The extra level of indirection allows
// it to be more easily added to a promise chain, like so:
// waitUntilApzStable().then(runContinuation(myTest));
//
// If you want to run the continuation directly, outside of a promise chain,
// you can invoke the return value of this function, like so:
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_key_scroll.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1383365
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Async key scrolling test, helper page</title>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript">
+ var SimpleTest = window.opener.SimpleTest;
+
+ SimpleTest.waitForFocus(runTests, window);
+
+ // --------------------------------------------------------------------
+ // Async key scrolling test
+ //
+ // This test checks that a key scroll occurs asynchronously.
+ //
+ // The page contains a <div> that is large enough to make the page
+ // scrollable. We first synthesize a page down to scroll to the bottom
+ // of the page. Once we have reached the bottom of the page, we synthesize
+ // a page up to get us back to the top of the page.
+ //
+ // Once at the top, we request test data from APZ, rebuild the APZC tree
+ // structure, and use it to check that async key scrolling happened.
+ // --------------------------------------------------------------------
+
+ function runTests() {
+ // Sanity check
+ SimpleTest.is(checkHasAsyncKeyScrolled(false), false, "expected no async key scrolling before test");
+
+ // Send a key to initiate a page scroll to take us to the bottom of the
+ // page. This scroll is done synchronously because APZ doesn't have
+ // current focus state at page load.
+ window.addEventListener("scroll", waitForScrollBottom);
+ window.synthesizeKey("VK_END", {});
+ };
+
+ function waitForScrollBottom() {
+ if (window.scrollY < window.scrollYMax) {
+ return;
+ }
+ window.removeEventListener("scroll", waitForScrollBottom);
+
+ // Wait for scrolling to finish before dispatching the next key input or
+ // the default action won't occur.
+ waitForApzFlushedRepaints(function () {
+ // This scroll should be asynchronous now that the focus state is up to date.
+ window.addEventListener("scroll", waitForScrollTop);
+ window.synthesizeKey("VK_HOME", {});
+ });
+ };
+
+ function waitForScrollTop() {
+ if (window.scrollY > 0) {
+ return;
+ }
+ window.removeEventListener("scroll", waitForScrollTop);
+
+ // Wait for APZ to settle and then check that async scrolling happened.
+ waitForApzFlushedRepaints(function () {
+ SimpleTest.is(checkHasAsyncKeyScrolled(true), true, "expected async key scrolling after test");
+ window.opener.finishTest();
+ });
+ };
+
+ function checkHasAsyncKeyScrolled(requirePaints) {
+ // Get the compositor-side test data from nsIDOMWindowUtils.
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var compositorTestData = utils.getCompositorAPZTestData();
+
+ if (requirePaints) {
+ SimpleTest.ok(compositorTestData.paints.length > 0,
+ "expected at least one paint in compositor test data");
+ }
+
+ // Get the sequence number of the last paint on the compositor side.
+ // We do this before converting the APZ test data because the conversion
+ // loses the order of the paints.
+ var lastPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
+ var lastPaintSeqNo = lastPaint.sequenceNumber;
+
+ // Convert the test data into a representation that's easier to navigate.
+ compositorTestData = convertTestData(compositorTestData);
+
+ // Reconstruct the APZC tree structure in the last paint.
+ var apzcTree = buildApzcTree(compositorTestData.paints[lastPaintSeqNo]);
+ var rcd = findRcdNode(apzcTree);
+
+ if (rcd) {
+ return rcd.hasAsyncKeyScrolled === "1";
+ } else {
+ SimpleTest.info("Last paint rcd is null");
+ return false;
+ }
+ }
+ </script>
+</head>
+<body style="height: 500px; overflow: scroll">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1383365">Async key scrolling test</a>
+ <!-- Put enough content into the page to make it have a nonzero scroll range -->
+ <div style="height: 5000px"></div>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -13,16 +13,17 @@
helper_bug1346632.html
helper_click.html
helper_div_pan.html
helper_drag_click.html
helper_drag_scroll.html
helper_iframe_pan.html
helper_iframe1.html
helper_iframe2.html
+ helper_key_scroll.html
helper_long_tap.html
helper_scroll_inactive_perspective.html
helper_scroll_inactive_zindex.html
helper_scroll_on_position_fixed.html
helper_scroll_over_scrollbar.html
helper_scrollto_tap.html
helper_subframe_style.css
helper_tall.html
@@ -48,16 +49,17 @@
skip-if = (toolkit == 'android') # mouse events not supported on mobile
[test_group_pointerevents.html]
[test_group_touchevents.html]
[test_group_wheelevents.html]
skip-if = (toolkit == 'android') # wheel events not supported on mobile
[test_group_zoom.html]
skip-if = (toolkit != 'android') # only android supports zoom
[test_interrupted_reflow.html]
+[test_key_scroll.html]
[test_layerization.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_inactive_bug1190112.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_inactive_flattened_frame.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_subframe_scrollbar.html]
skip-if = (os == 'android') # wheel events not supported on mobile
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_key_scroll.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1383365
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Async key scrolling test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ if (isKeyApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+
+ // Run the actual test in its own window, because it requires that the
+ // root APZC be scrollable. Mochitest pages themselves often run
+ // inside an iframe which means we have no control over the root APZC.
+ var w = null;
+ window.onload = function() {
+ pushPrefs([["apz.test.logging_enabled", true],
+ ["test.events.async.enabled", true]]).then(function() {
+ w = window.open("helper_key_scroll.html", "_blank");
+ });
+ };
+ } else {
+ SimpleTest.ok(true, "Keyboard APZ is disabled");
+ }
+
+ function finishTest() {
+ w.close();
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1383365">Async key scrolling test</a>
+</body>
+</html>