chromium/third_party/blink/web_tests/http/tests/mojo/detached-frame.html

<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script type="module">
import {MojoWebTestHelper, MojoWebTestHelperRemote} from '/gen/content/test/data/mojo_web_test_helper_test.mojom.m.js';

function frameLoaded(frame) {
  return new Promise(
      resolve => frame.addEventListener('load', resolve, {once: true}));
}

promise_test(async () => {
  let frame = document.createElement("iframe");
  let promise = frameLoaded(frame);
  document.body.appendChild(frame);
  await promise;

  // Save a reference to the Mojo object from the child frame so that we can
  // make calls to it after the context has been destroyed.
  let frameMojo = frame.contentWindow.Mojo;
  document.body.removeChild(frame);

  const {handle0, handle1} = frameMojo.createMessagePipe();
  let helper = new MojoWebTestHelperRemote(handle0);
  frameMojo.bindInterface(MojoWebTestHelper.$interfaceName, handle1);

  try {
    await helper.reverse("hello world.");
    assert_unreached();
  } catch (e) {
    // Connection failure expected.
  }
}, "Mojo object is safe to use after its frame has been detached");

promise_test(async () => {
  let frame = document.createElement("iframe");
  let promise = frameLoaded(frame);
  document.body.appendChild(frame);
  await promise;

  // Save a reference to the MojoInterfaceInterceptor constructor from the child
  // frame so that we can be used after the context has been destroyed.
  let frameMojoInterfaceInterceptor =
      frame.contentWindow.MojoInterfaceInterceptor;
  document.body.removeChild(frame);

  let interceptor = new frameMojoInterfaceInterceptor(
      MojoWebTestHelper.$interfaceName);
  try {
    interceptor.start();
    assert_unreached();
  } catch (e) {
    // Failure expected.
  }
}, "MojoInterfaceInterceptor constructor is safe to use after its frame has been detached");

promise_test(async () => {
  let frame = document.createElement("iframe");
  let promise = frameLoaded(frame);
  document.body.appendChild(frame);
  await promise;

  // Create the interceptor while the frame is attached so that it is associated
  // with the frame's execution context.
  let interceptor = new frame.contentWindow.MojoInterfaceInterceptor(
      MojoWebTestHelper.$interfaceName);
  document.body.removeChild(frame);

  try {
    interceptor.start();
    assert_unreached();
  } catch (e) {
    // Failure expected.
  }
}, "MojoInterfaceInterceptor can't be started after its frame has been detached");

promise_test(async () => {
  let frame = document.createElement("iframe");
  let promise = frameLoaded(frame);
  document.body.appendChild(frame);
  await promise;

  // Create the interceptor and start it while the frame is attached.
  let interceptor = new frame.contentWindow.MojoInterfaceInterceptor(
      MojoWebTestHelper.$interfaceName);
  interceptor.start();
  document.body.removeChild(frame);

  // stop() will succeed because it is implicitly called when the execution
  // context is destroyed.
  interceptor.stop();
}, "MojoInterfaceInterceptor is stopped on frame destruction");

</script>
</body>