<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="utils.js"></script>
<title>Nested fenced frame named navigation helper</title>

<body>
<script>
(async () => {
  // We need to wait for the window's `load` event to fire, because client-side
  // redirect navigations that take place before a document is "completely
  // loaded" [1] are carried out with replacement, as specified in [2]. Just
  // waiting for `load` is not enough though! After the `load` event is fired
  // (but before a document is marked "completely loaded"), a microtask
  // checkpoint is performed, which is where the below `Promise`'s `then`
  // handler is invoked (i.e., the rest of the script). So if we just resolve
  // the promise and continue, the whole script continues in the next immediate
  // microtask before the document is completely loaded. So we have to queue
  // another task so that we only continue executing once the document is
  // considered completely loaded, and then `location.href` assignments will not
  // be made with replacement history handling.
  //
  // [1]: https://html.spec.whatwg.org/C#the-end:completely-finish-loading
  // [2]: https://html.spec.whatwg.org/#the-location-interface:completely-loaded
  await new Promise(resolve => {
    window.onload = e => {
      setTimeout(resolve, 0);
    };
  });

  const kNavigationLimit = 5;
  // This is a helper file meant to be loaded inside a fenced frame. It performs
  // various navigations inside of the "fence" defined by this document, and
  // ensures that they are all done in a replace-only fashion [1].
  // Once we ensure that they are all done with replacement, we report back to
  // the outermost page via the server stash, and it ensures that there was no
  // impact on the joint session history as observed from beyond the fence.
  //
  // [1]: https://html.spec.whatwg.org/C/#hh-replace

  // See documentation in the outer page.
  const [fenced_navigation_complete_key,
         outer_page_ready_for_next_fenced_navigation_key,
         level] = parseKeylist();

  const url = new URL(location.href);
  const is_top_level_fenced_frame = (level == "top-level-fenced-frame");

  ////////////// Navigation code that may impact `history.length` should go here
  // The code in this block performs navigations that will run inside:
  //   - The top-level fenced frame
  //   - The nested fenced frame
  //   - The nested iframe

  // First, perform some real navigations to this same page. Normally this would
  // increase `history.length`.
  if (url.searchParams.get("navigationNumber") == null)
    url.searchParams.append("navigationNumber", 0);

  let navigationNumber = parseInt(url.searchParams.get("navigationNumber"));

  if (navigationNumber <= kNavigationLimit) {
    url.searchParams.set('navigationNumber', navigationNumber + 1);
    location.href = url;
    return;
  }

  // At this point we're done performing 5 subsequent navigations...

  // Next, perform `history.pushState()`s.
  history.pushState({} , "");
  history.pushState({} , "");
  history.pushState({} , "");
  ////////////// END

  // Finally observe `history.length` from within the fenced frame, and report
  // the results back to the outermost page.
  if (history.length == 1) {
    writeValueToServer(fenced_navigation_complete_key, "PASS > " +
        level);
  } else {
    writeValueToServer(fenced_navigation_complete_key,
        "FAIL > " + level + " history.length: " +
        history.length);
  }

  // We're only testing fenced frames, nested fenced frames, and iframes nested
  // within fenced frames. The below code adds a nested fenced frame and a
  // nested iframe, so it should only be reached by the top-level fenced frame.
  if (level != "top-level-fenced-frame")
    return;

  // Only top-level fenced frames will attach a nested fenced frame and run the
  // same tests there.
  await nextValueFromServer(outer_page_ready_for_next_fenced_navigation_key);
  const nested_fenced_frame_level = "nested-fenced-frame";
  attachFencedFrame(generateURL(
      "history-length-fenced-navigations-replace-do-not-" +
      "contribute-to-joint-inner.html",
      [fenced_navigation_complete_key,
      outer_page_ready_for_next_fenced_navigation_key,
      nested_fenced_frame_level]));

  await nextValueFromServer(outer_page_ready_for_next_fenced_navigation_key);
  const iframe = document.createElement('iframe');
  const nested_iframe_level = "nested-iframe";
  iframe.src = generateURL(
      "history-length-fenced-navigations-replace-do-not-contribute-to-joint-" +
      "inner.html",
      [fenced_navigation_complete_key,
      outer_page_ready_for_next_fenced_navigation_key,
      nested_iframe_level]);
  document.body.append(iframe);
})();
</script>
</body>
