const { NetUtil } = ChromeUtils.importESModule(
  "resource://gre/modules/NetUtil.sys.mjs"
);
const { HttpServer } = ChromeUtils.importESModule(
  "resource://testing-common/httpd.sys.mjs"
);

const ReferrerInfo = Components.Constructor(
  "@mozilla.org/referrer-info;1",
  "nsIReferrerInfo",
  "init"
);

var server = new HttpServer();
server.registerPathHandler("/image.png", imageHandler);
server.start(-1);

/* import-globals-from image_load_helpers.js */
load("image_load_helpers.js");

var gHits = 0;

var gIoService = Services.io;
var gPublicLoader = Cc["@mozilla.org/image/loader;1"].createInstance(
  Ci.imgILoader
);
var gPrivateLoader = Cc["@mozilla.org/image/loader;1"].createInstance(
  Ci.imgILoader
);
gPrivateLoader.QueryInterface(Ci.imgICache).respectPrivacyNotifications();

var nonPrivateLoadContext = Cu.createLoadContext();
var privateLoadContext = Cu.createPrivateLoadContext();

function imageHandler(metadata, response) {
  gHits++;
  response.setHeader("Cache-Control", "max-age=10000", false);
  response.setStatusLine(metadata.httpVersion, 200, "OK");
  response.setHeader("Content-Type", "image/png", false);
  var body = atob(
    "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
  );
  response.bodyOutputStream.write(body, body.length);
}

var requests = [];
var listeners = [];

var gImgPath = "http://localhost:" + server.identity.primaryPort + "/image.png";

function setup_chan(path, isPrivate, callback) {
  var uri = NetUtil.newURI(gImgPath);
  var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
  var principal = Services.scriptSecurityManager.createContentPrincipal(uri, {
    privateBrowsingId: isPrivate ? 1 : 0,
  });
  var chan = NetUtil.newChannel({
    uri,
    loadingPrincipal: principal,
    securityFlags,
    contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE,
  });
  chan.notificationCallbacks = isPrivate
    ? privateLoadContext
    : nonPrivateLoadContext;
  var channelListener = new ChannelListener();
  chan.asyncOpen(channelListener);

  var listener = new ImageListener(null, callback);
  var outlistener = {};
  var loader = isPrivate ? gPrivateLoader : gPublicLoader;
  var outer = Cc["@mozilla.org/image/tools;1"]
    .getService(Ci.imgITools)
    .createScriptedObserver(listener);
  listeners.push(outer);
  requests.push(
    loader.loadImageWithChannelXPCOM(chan, outer, null, outlistener)
  );
  channelListener.outputListener = outlistener.value;
  listener.synchronous = false;
}

function loadImage(isPrivate, callback) {
  var listener = new ImageListener(null, callback);
  var outer = Cc["@mozilla.org/image/tools;1"]
    .getService(Ci.imgITools)
    .createScriptedObserver(listener);
  var uri = gIoService.newURI(gImgPath);
  var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(
    Ci.nsILoadGroup
  );
  loadGroup.notificationCallbacks = isPrivate
    ? privateLoadContext
    : nonPrivateLoadContext;
  var loader = isPrivate ? gPrivateLoader : gPublicLoader;
  var referrerInfo = new ReferrerInfo(
    Ci.nsIReferrerInfo.NO_REFERRER_WHEN_DOWNGRADE,
    true,
    null
  );
  requests.push(
    loader.loadImageXPCOM(
      uri,
      null,
      referrerInfo,
      null,
      loadGroup,
      outer,
      null,
      0,
      null
    )
  );
  listener.synchronous = false;
}

function run_loadImage_tests() {
  function observer() {
    Services.obs.removeObserver(observer, "cacheservice:empty-cache");
    gHits = 0;
    loadImage(false, function () {
      loadImage(false, function () {
        loadImage(true, function () {
          loadImage(true, function () {
            Assert.equal(gHits, 2);
            server.stop(do_test_finished);
          });
        });
      });
    });
  }

  for (let loader of [gPublicLoader, gPrivateLoader]) {
    loader.QueryInterface(Ci.imgICache).clearCache(true);
    loader.QueryInterface(Ci.imgICache).clearCache(false);
  }
  Services.obs.addObserver(observer, "cacheservice:empty-cache");
  let cs = Services.cache2;
  cs.clear();
}

function cleanup() {
  for (var i = 0; i < requests.length; ++i) {
    requests[i].cancelAndForgetObserver(0);
  }
}

function run_test() {
  registerCleanupFunction(cleanup);

  do_test_pending();

  Services.prefs.setBoolPref("network.http.rcwn.enabled", false);

  // We create a public channel that loads an image, then an identical
  // one that should cause a cache read. We then create a private channel
  // and load the same image, and do that a second time to ensure a cache
  // read. In total, we should cause two separate http responses to occur,
  // since the private channels shouldn't be able to use the public cache.
  setup_chan("/image.png", false, function () {
    setup_chan("/image.png", false, function () {
      setup_chan("/image.png", true, function () {
        setup_chan("/image.png", true, function () {
          Assert.equal(gHits, 2);
          run_loadImage_tests();
        });
      });
    });
  });
}
