"use strict";

add_setup(() => {
  Services.prefs.setBoolPref("extensions.dnr.feedback", true);
});

async function test_dnr_with_file_initiator(hasFilePermissions) {
  const host_permissions = ["http://example.com/*"];
  if (hasFilePermissions) {
    host_permissions.push("file:///*");
  }
  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      permissions: ["declarativeNetRequest", "declarativeNetRequestFeedback"],
      host_permissions,
    },
    background: async () => {
      const hasFilePermissions = await browser.permissions.contains({
        origins: ["file:///*"],
      });
      const dnr = browser.declarativeNetRequest;
      await dnr.updateSessionRules({
        addRules: [
          {
            id: 1,
            condition: { excludedResourceTypes: [] },
            // "allow" action does not require permissions because of the
            // "declarativeNetRequest" permission, which enables actions such
            // as "block" and "allow" without host permissions.
            action: { type: "allow" },
          },
          {
            id: 2,
            priority: 2,
            condition: { excludedResourceTypes: [] },
            // "modifyHeaders" action requires host permissions to the request
            // URL and the initiator (with exceptions, see below).
            action: {
              type: "modifyHeaders",
              responseHeaders: [{ operation: "set", header: "x", value: "y" }],
            },
          },
        ],
      });

      async function testMatchesRequest(request, ruleIds, description) {
        browser.test.assertDeepEq(
          ruleIds,
          (await dnr.testMatchOutcome(request)).matchedRules.map(m => m.ruleId),
          description
        );
      }

      const url = "http://example.com/";
      if (hasFilePermissions) {
        await testMatchesRequest(
          { url, initiator: "file:///tmp", type: "other" },
          [1, 2],
          "Can modify requests from file:-initiator, with permission"
        );
      } else {
        await testMatchesRequest(
          { url, initiator: "file:///tmp", type: "other" },
          [1],
          "Cannot modify requests from file:-initiator, without permission"
        );
      }

      // main_frame/sub_frame are exempt from initiator checks by design, for
      // context see: https://bugzilla.mozilla.org/show_bug.cgi?id=1825824#c2
      await testMatchesRequest(
        { url, initiator: "file:///tmp", type: "main_frame" },
        [1, 2],
        "Can match requests from file:-initiator for main_frame requests"
      );
      await testMatchesRequest(
        { url, initiator: "file:///tmp", type: "sub_frame" },
        [1, 2],
        "Can match requests from file:-initiator for sub_frame requests"
      );

      // Regardless of initiator, file:-requests themselves cannot be matched by
      // DNR, both by design and in its implementation, which is only hooked up
      // to http(s) requests via NetworkIntegration.startDNREvaluation.
      // testMatchesRequest can artifically match file:-URLs but that is not
      // supported and should be fixed when we fix bug 1827422.
      await testMatchesRequest(
        { url: "file:///", initiator: "file:///tmp", type: "sub_frame" },
        [1], // Ideally [], would be fixed by bug 1827422.
        "Cannot match file:-requests from file."
      );
      await testMatchesRequest(
        { url: "file:///", type: "sub_frame" },
        [1], // Ideally [], would be fixed by bug 1827422.
        "Cannot match file:-requests from file."
      );
      browser.test.notifyPass();
    },
  });
  await extension.startup();
  await extension.awaitFinish();
  await extension.unload();
}

add_task(async function dnr_without_file_permissions() {
  await test_dnr_with_file_initiator(/* hasFilePermissions */ false);
});

add_task(async function dnr_with_file_permissions() {
  await test_dnr_with_file_initiator(/* hasFilePermissions */ true);
});
