chromium/third_party/blink/web_tests/wpt_internal/task-tracking/track-xhr.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>Verify that promise tasks can be properly tracked.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/task-ids.js"></script>

<script>
'use strict'

promise_test((t) => {
  return new Promise((resolve, reject) => {
    const initialId = initializeTaskId();
    const request = new XMLHttpRequest();
    request.onload = t.step_func(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("XHR is not a descendant");
      }
    });
    request.open("GET", "/resources/blank.html");
    request.send();
  });
}, "An XMLHttpRequest load event is a descendant of the requesting task");

promise_test((t) => {
  return new Promise((resolve, reject) => {
    const initialId = initializeTaskId();
    const request = new XMLHttpRequest();
    request.addEventListener('load', t.step_func(() => {
      Promise.resolve().then(() => {});
    }));
    // This microtask checkpoint from the first listener will clear the
    // continuation data in v8. Make sure the continuation data is set for the
    // second listener.
    request.addEventListener('load', t.step_func(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
        resolve();
      } catch {
        reject("XHR is not a descendant)");
      }
    }));
    request.open("GET", "/resources/blank.html");
    request.send();
  });
}, "An XMLHttpRequest load event is a descendant of the requesting task (multiple listeners)");

promise_test((t) => {
  return new Promise((resolve, reject) => {
    const initialId = initializeTaskId();
    const request = new XMLHttpRequest();
    request.onreadystatechange= t.step_func(() => {
      try {
        assert_equals(scheduler.taskId, initialId);
      } catch {
        reject("XHR readystatechange is not a descendant");
      }
      if (request.readyState === XMLHttpRequest.DONE) {
        resolve();
      }
    });
    request.open("GET", "/resources/blank.html");
    request.send();
  });
}, "XMLHttpRequest readystatechange events are descendants of the requesting task");

promise_test((t) => {
  return new Promise((resolve, reject) => {
    let request;
    let id1;
    let id2;

    scheduler.postTask(() => {
      id1 = initializeTaskId();
      request = new XMLHttpRequest();
      request.onload = t.step_func(() => {
        try {
          assert_not_equals(scheduler.taskId, id1);
          assert_equals(scheduler.taskId, id2);
          resolve();
        } catch {
          reject("XHR is not a descendant of the correct task");
        }
      });
      request.open("GET", "/resources/blank.html");
    });

    scheduler.postTask(() => {
      id2 = initializeTaskId();
      assert_equals(scheduler.taskId, id2);
      request.send();
    });
  });
}, "XMLHttpRequest load event is an ancestor of send()");

promise_test((t) => {
  return new Promise((resolve, reject) => {
    let request;
    let id1;
    let id2;

    scheduler.postTask(() => {
      id1 = initializeTaskId();
      request = new XMLHttpRequest();
      request.open("GET", "/resources/blank.html");

      // This runs after the send() call below.
      scheduler.postTask(() => {
        request.open("GET", "/resources/blank.html");
        request.onload = t.step_func(() => {
          try {
            assert_equals(scheduler.taskId, id1);
            assert_not_equals(scheduler.taskId, id2);
            resolve();
          } catch {
            reject("XHR is not a descendant of the correct task");
          }
        });
        request.send();
      });
    });

    scheduler.postTask(() => {
      id2 = initializeTaskId();
      request.send();
    });
  });
}, "XMLHttpRequest load event is an ancestor of send() when reusing the object");

promise_test((t) => {
  // This resolves the promise created below in an unrelated task.
  let innerResolve;
  (async function() {
    while (!innerResolve) {
      await scheduler.postTask(() => {}, {delay: 50});
    }
    innerResolve();
  })();

  return new Promise((resolve, reject) => {
    // Run the rest of the test in a separate task so the polling tasks above
    // won't be descendants of `initialId`.
    scheduler.postTask(() => {
      const initialId = initializeTaskId();
      const request = new XMLHttpRequest();
      request.addEventListener('load', t.step_func(() => {
        assert_equals(scheduler.taskId, initialId);
        Promise.resolve().then(() => {});
      }));

      // The microtask in the event listener above clears the continuation data
      // in v8. Ensure that the right context is captured for the microtask
      // below resolving the related promise in a non-ancestor task.
      request.addEventListener('load', t.step_func(async () => {
        await new Promise((r) => { innerResolve = r; });
        try {
          assert_equals(scheduler.taskId, initialId);
          resolve();
        } catch (e) {
          reject("XHR is not a descendant");
        }
      }));
      request.open("GET", "/resources/blank.html");
      request.send();
    });
  });
}, "XMLHttpRequest load event is an ancestor of send() after a microtask checkpoint");

</script>