/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const TEST_THIRD_PARTY_DOMAIN = TEST_DOMAIN_2;
const TEST_THIRD_PARTY_SUB_DOMAIN = "http://sub1.xn--exmple-cua.test/";

const TEST_URI = TEST_DOMAIN + TEST_PATH + "file_stripping.html";
const TEST_THIRD_PARTY_URI =
  TEST_THIRD_PARTY_DOMAIN + TEST_PATH + "file_stripping.html";
const TEST_REDIRECT_URI = TEST_DOMAIN + TEST_PATH + "redirect.sjs";

const TEST_QUERY_STRING = "paramToStrip1=123&paramToKeep=456";
const TEST_STRIPPED_QUERY_STRING = "paramToKeep=456";

let listService;

function observeChannel(uri, expected) {
  return TestUtils.topicObserved("http-on-modify-request", subject => {
    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
    let channelURI = channel.URI;

    if (channelURI.spec.startsWith(uri)) {
      is(
        channelURI.query,
        expected,
        "The loading channel has the expected query string."
      );
      return true;
    }

    return false;
  });
}

async function verifyQueryString(browser, expected) {
  await SpecialPowers.spawn(browser, [expected], expected => {
    // Strip the first question mark.
    let search = content.location.search.slice(1);

    is(search, expected, "The query string is correct.");
  });
}

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.query_stripping.strip_list", "paramToStrip1 paramToStrip2"],
      ["privacy.query_stripping.redirect", true],
      ["privacy.query_stripping.listService.logLevel", "Debug"],
      ["privacy.query_stripping.strip_on_share.enabled", false],
    ],
  });

  // Get the list service so we can wait for it to be fully initialized before running tests.
  listService = Cc["@mozilla.org/query-stripping-list-service;1"].getService(
    Ci.nsIURLQueryStrippingListService
  );
  // Here we don't care about the actual enabled state, we just want any init to be done so we get reliable starting conditions.
  await listService.testWaitForInit();
});

async function waitForListServiceInit(strippingEnabled) {
  info("Waiting for nsIURLQueryStrippingListService to be initialized.");
  let isInitialized = await listService.testWaitForInit();
  is(
    isInitialized,
    strippingEnabled,
    "nsIURLQueryStrippingListService should be initialized when the feature is enabled."
  );
}

