gfx/layers/apz/test/mochitest/helper_hittest_spam.html
author James Teh <jteh@mozilla.com>
Wed, 01 Feb 2023 05:02:01 +0000
changeset 651150 dd0fdd1daa69783be36acd5c50544f3694eaa8f9
parent 576266 82cf330340dc0e9703b5774c3af1f9ffbf394de3
permissions -rw-r--r--
Bug 1813980: Check IsDoc before Parent in RemoteAccessibleBase::ApplyCrossDocOffset. r=morgan We call this function on every ancestor when calculating bounds. RemoteParent() currently requires a hash lookup, so it's more efficient to early return for !IsDoc() first. This is a micro-optimisation, but it might have some impact given that we call this on every ancestor, especially when hit testing, where we call Bounds() a lot. As a bit of drive-by cleanup, use RemoteParent() rather than calling Parent() and IsRemote/AsRemote(). Differential Revision: https://phabricator.services.mozilla.com/D168346

<!DOCTYPE HTML>
<html>
<head>
  <title>Test doing lots of hit-testing on a rapidly changing page</title>
  <script type="application/javascript" src="apz_test_utils.js"></script>
  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
  <script src="/tests/SimpleTest/paint_listener.js"></script>
  <meta name="viewport" content="width=device-width"/>
</head>
<style>
#spamdiv {
    overflow: scroll;
    width: 400px;
    height: 400px;
}
#spamdiv div {
    width: 1000px;
    height: 1000px;
}
</style>
<body>
<script type="application/javascript">

var SPAM_LIMIT = 200; // bigger numbers make the test run longer

// This function adds and removes a scrollable div very rapidly (via
// setTimeout(0) self-scheduling). This causes very frequent layer
// transactions with a new APZ hit-testing tree from the main thread to APZ.
// The div is created afresh every time so that the scroll identifier in
// Gecko is continually increasing, and hit results from a stale tree will
// not be valid on the new tree.
var spamCount = 0;
var spamPoint = null;
function divSpammer() {
  spamCount++;
  if (spamCount >= SPAM_LIMIT) {
    return;
  }
  setTimeout(divSpammer, 0);

  // Remove the div if it exists...
  var spamdiv = document.getElementById('spamdiv');
  if (spamdiv) {
    spamdiv.remove();
    return;
  }
  // ... and add it if it doesn't exist.
  spamdiv = document.createElement('div');
  spamdiv.id = 'spamdiv';
  spamdiv.appendChild(document.createElement('div'));
  document.body.appendChild(spamdiv);
  if (spamPoint == null) {
    spamPoint = centerOf(spamdiv);
  }
}

// This function does continuous hit-testing by scheduling itself over and
// over with setTimeout(0). It hit-tests the same spot and expects to hit
// either the root scrollframe (if the spamdiv is not present in that
// instant) or the spamdiv (if it is present). If the spamdiv is hit, it
// expects the scrollid to be non-decreasing.
var rootScrollId = null;
var lastScrollId = -1;
function hitTestSpammer() {
  if (spamCount >= SPAM_LIMIT) {
    subtestDone();
    return;
  }
  setTimeout(hitTestSpammer, 0);

  if (spamPoint == null) {
    // The very first invocation of this function will have spamPoint as null,
    // and we use that to pick up the rootScrollId.
    ok(rootScrollId == null, "This codepath shouldn't get hit twice");
    rootScrollId = hitTest(centerOf(document.body)).scrollId;
    ok(true, "Root scroll id detected as " + rootScrollId);
    return;
  }

  var scrollId = hitTest(spamPoint).scrollId;
  if (scrollId == rootScrollId) {
    ok(true, "Hit test hit the root scroller, spamdiv is not in compositor");
  } else {
    is(scrollId >= lastScrollId, true, "spamdiv's scroll id is now " + scrollId);
    lastScrollId = scrollId;
  }
}

function startTest() {
  // Make sure to run hitTestSpammer first so the first iteration is while
  // spamPoint is still null.
  setTimeout(hitTestSpammer, 0);
  setTimeout(divSpammer, 0);
}

waitUntilApzStable().then(startTest);

</script>
</body>
</html>