chromium/third_party/blink/web_tests/external/wpt/css/cssom-view/support/scroll-behavior.js

// TODO(crbug.com/888443): It would be better to listen to the scrollend event
// instead of polling the scroll position.
function observeScrolling(elements, callback) {
  if (!Array.isArray(elements))
      elements = [elements];
  var lastChangedFrame = 0;
  var lastLeft = new Map();
  var lastTop = new Map();
  elements.forEach((element) => {
    lastLeft.set(element, element.scrollLeft);
    lastTop.set(element, element.scrollTop);
  });
  function tick(frames) {
    // We requestAnimationFrame either for 5000 frames or until 20 frames with
    // no change have been observed. (In Chromium, frames may run as frequently
    // as once per millisecond when threaded compositing is disabled. The limit
    // of 5000 frames is chosen to be high enough to reasonably ensure any
    // scroll animation will run to completion.)
    if (frames >= 5000 || frames - lastChangedFrame > 20) {
      callback(true);
    } else {
      var scrollHappened = elements.some((element) => {
        return element.scrollLeft != lastLeft.get(element) || element.scrollTop != lastTop.get(element);
      });
      if (scrollHappened) {
        lastChangedFrame = frames;
        elements.forEach((element) => {
          lastLeft.set(element, element.scrollLeft);
          lastTop.set(element, element.scrollTop);
        });
        callback(false);
      }
      requestAnimationFrame(tick.bind(null, frames + 1));
    }
  }
  tick(0);
}

function waitForScrollEnd(elements) {
  return new Promise((resolve) => {
    observeScrolling(elements, (done) => {
      if (done)
        resolve();
    });
  });
}

function resetScroll(scrollingElement) {
  // Try various methods to ensure the element position is reset immediately.
  scrollingElement.scrollLeft = 0;
  scrollingElement.scrollTop = 0;
  scrollingElement.scroll({left: 0, top: 0, behavior: "instant"});
}

function resetScrollForWindow(scrollingWindow) {
  // Try various methods to ensure the element position is reset immediately.
  scrollingWindow.document.scrollingElement.scrollLeft = 0;
  scrollingWindow.document.scrollingElement.scrollTop = 0;
  scrollingWindow.scroll({left: 0, top: 0, behavior: "instant"});
}

function setScrollBehavior(styledElement, className) {
  styledElement.classList.remove("autoBehavior", "smoothBehavior");
  styledElement.classList.add(className);
}

function scrollNode(scrollingElement, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
  var args = {};
  if (behavior)
    args.behavior = behavior;
  switch (scrollFunction) {
    case "scrollIntoView":
      args.inline = "start";
      args.block = "start";
      elementToReveal.scrollIntoView(args);
      break;
    default:
      args.left = elementToRevealLeft;
      args.top = elementToRevealTop;
      scrollingElement[scrollFunction](args);
      break;
  }
}

function scrollWindow(scrollingWindow, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
  var args = {};
  if (behavior)
    args.behavior = behavior;
  args.left = elementToRevealLeft;
  args.top = elementToRevealTop;
  scrollingWindow[scrollFunction](args);
}