add_task(async function doTestsForTabOpen() {
  info("Start testing query stripping for tab open.");
  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    let testURI = TEST_URI + "?" + TEST_QUERY_STRING;
    let expected = strippingEnabled
      ? TEST_STRIPPED_QUERY_STRING
      : TEST_QUERY_STRING;

    let networkPromise = observeChannel(TEST_URI, expected);

    await BrowserTestUtils.withNewTab(testURI, async browser => {
      await verifyQueryString(browser, expected);
    });

    await networkPromise;

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForWindowOpen() {
  info("Start testing query stripping for window.open().");
  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    let testFirstPartyURI = TEST_URI + "?" + TEST_QUERY_STRING;
    let testThirdPartyURI = TEST_THIRD_PARTY_URI + "?" + TEST_QUERY_STRING;

    let originalQueryString = TEST_QUERY_STRING;
    let expectedQueryString = strippingEnabled
      ? TEST_STRIPPED_QUERY_STRING
      : TEST_QUERY_STRING;

    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Observe the channel and check if the query string is intact when open
      // a same-origin URI.
      let networkPromise = observeChannel(TEST_URI, originalQueryString);

      // Create the promise to wait for the opened tab.
      let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url => {
        return url.startsWith(TEST_URI);
      });

      // Call window.open() to open the same-origin URI where the query string
      // won't be stripped.
      await SpecialPowers.spawn(browser, [testFirstPartyURI], async url => {
        content.postMessage({ type: "window-open", url }, "*");
      });

      await networkPromise;
      let newTab = await newTabPromise;

      // Verify if the query string is expected in the new opened tab.
      await verifyQueryString(newTab.linkedBrowser, originalQueryString);

      BrowserTestUtils.removeTab(newTab);

      // Observe the channel and check if the query string is expected for
      // cross-origin URI.
      networkPromise = observeChannel(
        TEST_THIRD_PARTY_URI,
        expectedQueryString
      );

      newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url => {
        return url.startsWith(TEST_THIRD_PARTY_URI);
      });

      // Call window.open() to open the cross-site URI where the query string
      // could be stripped.
      await SpecialPowers.spawn(browser, [testThirdPartyURI], async url => {
        content.postMessage({ type: "window-open", url }, "*");
      });

      await networkPromise;
      newTab = await newTabPromise;

      // Verify if the query string is expected in the new opened tab.
      await verifyQueryString(newTab.linkedBrowser, expectedQueryString);

      BrowserTestUtils.removeTab(newTab);
    });

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForLinkClick() {
  info("Start testing query stripping for link navigation.");
  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    {
      let testFirstPartyURI = TEST_URI + "?" + TEST_QUERY_STRING;
      let testThirdPartyURI = TEST_THIRD_PARTY_URI + "?" + TEST_QUERY_STRING;

      let originalQueryString = TEST_QUERY_STRING;
      let expectedQueryString = strippingEnabled
        ? TEST_STRIPPED_QUERY_STRING
        : TEST_QUERY_STRING;

      await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
        // Observe the channel and check if the query string is intact when
        // click a same-origin link.
        let networkPromise = observeChannel(TEST_URI, originalQueryString);

        // Create the promise to wait for the location change.
        let locationChangePromise = BrowserTestUtils.waitForLocationChange(
          gBrowser,
          testFirstPartyURI
        );

        // Create a link and click it to navigate.
        await SpecialPowers.spawn(browser, [testFirstPartyURI], async uri => {
          let link = content.document.createElement("a");
          link.setAttribute("href", uri);
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        });

        await networkPromise;
        await locationChangePromise;

        // Verify the query string in the content window.
        await verifyQueryString(browser, originalQueryString);

        // Second, create a link to a cross-origin site to see if the query
        // string is stripped as expected.

        // Observe the channel and check if the query string is expected when
        // click a cross-origin link.
        networkPromise = observeChannel(
          TEST_THIRD_PARTY_URI,
          expectedQueryString
        );

        let targetURI = expectedQueryString
          ? `${TEST_THIRD_PARTY_URI}?${expectedQueryString}`
          : TEST_THIRD_PARTY_URI;
        // Create the promise to wait for the location change.
        locationChangePromise = BrowserTestUtils.waitForLocationChange(
          gBrowser,
          targetURI
        );

        // Create a cross-origin link and click it to navigate.
        await SpecialPowers.spawn(browser, [testThirdPartyURI], async uri => {
          let link = content.document.createElement("a");
          link.setAttribute("href", uri);
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        });

        await networkPromise;
        await locationChangePromise;

        // Verify the query string in the content window.
        await verifyQueryString(browser, expectedQueryString);
      });
    }

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForLinkClickInIframe() {
  info("Start testing query stripping for link navigation in iframe.");
  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    {
      let testFirstPartyURI = TEST_URI + "?" + TEST_QUERY_STRING;
      let testThirdPartyURI = TEST_THIRD_PARTY_URI + "?" + TEST_QUERY_STRING;

      let originalQueryString = TEST_QUERY_STRING;
      let expectedQueryString = strippingEnabled
        ? TEST_STRIPPED_QUERY_STRING
        : TEST_QUERY_STRING;

      await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
        // Create an iframe and wait until it has been loaded.
        let iframeBC = await SpecialPowers.spawn(
          browser,
          [TEST_URI],
          async url => {
            let frame = content.document.createElement("iframe");
            content.document.body.appendChild(frame);

            await new Promise(done => {
              frame.addEventListener(
                "load",
                function () {
                  done();
                },
                { capture: true, once: true }
              );

              frame.setAttribute("src", url);
            });

            return frame.browsingContext;
          }
        );

        // Observe the channel and check if the query string is intact when
        // click a same-origin link.
        let networkPromise = observeChannel(TEST_URI, originalQueryString);

        // Create the promise to wait for the new tab.
        let newTabPromise = BrowserTestUtils.waitForNewTab(
          gBrowser,
          testFirstPartyURI
        );

        // Create a same-site link which has '_blank' as target in the iframe
        // and click it to navigate.
        await SpecialPowers.spawn(iframeBC, [testFirstPartyURI], async uri => {
          let link = content.document.createElement("a");
          link.setAttribute("href", uri);
          link.setAttribute("target", "_blank");
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        });

        await networkPromise;
        let newOpenedTab = await newTabPromise;

        // Verify the query string in the content window.
        await verifyQueryString(
          newOpenedTab.linkedBrowser,
          originalQueryString
        );
        BrowserTestUtils.removeTab(newOpenedTab);

        // Second, create a link to a cross-origin site in the iframe to see if
        // the query string is stripped as expected.

        // Observe the channel and check if the query string is expected when
        // click a cross-origin link.
        networkPromise = observeChannel(
          TEST_THIRD_PARTY_URI,
          expectedQueryString
        );

        let targetURI = expectedQueryString
          ? `${TEST_THIRD_PARTY_URI}?${expectedQueryString}`
          : TEST_THIRD_PARTY_URI;
        // Create the promise to wait for the new tab.
        newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, targetURI);

        // Create a cross-origin link which has '_blank' as target in the iframe
        // and click it to navigate.
        await SpecialPowers.spawn(iframeBC, [testThirdPartyURI], async uri => {
          let link = content.document.createElement("a");
          link.setAttribute("href", uri);
          link.setAttribute("target", "_blank");
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        });

        await networkPromise;
        newOpenedTab = await newTabPromise;

        // Verify the query string in the content window.
        await verifyQueryString(
          newOpenedTab.linkedBrowser,
          expectedQueryString
        );
        BrowserTestUtils.removeTab(newOpenedTab);
      });
    }

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForScriptNavigation() {
  info("Start testing query stripping for script navigation.");
  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    let testFirstPartyURI = TEST_URI + "?" + TEST_QUERY_STRING;
    let testThirdPartyURI = TEST_THIRD_PARTY_URI + "?" + TEST_QUERY_STRING;

    let originalQueryString = TEST_QUERY_STRING;
    let expectedQueryString = strippingEnabled
      ? TEST_STRIPPED_QUERY_STRING
      : TEST_QUERY_STRING;

    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Observe the channel and check if the query string is intact when
      // navigating to a same-origin URI via script.
      let networkPromise = observeChannel(TEST_URI, originalQueryString);

      // Create the promise to wait for the location change.
      let locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        testFirstPartyURI
      );

      // Trigger the navigation by script.
      await SpecialPowers.spawn(browser, [testFirstPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the content window.
      await verifyQueryString(browser, originalQueryString);

      // Second, trigger a cross-origin navigation through script to see if
      // the query string is stripped as expected.

      let targetURI = expectedQueryString
        ? `${TEST_THIRD_PARTY_URI}?${expectedQueryString}`
        : TEST_THIRD_PARTY_URI;

      // Observe the channel and check if the query string is expected.
      networkPromise = observeChannel(
        TEST_THIRD_PARTY_URI,
        expectedQueryString
      );

      locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        targetURI
      );

      // Trigger the cross-origin navigation by script.
      await SpecialPowers.spawn(browser, [testThirdPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the content window.
      await verifyQueryString(browser, expectedQueryString);
    });

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForNoStrippingForIframeNavigation() {
  info("Start testing no query stripping for iframe navigation.");

  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    let testFirstPartyURI = TEST_URI + "?" + TEST_QUERY_STRING;
    let testThirdPartyURI = TEST_THIRD_PARTY_URI + "?" + TEST_QUERY_STRING;

    // There should be no query stripping for the iframe navigation.
    let originalQueryString = TEST_QUERY_STRING;
    let expectedQueryString = TEST_QUERY_STRING;

    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Create an iframe and wait until it has been loaded.
      let iframeBC = await SpecialPowers.spawn(
        browser,
        [TEST_URI],
        async url => {
          let frame = content.document.createElement("iframe");
          content.document.body.appendChild(frame);

          await new Promise(done => {
            frame.addEventListener(
              "load",
              function () {
                done();
              },
              { capture: true, once: true }
            );

            frame.setAttribute("src", url);
          });

          return frame.browsingContext;
        }
      );

      // Observe the channel and check if the query string is intact when
      // navigating an iframe.
      let networkPromise = observeChannel(TEST_URI, originalQueryString);

      // Create the promise to wait for the location change.
      let locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        testFirstPartyURI
      );

      // Trigger the iframe navigation by script.
      await SpecialPowers.spawn(iframeBC, [testFirstPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the iframe.
      await verifyQueryString(iframeBC, originalQueryString);

      // Second, trigger a cross-origin navigation through script to see if
      // the query string is still the same.

      let targetURI = expectedQueryString
        ? `${TEST_THIRD_PARTY_URI}?${expectedQueryString}`
        : TEST_THIRD_PARTY_URI;

      // Observe the channel and check if the query string is not stripped.
      networkPromise = observeChannel(
        TEST_THIRD_PARTY_URI,
        expectedQueryString
      );

      locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        targetURI
      );

      // Trigger the cross-origin iframe navigation by script.
      await SpecialPowers.spawn(iframeBC, [testThirdPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the content window.
      await verifyQueryString(iframeBC, expectedQueryString);
    });

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestsForRedirect() {
  info("Start testing query stripping for redirects.");

  for (const strippingEnabled of [false, true]) {
    await SpecialPowers.pushPrefEnv({
      set: [["privacy.query_stripping.enabled", strippingEnabled]],
    });
    await waitForListServiceInit(strippingEnabled);

    let testFirstPartyURI =
      TEST_REDIRECT_URI + "?" + TEST_URI + "?" + TEST_QUERY_STRING;
    let testThirdPartyURI = `${TEST_REDIRECT_URI}?${TEST_THIRD_PARTY_URI}?${TEST_QUERY_STRING}`;

    let originalQueryString = TEST_QUERY_STRING;
    let expectedQueryString = strippingEnabled
      ? TEST_STRIPPED_QUERY_STRING
      : TEST_QUERY_STRING;

    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Observe the channel and check if the query string is intact when
      // redirecting to a same-origin URI .
      let networkPromise = observeChannel(TEST_URI, originalQueryString);

      let targetURI = `${TEST_URI}?${originalQueryString}`;

      // Create the promise to wait for the location change.
      let locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        targetURI
      );

      // Trigger the redirect.
      await SpecialPowers.spawn(browser, [testFirstPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the content window.
      await verifyQueryString(browser, originalQueryString);

      // Second, trigger a redirect to a cross-origin site where the query
      // string should be stripped.

      targetURI = expectedQueryString
        ? `${TEST_THIRD_PARTY_URI}?${expectedQueryString}`
        : TEST_THIRD_PARTY_URI;

      // Observe the channel and check if the query string is expected.
      networkPromise = observeChannel(
        TEST_THIRD_PARTY_URI,
        expectedQueryString
      );

      locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        targetURI
      );

      // Trigger the cross-origin redirect.
      await SpecialPowers.spawn(browser, [testThirdPartyURI], async url => {
        content.postMessage({ type: "script", url }, "*");
      });

      await networkPromise;
      await locationChangePromise;

      // Verify the query string in the content window.
      await verifyQueryString(browser, expectedQueryString);
    });

    await SpecialPowers.popPrefEnv();
  }
});

