<!DOCTYPE html>
<meta charset="utf-8" />
<title>PaymentRequest show() rejects if doc is not fully active</title>
<link rel="help" href="https://w3c.github.io/payment-request/#show-method" />
<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>
<body>
  <script>
    const applePay = Object.freeze({
      supportedMethods: "https://apple.com/apple-pay",
      data: {
        version: 3,
        merchantIdentifier: "merchant.com.example",
        countryCode: "US",
        merchantCapabilities: ["supports3DS"],
        supportedNetworks: ["visa"],
      },
    });
    const validMethod = Object.freeze({
      supportedMethods: "basic-card",
    });
    const validMethods = Object.freeze([validMethod, applePay]);

    const validDetails = Object.freeze({
      total: {
        label: "Total due",
        amount: {
          currency: "USD",
          value: "5.00",
        },
      },
    });

    async function getLoadedPaymentRequest(iframe, url) {
      await new Promise((resolve) => {
        iframe.addEventListener("load", resolve, { once: true });
        iframe.src = url;
      });
      return new iframe.contentWindow.PaymentRequest(
        validMethods,
        validDetails
      );
    }

    promise_test(async (t) => {
      const iframe = document.createElement("iframe");
      iframe.allow = "payment";
      document.body.appendChild(iframe);
      t.add_cleanup(() => {
        iframe.remove();
      });
      // We first got to page1.html, grab a PaymentRequest instance.
      const request1 = await getLoadedPaymentRequest(
        iframe,
        "./resources/page1.html"
      );
      // Save the DOMException of page1.html before navigating away.
      const frameDOMException1 = iframe.contentWindow.DOMException;

      // Get transient activation
      await test_driver.bless("payment 1", () => {}, iframe.contentWindow);

      // We navigate the iframe again, putting request1's document into an non-fully active state.
      const request2 = await getLoadedPaymentRequest(
        iframe,
        "./resources/page2.html"
      );

      // Now, request1's relevant global object's document is no longer active.
      // So, call .show(), and make sure it rejects appropriately.
      await promise_rejects_dom(
        t,
        "AbortError",
        frameDOMException1,
        request1.show(),
        "Inactive document, so must throw AbortError"
      );
      // request2 has an active document tho, so confirm it's working as expected:
      // Get transient activation
      await test_driver.bless("payment 2", () => {}, iframe.contentWindow);
      request2.show();
      await request2.abort();
      await promise_rejects_dom(
        t,
        "InvalidStateError",
        iframe.contentWindow.DOMException,
        request2.show(),
        "Abort already called, so InvalidStateError"
      );
    }, "PaymentRequest.show() aborts if the document is not active.");

    promise_test(async (t) => {
      // We nest two iframes and wait for them to load.
      const outerIframe = document.createElement("iframe");
      outerIframe.allow = "payment";
      document.body.appendChild(outerIframe);
      t.add_cleanup(() => {
        outerIframe.remove();
      });
      // Load the outer iframe (we don't care about the awaited request)
      await getLoadedPaymentRequest(outerIframe, "./resources/page1.html");

      // Now we create the inner iframe
      const innerIframe = outerIframe.contentDocument.createElement("iframe");
      innerIframe.allow = "payment";

      // nest them
      outerIframe.contentDocument.body.appendChild(innerIframe);

      // load innerIframe, and get the PaymentRequest instance
      const request = await getLoadedPaymentRequest(
        innerIframe,
        "./resources/page2.html"
      );
      // Save DOMException from innerIframe before navigating away.
      const innerIframeDOMException = innerIframe.contentWindow.DOMException;

      // Navigate the outer iframe to a new location.
      // Wait for the load event to fire.
      await new Promise((resolve) => {
        outerIframe.addEventListener("load", resolve);
        outerIframe.src = "./resources/page2.html";
      });

      await test_driver.bless("", () => {}, innerIframe.contentWindow);
      const showPromise = request.show();
      // Now, request's relevant global object's document is still active
      // (it is the active document of the inner iframe), but is not fully active
      // (since the parent of the inner iframe is itself no longer active).
      // So, call request.show() and make sure it rejects appropriately.
      await promise_rejects_dom(
        t,
        "AbortError",
        innerIframeDOMException,
        showPromise,
        "Active, but not fully active, so must throw AbortError"
      );
    }, "PaymentRequest.show() aborts if the document is active, but not fully active.");
  </script>
</body>
