/*
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";

// This must be loaded in the remote process for this test to be useful
const TEST_URL = "https://example.com/browser/netwerk/test/browser/dummy.html";

const resProtocol = Cc[
  "@mozilla.org/network/protocol;1?name=resource"
].getService(Ci.nsIResProtocolHandler);

function waitForEvent(obj, name, capturing, chromeEvent) {
  info("Waiting for " + name);
  return new Promise(resolve => {
    function listener(event) {
      info("Saw " + name);
      obj.removeEventListener(name, listener, capturing, chromeEvent);
      resolve(event);
    }

    obj.addEventListener(name, listener, capturing, chromeEvent);
  });
}

function resolveURI(uri) {
  uri = Services.io.newURI(uri);
  try {
    return resProtocol.resolveURI(uri);
  } catch (e) {
    return null;
  }
}

function remoteResolveURI(uri) {
  return SpecialPowers.spawn(gBrowser.selectedBrowser, [uri], uriToResolve => {
    let resProtocol = Cc[
      "@mozilla.org/network/protocol;1?name=resource"
    ].getService(Ci.nsIResProtocolHandler);

    uriToResolve = Services.io.newURI(uriToResolve);
    try {
      return resProtocol.resolveURI(uriToResolve);
    } catch (e) {}
    return null;
  });
}

// Restarts the child process by crashing it then reloading the tab
var restart = async function () {
  let browser = gBrowser.selectedBrowser;
  // If the tab isn't remote this would crash the main process so skip it
  if (!browser.hasAttribute("remote")) {
    return browser;
  }

  await BrowserTestUtils.crashFrame(browser);

  browser.reload();

  await BrowserTestUtils.browserLoaded(browser);
  is(
    browser.hasAttribute("remote"),
    gMultiProcessBrowser,
    "Browser should be in the right process"
  );
  return browser;
};

// Sanity check that this test is going to be useful
add_task(async function () {
  await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  // This must be loaded in the remote process for this test to be useful
  is(
    gBrowser.selectedBrowser.hasAttribute("remote"),
    gMultiProcessBrowser,
    "Browser should be in the right process"
  );

  let local = resolveURI("resource://gre/modules/AppConstants.sys.mjs");
  let remote = await remoteResolveURI(
    "resource://gre/modules/AppConstants.sys.mjs"
  );
  is(local, remote, "AppConstants.sys.mjs should resolve in both processes");

  gBrowser.removeCurrentTab();
});

// Add a mapping, update it then remove it
add_task(async function () {
  await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  info("Set");
  resProtocol.setSubstitution(
    "testing",
    Services.io.newURI("chrome://global/content")
  );
  let local = resolveURI("resource://testing/test.js");
  let remote = await remoteResolveURI("resource://testing/test.js");
  is(
    local,
    "chrome://global/content/test.js",
    "Should resolve in main process"
  );
  is(
    remote,
    "chrome://global/content/test.js",
    "Should resolve in child process"
  );

  info("Change");
  resProtocol.setSubstitution(
    "testing",
    Services.io.newURI("chrome://global/skin")
  );
  local = resolveURI("resource://testing/test.js");
  remote = await remoteResolveURI("resource://testing/test.js");
  is(local, "chrome://global/skin/test.js", "Should resolve in main process");
  is(remote, "chrome://global/skin/test.js", "Should resolve in child process");

  info("Clear");
  resProtocol.setSubstitution("testing", null);
  local = resolveURI("resource://testing/test.js");
  remote = await remoteResolveURI("resource://testing/test.js");
  is(local, null, "Shouldn't resolve in main process");
  is(remote, null, "Shouldn't resolve in child process");

  gBrowser.removeCurrentTab();
});

// Add a mapping, restart the child process then check it is still there
add_task(async function () {
  await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  info("Set");
  resProtocol.setSubstitution(
    "testing",
    Services.io.newURI("chrome://global/content")
  );
  let local = resolveURI("resource://testing/test.js");
  let remote = await remoteResolveURI("resource://testing/test.js");
  is(
    local,
    "chrome://global/content/test.js",
    "Should resolve in main process"
  );
  is(
    remote,
    "chrome://global/content/test.js",
    "Should resolve in child process"
  );

  await restart();

  local = resolveURI("resource://testing/test.js");
  remote = await remoteResolveURI("resource://testing/test.js");
  is(
    local,
    "chrome://global/content/test.js",
    "Should resolve in main process"
  );
  is(
    remote,
    "chrome://global/content/test.js",
    "Should resolve in child process"
  );

  info("Change");
  resProtocol.setSubstitution(
    "testing",
    Services.io.newURI("chrome://global/skin")
  );

  await restart();

  local = resolveURI("resource://testing/test.js");
  remote = await remoteResolveURI("resource://testing/test.js");
  is(local, "chrome://global/skin/test.js", "Should resolve in main process");
  is(remote, "chrome://global/skin/test.js", "Should resolve in child process");

  info("Clear");
  resProtocol.setSubstitution("testing", null);

  await restart();

  local = resolveURI("resource://testing/test.js");
  remote = await remoteResolveURI("resource://testing/test.js");
  is(local, null, "Shouldn't resolve in main process");
  is(remote, null, "Shouldn't resolve in child process");

  gBrowser.removeCurrentTab();
});

// Adding a mapping to a resource URI should work
add_task(async function () {
  await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  info("Set");
  resProtocol.setSubstitution(
    "testing",
    Services.io.newURI("chrome://global/content")
  );
  resProtocol.setSubstitution(
    "testing2",
    Services.io.newURI("resource://testing")
  );
  let local = resolveURI("resource://testing2/test.js");
  let remote = await remoteResolveURI("resource://testing2/test.js");
  is(
    local,
    "chrome://global/content/test.js",
    "Should resolve in main process"
  );
  is(
    remote,
    "chrome://global/content/test.js",
    "Should resolve in child process"
  );

  info("Clear");
  resProtocol.setSubstitution("testing", null);
  local = resolveURI("resource://testing2/test.js");
  remote = await remoteResolveURI("resource://testing2/test.js");
  is(
    local,
    "chrome://global/content/test.js",
    "Should resolve in main process"
  );
  is(
    remote,
    "chrome://global/content/test.js",
    "Should resolve in child process"
  );

  resProtocol.setSubstitution("testing2", null);
  local = resolveURI("resource://testing2/test.js");
  remote = await remoteResolveURI("resource://testing2/test.js");
  is(local, null, "Shouldn't resolve in main process");
  is(remote, null, "Shouldn't resolve in child process");

  gBrowser.removeCurrentTab();
});
