<!DOCTYPE HTML>
<meta charset=utf-8>
<title>Long Animation Frame Timing: queue time</title>
<meta name="timeout" content="long">
<script src=/resources/testdriver.js></script>
<script src=/resources/testdriver-actions.js></script>
<script src=/resources/testdriver-vendor.js></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>

<body>
<h1>Long Animation Frame: queue time</h1>
<div id="log"></div>
<script>

const INTERNAL_OVERHEAD_DELAY_EPSILON = 5;

promise_test(async t => {
  const button = document.createElement("button");
  button.innerText = "Click";
  document.body.appendChild(button);
  t.add_cleanup(() => button.remove());
  const eventPromise = new Promise(resolve => button.addEventListener("click", event => {
    busy_wait();
    resolve(event);
  }));
  const entryPromise = new Promise(resolve => new PerformanceObserver(
    (entryList, observer) => {
      const scriptPredicate = s => s.name === "BUTTON.onclick";
      const entry = entryList.getEntries().find(
        e => e.scripts.length && e.scripts.find(scriptPredicate));
      if (entry) {
        resolve([entry, entry.scripts.find(scriptPredicate)]);
        observer.disconnect();
      }
    }).observe({entryTypes: ["long-animation-frame"]}));
  test_driver.click(button);
  await new Promise(resolve => t.step_timeout(resolve, 0));
  const event = await eventPromise;
  const [entry, script] = await entryPromise;
  assert_equals(script.desiredExecutionStart, event.timeStamp);
}, "event-listener entries desiredExecutionStart is the eventTimestamp");

promise_test(async t => {
  const entryPromise = loaf_promise(t);
  let timeBeforeSetup, timeAfterSetup;
  const delay = 100;
  const timeoutPromise = new Promise(resolve => {
    timeBeforeSetup = performance.now();
    t.step_timeout(() => {
      busy_wait();
      resolve();
    }, delay);
    timeAfterSetup = performance.now();
  });
  const entry = await entryPromise;
  const script = entry.scripts.find(s => s.name === "TimerHandler:setTimeout");
  assert_greater_than_equal(script.desiredExecutionStart, timeBeforeSetup + delay - 5);
  assert_less_than_equal(script.desiredExecutionStart, timeAfterSetup + delay);
}, "desiredExecutionStart for setTimeout should be the setup time + delay");

promise_test(async t => {
  const entryPromise = loaf_promise(t);
  let timeBeforeSetup, timeAfterSetup;
  const timeoutPromise = new Promise(resolve => {
    timeBeforeSetup = performance.now();
   scheduler.postTask(t.step_func(() => {
      busy_wait();
      resolve();
    }));
    timeAfterSetup = performance.now();
  });
  const entry = await entryPromise;
  const script = entry.scripts.find(s => s.name === "SchedulerPostTaskCallback");
  assert_greater_than_equal(script.desiredExecutionStart, timeBeforeSetup);
  assert_less_than_equal(script.desiredExecutionStart, timeAfterSetup);
}, "desiredExecutionStart for Scheduler.postTask should be the time it was called");

promise_test(async t => {
  const entryPromise = loaf_promise(t);
  const rafPromise = new Promise(resolve => {
    // We fire two rAFs to ensure both of them receive the same
    // desiredExecutionStart
  requestAnimationFrame(rafTime => {
      busy_wait();
    })
    requestAnimationFrame(rafTime => {
      busy_wait();
      resolve(rafTime);
    })
  });
  const entry = await entryPromise;
  const rafTime = await rafPromise;
  const scripts = entry.scripts.filter(
    s => s.name === "FrameRequestCallback");
  for (const script of scripts) {
    assert_approx_equals(script.desiredExecutionStart, rafTime, INTERNAL_OVERHEAD_DELAY_EPSILON);
  }
  assert_approx_equals(entry.desiredRenderStart, rafTime, INTERNAL_OVERHEAD_DELAY_EPSILON);
}, "desiredExecutionStart & desiredRenderStart for requestAnimationFrame " +
   "should be the same as the rAF argument");

promise_test(async t => {
  const entryPromise = loaf_promise(t);
  const timeBeforeWait = performance.now();
  let timeAfterWait;
  const rafPromise = new Promise(resolve => t.step_timeout(() => {
    requestAnimationFrame(rafTime => {
      busy_wait(very_long_frame_duration / 2);
      resolve(rafTime);
    });

    busy_wait(very_long_frame_duration / 2);
    timeAfterWait = performance.now();
  }), 0);
  const [entry, rafTime] = await Promise.all([entryPromise, rafPromise]);
  assert_approx_equals(entry.desiredRenderStart, rafTime, INTERNAL_OVERHEAD_DELAY_EPSILON);
}, "desiredRenderStart and renderStart should reflect main thread delays");
</script>
</body>
