var tests = [
  // Config is an array with 4 elements:
  // - annotation blocklist
  // - annotation entitylist
  // - tracking blocklist
  // - tracking entitylist

  // All disabled.
  {
    config: [false, false, false, false],
    loadExpected: true,
    annotationExpected: false,
  },

  // Just entitylisted.
  {
    config: [false, false, false, true],
    loadExpected: true,
    annotationExpected: false,
  },

  // Just blocklisted.
  {
    config: [false, false, true, false],
    loadExpected: false,
    annotationExpected: false,
  },

  // entitylist + blocklist => entitylist wins
  {
    config: [false, false, true, true],
    loadExpected: true,
    annotationExpected: false,
  },

  // just annotated in entitylist.
  {
    config: [false, true, false, false],
    loadExpected: true,
    annotationExpected: false,
  },

  // TP and annotation entitylisted.
  {
    config: [false, true, false, true],
    loadExpected: true,
    annotationExpected: false,
  },

  // Annotation entitylisted, but TP blocklisted.
  {
    config: [false, true, true, false],
    loadExpected: false,
    annotationExpected: false,
  },

  // Annotation entitylisted. TP blocklisted and entitylisted: entitylist wins.
  {
    config: [false, true, true, true],
    loadExpected: true,
    annotationExpected: false,
  },

  // Just blocklist annotated.
  {
    config: [true, false, false, false],
    loadExpected: true,
    annotationExpected: true,
  },

  // annotated but TP entitylisted.
  {
    config: [true, false, false, true],
    loadExpected: true,
    annotationExpected: true,
  },

  // annotated and blocklisted.
  {
    config: [true, false, true, false],
    loadExpected: false,
    annotationExpected: false,
  },

  // annotated, TP blocklisted and entitylisted: entitylist wins.
  {
    config: [true, false, true, true],
    loadExpected: true,
    annotationExpected: true,
  },

  // annotated in white and blocklist.
  {
    config: [true, true, false, false],
    loadExpected: true,
    annotationExpected: false,
  },

  // annotated in white and blocklist. TP Whiteslited
  {
    config: [true, true, false, true],
    loadExpected: true,
    annotationExpected: false,
  },

  // everywhere. TP entitylist wins.
  {
    config: [true, true, true, true],
    loadExpected: true,
    annotationExpected: false,
  },
];

function prefBlacklistValue(value) {
  return value ? "example.com" : "";
}

function prefWhitelistValue(value) {
  return value ? "mochi.test,mochi.xorigin-test" : "";
}

async function runTest(test, expectedFlag, expectedTrackingResource, prefs) {
  let config = [
    [
      "urlclassifier.trackingAnnotationTable.testEntries",
      prefBlacklistValue(test.config[0]),
    ],
    [
      "urlclassifier.features.fingerprinting.annotate.blacklistHosts",
      prefBlacklistValue(test.config[0]),
    ],
    [
      "urlclassifier.features.cryptomining.annotate.blacklistHosts",
      prefBlacklistValue(test.config[0]),
    ],
    [
      "urlclassifier.features.socialtracking.annotate.blacklistHosts",
      prefBlacklistValue(test.config[0]),
    ],

    [
      "urlclassifier.trackingAnnotationWhitelistTable.testEntries",
      prefWhitelistValue(test.config[1]),
    ],
    [
      "urlclassifier.features.fingerprinting.annotate.whitelistHosts",
      prefWhitelistValue(test.config[1]),
    ],
    [
      "urlclassifier.features.cryptomining.annotate.whitelistHosts",
      prefWhitelistValue(test.config[1]),
    ],
    [
      "urlclassifier.features.socialtracking.annotate.whitelistHosts",
      prefWhitelistValue(test.config[1]),
    ],

    [
      "urlclassifier.trackingTable.testEntries",
      prefBlacklistValue(test.config[2]),
    ],
    [
      "urlclassifier.features.fingerprinting.blacklistHosts",
      prefBlacklistValue(test.config[2]),
    ],
    [
      "urlclassifier.features.cryptomining.blacklistHosts",
      prefBlacklistValue(test.config[2]),
    ],
    [
      "urlclassifier.features.socialtracking.blacklistHosts",
      prefBlacklistValue(test.config[2]),
    ],

    [
      "urlclassifier.trackingWhitelistTable.testEntries",
      prefWhitelistValue(test.config[3]),
    ],
    [
      "urlclassifier.features.fingerprinting.whitelistHosts",
      prefWhitelistValue(test.config[3]),
    ],
    [
      "urlclassifier.features.cryptomining.whitelistHosts",
      prefWhitelistValue(test.config[3]),
    ],
    [
      "urlclassifier.features.socialtracking.whitelistHosts",
      prefWhitelistValue(test.config[3]),
    ],
  ];

  info("Testing: " + JSON.stringify(config) + "\n");

  await SpecialPowers.pushPrefEnv({ set: config.concat(prefs) });

  // This promise will be resolved when the chromeScript knows if the channel
  // is annotated or not.
  let annotationPromise;
  if (test.loadExpected) {
    info("We want to have annotation information");
    annotationPromise = new Promise(resolve => {
      chromeScript.addMessageListener(
        "last-channel-flags",
        data => resolve(data),
        { once: true }
      );
    });
  }

  // Let's load a script with a random query string to avoid network cache.
  // Using a script as the fingerprinting feature does not block display content
  let result = await new Promise(resolve => {
    let script = document.createElement("script");
    script.setAttribute(
      "src",
      "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
        Math.random()
    );
    script.onload = _ => resolve(true);
    script.onerror = _ => resolve(false);
    document.body.appendChild(script);
  });

  is(result, test.loadExpected, "The loading happened correctly");

  if (annotationPromise) {
    let data = await annotationPromise;
    is(
      !!data.classificationFlags,
      test.annotationExpected,
      "The annotation happened correctly"
    );
    if (test.annotationExpected) {
      is(data.classificationFlags, expectedFlag, "Correct flag");
      is(
        data.isThirdPartyTrackingResource,
        expectedTrackingResource,
        "Tracking resource flag matches"
      );
    }
  }
}

var chromeScript;

function runTests(flag, prefs, trackingResource) {
  chromeScript = SpecialPowers.loadChromeScript(_ => {
    function onExamResp(subject) {
      let channel = subject.QueryInterface(Ci.nsIHttpChannel);
      let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel);
      if (
        !channel ||
        !classifiedChannel ||
        !channel.URI.spec.startsWith(
          "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"
        )
      ) {
        return;
      }

      sendAsyncMessage("last-channel-flags", {
        classificationFlags: classifiedChannel.classificationFlags,
        isThirdPartyTrackingResource:
          classifiedChannel.isThirdPartyTrackingResource(),
      });
    }

    addMessageListener("done", __ => {
      Services.obs.removeObserver(onExamResp, "http-on-examine-response");
    });

    Services.obs.addObserver(onExamResp, "http-on-examine-response");

    sendAsyncMessage("start-test");
  });

  chromeScript.addMessageListener(
    "start-test",
    async _ => {
      for (let test in tests) {
        await runTest(tests[test], flag, trackingResource, prefs);
      }

      chromeScript.sendAsyncMessage("done");
      SimpleTest.finish();
    },
    { once: true }
  );
}