add_task(async function doTestForAllowList() {
  info("Start testing query stripping allow list.");

  // Enable the query stripping and set the allow list.
  await SpecialPowers.pushPrefEnv({
    set: [
      ["privacy.query_stripping.enabled", true],
      ["privacy.query_stripping.allow_list", "xn--exmple-cua.test"],
    ],
  });
  await waitForListServiceInit(true);

  const expected = "paramToStrip1=123";

  // Make sure the allow list works for sites, so we will test both the domain
  // and the sub domain.
  for (const domain of [TEST_THIRD_PARTY_DOMAIN, TEST_THIRD_PARTY_SUB_DOMAIN]) {
    let testURI = `${domain}${TEST_PATH}file_stripping.html`;
    let testURIWithQueryString = `${testURI}?${expected}`;

    // 1. Test the allow list for tab open.
    info("Run tab open test.");

    // Observe the channel and check if the query string is not stripped.
    let networkPromise = observeChannel(testURI, expected);

    await BrowserTestUtils.withNewTab(testURIWithQueryString, async browser => {
      // Verify if the query string is not stripped in the new tab.
      await verifyQueryString(browser, expected);
    });

    await networkPromise;

    // 2. Test the allow list for window open
    info("Run window open test.");
    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Observe the channel and check if the query string is not stripped.
      let networkPromise = observeChannel(testURI, expected);

      let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url => {
        return url.startsWith(testURI);
      });

      await SpecialPowers.spawn(
        browser,
        [testURIWithQueryString],
        async url => {
          content.postMessage({ type: "window-open", url }, "*");
        }
      );

      await networkPromise;
      let newTab = await newTabPromise;

      // Verify if the query string is not stripped in the new opened tab.
      await verifyQueryString(newTab.linkedBrowser, expected);

      BrowserTestUtils.removeTab(newTab);
    });

    // 3. Test the allow list for link click
    info("Run link click test.");
    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Observe the channel and check if the query string is not stripped.
      let networkPromise = observeChannel(testURI, expected);

      // Create the promise to wait for the location change.
      let locationChangePromise = BrowserTestUtils.waitForLocationChange(
        gBrowser,
        testURIWithQueryString
      );

      await SpecialPowers.spawn(
        browser,
        [testURIWithQueryString],
        async url => {
          let link = content.document.createElement("a");
          link.setAttribute("href", url);
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        }
      );

      await networkPromise;
      await locationChangePromise;

      // Verify the query string is not stripped in the content window.
      await verifyQueryString(browser, expected);
    });

    // 4. Test the allow list for clicking link in an iframe.
    info("Run link click in iframe test.");
    await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
      // Create an iframe and wait until it has been loaded.
      let iframeBC = await SpecialPowers.spawn(
        browser,
        [TEST_URI],
        async url => {
          let frame = content.document.createElement("iframe");
          content.document.body.appendChild(frame);

          await new Promise(done => {
            frame.addEventListener(
              "load",
              function () {
                done();
              },
              { capture: true, once: true }
            );

            frame.setAttribute("src", url);
          });

          return frame.browsingContext;
        }
      );

      // Observe the channel and check if the query string is not stripped.
      let networkPromise = observeChannel(testURI, expected);

      // Create the promise to wait for the new tab.
      let newTabPromise = BrowserTestUtils.waitForNewTab(
        gBrowser,
        testURIWithQueryString
      );

      // Create a same-site link which has '_blank' as target in the iframe
      // and click it to navigate.
      await SpecialPowers.spawn(
        iframeBC,
        [testURIWithQueryString],
        async uri => {
          let link = content.document.createElement("a");
          link.setAttribute("href", uri);
          link.setAttribute("target", "_blank");
          link.textContent = "Link";
          content.document.body.appendChild(link);
          link.click();
        }
      );

      await networkPromise;
      let newOpenedTab = await newTabPromise;

      // Verify the query string is not stripped in the content window.
      await verifyQueryString(newOpenedTab.linkedBrowser, expected);
      BrowserTestUtils.removeTab(newOpenedTab);

      // 5. Test the allow list for script navigation.
      info("Run script navigation test.");
      await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
        // Observe the channel and check if the query string is not stripped.
        let networkPromise = observeChannel(testURI, expected);

        // Create the promise to wait for the location change.
        let locationChangePromise = BrowserTestUtils.waitForLocationChange(
          gBrowser,
          testURIWithQueryString
        );

        await SpecialPowers.spawn(
          browser,
          [testURIWithQueryString],
          async url => {
            content.postMessage({ type: "script", url }, "*");
          }
        );

        await networkPromise;
        await locationChangePromise;

        // Verify the query string is not stripped in the content window.
        await verifyQueryString(browser, expected);
      });

      // 6. Test the allow list for redirect.
      info("Run redirect test.");
      await BrowserTestUtils.withNewTab(TEST_URI, async browser => {
        // Observe the channel and check if the query string is not stripped.
        let networkPromise = observeChannel(testURI, expected);

        // Create the promise to wait for the location change.
        let locationChangePromise = BrowserTestUtils.waitForLocationChange(
          gBrowser,
          testURIWithQueryString
        );

        let testRedirectURI = `${TEST_REDIRECT_URI}?${testURI}?${expected}`;

        // Trigger the redirect.
        await SpecialPowers.spawn(browser, [testRedirectURI], async url => {
          content.postMessage({ type: "script", url }, "*");
        });

        await networkPromise;
        await locationChangePromise;

        // Verify the query string in the content window.
        await verifyQueryString(browser, expected);
      });
    });
  }
});
