/* 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 { HttpServer } = ChromeUtils.importESModule(
  "resource://testing-common/httpd.sys.mjs"
);

const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

var httpserver = new HttpServer();
var port;

function make_uri(urlStr) {
  return Services.io.newURI(urlStr);
}

function serverHandler(_metadata, response) {
  const body = "hello world";
  response.setHeader("Content-Type", "text/plain", false);
  response.bodyOutputStream.write(body, body.length);
}

add_setup(async function setup() {
  httpserver.registerPathHandler("/test", serverHandler);
  httpserver.registerPathHandler("/ignore", serverHandler);
  httpserver.start(-1);
  port = httpserver.identity.primaryPort;

  info("Starting test with port " + port);

  registerCleanupFunction(async function () {
    await httpserver.stop();
  });
});

async function load_get_principal(url, oa) {
  let chan = makeHTTPChannel(url);
  chan.loadInfo.originAttributes = oa;
  await new Promise(resolve => {
    chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL));
  });
  return chan.loadInfo.loadingPrincipal;
}

async function test(oaLoad, oaClear, shouldExist) {
  // populate cache entry
  let url = `http://localhost:${port}/test`;
  await load_get_principal(url, oaLoad);

  let clearPrincipal = Services.scriptSecurityManager.createContentPrincipal(
    Services.io.newURI(`http://localhost:${port}/`),
    oaClear
  );
  // sanity check that the originAttributes match
  let expectedOa = { ...clearPrincipal.originAttributes, ...oaClear };
  Assert.deepEqual(clearPrincipal.originAttributes, expectedOa);

  let cache_storage = getCacheStorage(
    "disk",
    Services.loadContextInfo.custom(false, oaLoad)
  );
  let exists = cache_storage.exists(make_uri(url), null);
  Assert.ok(exists, "Entry should be in cache");

  Services.cache2.clearOriginsByPrincipal(clearPrincipal);

  // clearOriginsByPrincipal is async, so we block on cacheIOThread
  await new Promise(resolve => {
    syncWithCacheIOThread(resolve, true);
  });

  await TestUtils.waitForCondition(
    () => {
      let existsAgain = cache_storage.exists(make_uri(url), null);
      // times out when codition isn't met.
      return shouldExist == existsAgain;
    },
    shouldExist ? "Entry stay in cache" : "Entry get cleared from the cache"
  );
}

add_task(async function test_usercontextid_same() {
  await test({ userContextId: 0 }, { userContextId: 0 }, false);
});

add_task(async function test_usercontextid_same_nondefault() {
  await test({ userContextId: 1 }, { userContextId: 1 }, false);
});

add_task(async function test_usercontextid_different_1() {
  await test({ userContextId: 1 }, { userContextId: 0 }, true);
});

add_task(async function test_usercontextid_different_2() {
  await test({ userContextId: 0 }, { userContextId: 1 }, true);
});

add_task(async function test_privatebrowsingid_same() {
  await test({ privateBrowsingId: 1 }, { privateBrowsingId: 1 }, false);
});

add_task(async function test_privatebrowsingid_default() {
  await test({ privateBrowsingId: 0 }, { privateBrowsingId: 1 }, true);
});

add_task(async function test_privatebrowsingid_private() {
  await test({ privateBrowsingId: 1 }, { privateBrowsingId: 0 }, true);
});

add_task(async function test_partitionkey_same() {
  await test(
    { partitionKey: "(https,example.com)" },
    { partitionKey: "(https,example.com)" },
    false
  );
});

add_task(async function test_partitionkey_different_1() {
  await test(
    { partitionKey: "(http,example.com)" },
    { partitionKey: "(https,example.com)" },
    true
  );
});

add_task(async function test_partitionkey_different_2() {
  await test(
    { partitionKey: "(http,example.com)" },
    { partitionKey: "(https,example.com)" },
    true
  );
});

add_task(async function test_partitionkey_nonexisting_firstparty() {
  await test({ partitionKey: `(http,localhost,${port})` }, {}, true);
});

add_task(async function test_partitionkey_firstparty_nonexisting() {
  await test({}, { partitionKey: `(http,localhost,${port})` }, true);
});
