<!DOCTYPE html>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/open-blank-host.js"></script>
<body>
<script>
  async function createPortal(doc, url) {
    assert_implements("HTMLPortalElement" in self);
    let portal = doc.createElement("portal");
    portal.src = url;
    doc.body.appendChild(portal);
    await new Promise(r => portal.onload = r);
    return portal;
  }

  promise_test(async t => {
    let portal = await createPortal(document, new URL("resources/focus-page-with-button.html", location.href));
    try {
      portal.onmessage = t.step_func(e => {
        assert_unreached("button inside portal should not be focused");
      });
      portal.postMessage("focus");
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that an element inside a portal cannot steal focus");

  promise_test(async () => {
    let portal = await createPortal(document, new URL("resources/focus-page-with-button.html", location.href));
    try {
      let activeElementUpdated = new Promise(r => {
        portal.onmessage = e => r(e.data.activeElementUpdated)
      });
      portal.postMessage('focus-update-active-element');
      assert_true(await activeElementUpdated);
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that activeElement inside a portal is updated after focus() is called");

  promise_test(async t => {
    let portal = await createPortal(document, new URL("resources/focus-page-with-x-origin-iframe.sub.html", location.href));
    try {
      portal.onmessage = t.step_func(e => {
        assert_unreached("button inside portal should not be focused");
      });
      portal.postMessage("focus");
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that an element inside a portal's x-origin subframe cannot steal focus");

  promise_test(async () => {
    let portal = await createPortal(document, new URL("resources/focus-page-with-x-origin-iframe.sub.html", location.href));
    try {
      portal.postMessage("focus-update-active-element");
      let {activeElementUpdated} = await new Promise(r => {
        portal.onmessage = e => r(e.data);
      });
      assert_true(activeElementUpdated);
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that a portal's x-origin subframe becomes active element on focus");

  promise_test(async t => {
    let win = await openBlankPortalHost();
    let doc = win.document;
    try {
      let portal = await createPortal(doc, new URL("resources/simple-portal-adopts-predecessor.html", location.href));
      let button = doc.createElement("button");
      doc.body.appendChild(button);

      await portal.activate();
      doc.body.removeChild(portal);

      button.onfocus = t.step_func(() => {
        assert_unreached("button inside adopted portal should not be focused");
      });
      button.focus();
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      win.close();
    }
  }, "test that an element inside an adopted portal cannot steal focus");

  promise_test(async t => {
    let win = await openBlankPortalHost();
    let doc = win.document;
    try {
      let portal = await createPortal(doc, new URL("resources/simple-portal-adopts-predecessor.html", location.href));
      let iframe = doc.createElement("iframe");
      iframe.src = new URL("resources/focus-page-with-button.html",
                           "http://{{hosts[alt][www]}}:{{ports[http][0]}}/portals/");
      doc.body.appendChild(iframe);
      await new Promise(r => iframe.onload = r);

      await portal.activate();
      doc.body.removeChild(portal);

      iframe.contentWindow.postMessage("focus", "*");
      window.onmessage = t.step_func(() => {
        assert_unreached("button inside x-origin iframe inside a portal should not be focused");
      });
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      win.close();
    }
  }, "test that a x-origin iframe inside an adopted portal cannot steal focus");

  promise_test(async () => {
    let win = await openBlankPortalHost();
    let doc = win.document;
    try {
      let portal = await createPortal(doc, new URL("resources/focus-page-with-autofocus.html", location.href));
      portal.postMessage('check-active-element');
      let result = await new Promise(r => {
        portal.onmessage = e => r(e.data);
      });
      assert_true(result, "autofocused element is active element");

      await portal.activate();
      win.portalHost.postMessage('check-active-element');
      result = await new Promise(r => {
        win.portalHost.onmessage = e => r(e.data)
      });
      assert_true(result, "autofocused element is still active element");
    } finally {
      win.close();
    }
  }, "test that autofocus inside a portal works");

  const TAB = "\ue004"; // https://w3c.github.io/webdriver/#keyboard-actions
  const SPACE = " "
  const RETURN = "\r";

  promise_test(async t => {
    let portal = await createPortal(document, "resources/focus-page-with-button.html");
    try {
      await test_driver.send_keys(document.body, TAB);
      portal.onmessage = t.unreached_func("button inside portal should not be focused");
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that a portal is keyboard focusable");

  promise_test(async t => {
    let portal = await createPortal(document, "resources/focus-page-with-button.html");
    try {
      let portalFocusPromise = new Promise(r => portal.onfocus = r);
      portal.onmessage = t.unreached_func("button inside portal should not be focused");
      await test_driver.send_keys(document.body, TAB);
      await portalFocusPromise;
      await test_driver.send_keys(document.body, TAB);
      await new Promise(r => t.step_timeout(r, 500));
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that we cannot tab into a portal's contents");

  promise_test(async t => {
    let portal = await createPortal(document, "resources/simple-portal.html");
    try {
      portal.focus();
      for (let key of [SPACE, RETURN]) {
        let clickPromise = new Promise((resolve) => {
          portal.onclick = e => { e.preventDefault(); resolve(); };
        });
        await test_driver.send_keys(document.body, key);
        await clickPromise;
      }
    } finally {
      document.body.removeChild(portal);
    }
  }, "test that a portal is keyboard activatable");

</script>
</body>
