<!DOCTYPE HTML>
<html>
<head>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
  <title>Test for Cross Site XMLHttpRequest</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="initTest()">
<p id="display">
<iframe id=loader></iframe>
</p>
<div id="content" style="display: none">

</div>
<pre id="test">
<script class="testbody" type="application/javascript">

const runPreflightTests = 1;
const runCookieTests = 1;
const runRedirectTests = 1;

var gen;

function initTest() {
  SimpleTest.waitForExplicitFinish();
  // Allow all cookies, then do the actual test initialization
  SpecialPowers.pushPrefEnv({
    "set": [
      ["network.cookie.cookieBehavior", 0],
      // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
      ["network.cookie.sameSite.laxByDefault", false],
      ["network.cors_preflight.authorization_covered_by_wildcard", false],
	]
  }, initTestCallback);
}

function initTestCallback() {
  window.addEventListener("message", function(e) {
    gen.next(e.data);
  });

  gen = runTest();

  gen.next()
}

// eslint-disable-next-line complexity
function* runTest() {
  var loader = document.getElementById('loader');
  var loaderWindow = loader.contentWindow;
  loader.onload = function () { gen.next() };

  // Test preflight-less requests
  basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
  baseURL = "http://mochi.test:8888" + basePath;

  // Test preflighted requests
  loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
  origin = "http://example.org";
  yield undefined;

  tests =     [// Plain request
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
               },

               // undefined username
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 username: undefined
               },

               // undefined username and password
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 username: undefined,
                 password: undefined
               },

               // nonempty username
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 username: "user",
               },

               // nonempty password
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 password: "password",
               },

               // Default allowed headers
               { pass: 1,
                 method: "GET",
                 headers: { "Content-Type": "text/plain",
                            "Accept": "foo/bar",
                            "Accept-Language": "sv-SE" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "GET",
                 headers: { "Content-Type": "foo/bar",
                            "Accept": "foo/bar",
                            "Accept-Language": "sv-SE" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "GET",
                 headers: { "Content-Type": "foo/bar, text/plain" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "GET",
                 headers: { "Content-Type": "foo/bar, text/plain, garbage" },
                 noAllowPreflight: 1,
               },

               // Custom headers
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "X-My-Header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
                 allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my%-header": "myValue" },
                 allowHeaders: "x-my%-header",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "" },
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "y-my-header",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header y-my-header",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header, y-my-header z",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header, y-my-he(ader",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "myheader": "" },
                 allowMethods: "myheader",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "User-Agent": "myValue" },
                 allowHeaders: "User-Agent",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "User-Agent": "myValue" },
               },

               // Multiple custom headers
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue",
                            "third-header": "thirdValue" },
                 allowHeaders: "x-my-header, second-header, third-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue",
                            "third-header": "thirdValue" },
                 allowHeaders: "x-my-header,second-header,third-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue",
                            "third-header": "thirdValue" },
                 allowHeaders: "x-my-header ,second-header ,third-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue",
                            "third-header": "thirdValue" },
                 allowHeaders: "x-my-header , second-header , third-header",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue" },
                 allowHeaders: ",  x-my-header, , ,, second-header, ,   ",
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "second-header": "secondValue" },
                 allowHeaders: "x-my-header, second-header, unused-header",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "myValue",
                            "y-my-header": "secondValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "",
                            "y-my-header": "" },
                 allowHeaders: "x-my-header",
               },

               // HEAD requests
               { pass: 1,
                 method: "HEAD",
                 noAllowPreflight: 1,
               },

               // HEAD with safe headers
               { pass: 1,
                 method: "HEAD",
                 headers: { "Content-Type": "text/plain",
                            "Accept": "foo/bar",
                            "Accept-Language": "sv-SE" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "Content-Type": "foo/bar",
                            "Accept": "foo/bar",
                            "Accept-Language": "sv-SE" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "Content-Type": "foo/bar, text/plain" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "Content-Type": "foo/bar, text/plain, garbage" },
                 noAllowPreflight: 1,
               },

               // HEAD with custom headers
               { pass: 1,
                 method: "HEAD",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "x-my-header": "myValue" },
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "",
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "y-my-header",
               },
               { pass: 0,
                 method: "HEAD",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header y-my-header",
               },

               // POST tests
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 noAllowPreflight: 1,
               },
               { pass: 1,
                 method: "POST",
               },
               { pass: 1,
                 method: "POST",
                 noAllowPreflight: 1,
               },

               // POST with standard headers
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "text/plain" },
                 noAllowPreflight: 1,
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "multipart/form-data" },
                 noAllowPreflight: 1,
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "application/x-www-form-urlencoded" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar" },
               },
               { pass: 0,
                 method: "POST",
                 headers: { "Content-Type": "foo/bar" },
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "text/plain",
                            "Accept": "foo/bar",
                            "Accept-Language": "sv-SE" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar, text/plain" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar, text/plain, garbage" },
                 noAllowPreflight: 1,
               },

               // POST with custom headers
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Accept": "foo/bar",
                            "Accept-Language": "sv-SE",
                            "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 headers: { "Content-Type": "text/plain",
                            "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "text/plain",
                            "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar",
                            "x-my-header": "myValue" },
                 allowHeaders: "x-my-header, content-type",
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar" },
                 noAllowPreflight: 1,
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar",
                            "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "x-my-header, $_%",
               },

               // Test cases for "Access-Control-Allow-Headers" containing "*".
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "*",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue",
                            "Authorization": "12345" },
                 allowHeaders: "*, Authorization",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue",
                            "Authorization": "12345" },
                 allowHeaders: "Authorization, *",
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue",
                            "Authorization": "12345" },
                 allowHeaders: "*",
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue",
                            "Authorization": "12345" },
                 allowHeaders: "x-my-header",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "*": "myValue" },
                 allowHeaders: "*",
                 withCred: 1,
                 allowCred: 1,
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "x-my-header": "myValue" },
                 allowHeaders: "*",
                 withCred: 1,
                 allowCred: 1,
               },

               // Other methods
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "DELETE",
               },
               { pass: 0,
                 method: "DELETE",
                 allowHeaders: "DELETE",
               },
               { pass: 0,
                 method: "DELETE",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "POST, PUT, DELETE",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "POST, DELETE, PUT",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "DELETE, POST, PUT",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "POST ,PUT ,DELETE",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "POST,PUT,DELETE",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "POST , PUT , DELETE",
               },
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "  ,,  PUT ,,  ,    , DELETE  ,  ,",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "PUT",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "DELETEZ",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "DELETE PUT",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "DELETE, PUT Z",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "DELETE, PU(T",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "PUT DELETE",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "PUT Z, DELETE",
               },
               { pass: 0,
                 method: "DELETE",
                 allowMethods: "PU(T, DELETE",
               },
               { pass: 0,
                 method: "MYMETHOD",
                 allowMethods: "myMethod",
               },
               { pass: 0,
                 method: "PUT",
                 allowMethods: "put",
               },

               // Progress events
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "text/plain" },
                 uploadProgress: "progress",
               },
               { pass: 0,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "text/plain" },
                 uploadProgress: "progress",
                 noAllowPreflight: 1,
               },

               // Status messages
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 status: 404,
                 statusMessage: "nothin' here",
               },
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 status: 401,
                 statusMessage: "no can do",
               },
               { pass: 1,
                 method: "POST",
                 body: "hi there",
                 headers: { "Content-Type": "foo/bar" },
                 allowHeaders: "content-type",
                 status: 500,
                 statusMessage: "server boo",
               },
               { pass: 1,
                 method: "GET",
                 noAllowPreflight: 1,
                 status: 200,
                 statusMessage: "Yes!!",
               },
               { pass: 0,
                 method: "GET",
                 headers: { "x-my-header": "header value" },
                 allowHeaders: "x-my-header",
                 preflightStatus: 400
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "header value" },
                 allowHeaders: "x-my-header",
                 preflightStatus: 200
               },
               { pass: 1,
                 method: "GET",
                 headers: { "x-my-header": "header value" },
                 allowHeaders: "x-my-header",
                 preflightStatus: 204
               },

               // exposed headers
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "x-my-header",
                 expectedResponseHeaders: ["x-my-header"],
               },
               { pass: 0,
                 method: "GET",
                 origin: "http://invalid",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "x-my-header",
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "x-my-header y",
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "y x-my-header",
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "x-my-header, y-my-header z",
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header" },
                 exposeHeaders: "x-my-header, y-my-hea(er",
                 expectedResponseHeaders: [],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "x-my-header": "x header",
                                    "y-my-header": "y header" },
                 exposeHeaders: "  ,  ,,y-my-header,z-my-header,  ",
                 expectedResponseHeaders: ["y-my-header"],
               },
               { pass: 1,
                 method: "GET",
                 responseHeaders: { "Cache-Control": "cacheControl header",
                                    "Content-Language": "contentLanguage header",
                                    "Expires":"expires header",
                                    "Last-Modified":"lastModified header",
                                    "Pragma":"pragma header",
                                    "Unexpected":"unexpected header" },
                 expectedResponseHeaders: ["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"],
               },
               // Check that sending a body in the OPTIONS response works
               { pass: 1,
                 method: "DELETE",
                 allowMethods: "DELETE",
                 preflightBody: "I'm a preflight response body",
               },
               ];

  if (!runPreflightTests) {
    tests = [];
  }

  for (test of tests) {
    var req = {
      url: baseURL + "allowOrigin=" + escape(test.origin || origin),
      method: test.method,
      headers: test.headers,
      uploadProgress: test.uploadProgress,
      body: test.body,
      responseHeaders: test.responseHeaders,
      withCred: test.withCred ? test.withCred : 0,
    };

    if (test.pass) {
       req.url += "&origin=" + escape(origin) +
                  "&requestMethod=" + test.method;
    }

    if ("username" in test) {
      req.username = test.username;
    }

    if ("password" in test) {
      req.password = test.password;
    }

    if (test.noAllowPreflight)
      req.url += "&noAllowPreflight";

    if (test.allowCred)
      req.url += "&allowCred";

    if (test.pass && "headers" in test) {
      function isUnsafeHeader(name) {
        lName = name.toLowerCase();
        return lName != "accept" &&
               lName != "accept-language" &&
               (lName != "content-type" ||
                !["text/plain",
                 "multipart/form-data",
                 "application/x-www-form-urlencoded"]
                   .includes(test.headers[name].toLowerCase()));
      }
      req.url += "&headers=" + escape(JSON.stringify(test.headers));
      reqHeaders =
        escape(Object.keys(test.headers)
               .filter(isUnsafeHeader)
               .map(s => s.toLowerCase())
               .sort()
               .join(","));
      req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
    }
    if ("allowHeaders" in test)
      req.url += "&allowHeaders=" + escape(test.allowHeaders);
    if ("allowMethods" in test)
      req.url += "&allowMethods=" + escape(test.allowMethods);
    if (test.body)
      req.url += "&body=" + escape(test.body);
    if (test.status) {
      req.url += "&status=" + test.status;
      req.url += "&statusMessage=" + escape(test.statusMessage);
    }
    if (test.preflightStatus)
      req.url += "&preflightStatus=" + test.preflightStatus;
    if (test.responseHeaders)
      req.url += "&responseHeaders=" + escape(JSON.stringify(test.responseHeaders));
    if (test.exposeHeaders)
      req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
    if (test.preflightBody)
      req.url += "&preflightBody=" + escape(test.preflightBody);

    loaderWindow.postMessage(JSON.stringify(req), origin);
    res = JSON.parse(yield);

    if (test.pass) {
      is(res.didFail, false,
        "shouldn't have failed in test for " + JSON.stringify(test));
      if (test.status) {
        is(res.status, test.status, "wrong status in test for " + JSON.stringify(test));
        is(res.statusText, test.statusMessage, "wrong status text for " + JSON.stringify(test));
      }
      else {
        is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
        is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
      }
      if (test.method !== "HEAD") {
        is(res.responseXML, "<res>hello pass</res>",
           "wrong responseXML in test for " + JSON.stringify(test));
        is(res.responseText, "<res>hello pass</res>\n",
           "wrong responseText in test for " + JSON.stringify(test));
        is(res.events.join(","),
           "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
           "wrong responseText in test for " + JSON.stringify(test));
      }
      else {
        is(res.responseXML, null,
           "wrong responseXML in test for " + JSON.stringify(test));
        is(res.responseText, "",
           "wrong responseText in test for " + JSON.stringify(test));
        is(res.events.join(","),
           "opening,rs1,sending,loadstart,rs2,rs4,load,loadend",
           "wrong responseText in test for " + JSON.stringify(test));
      }
      if (test.responseHeaders) {
        for (header in test.responseHeaders) {
          if (!test.expectedResponseHeaders.includes(header)) {
            is(res.responseHeaders[header], null,
               "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
               JSON.stringify(test));
            is(res.allResponseHeaders[header], undefined,
              "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
              JSON.stringify(test));
          }
          else {
            is(res.responseHeaders[header], test.responseHeaders[header],
               "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
               JSON.stringify(test));
            is(res.allResponseHeaders[header.toLowerCase()], test.responseHeaders[header],
              "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
              JSON.stringify(test));
          }
        }
      }
    }
    else {
      is(res.didFail, true,
        "should have failed in test for " + JSON.stringify(test));
      is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
      is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
      is(res.responseXML, null,
         "wrong responseXML in test for " + JSON.stringify(test));
      is(res.responseText, "",
         "wrong responseText in test for " + JSON.stringify(test));
      if (!res.sendThrew) {
        is(res.events.join(","),
           "opening,rs1,sending,loadstart,rs4,error,loadend",
           "wrong events in test for " + JSON.stringify(test));
      }
      is(res.progressEvents, 0,
         "wrong events in test for " + JSON.stringify(test));
      if (test.responseHeaders) {
        for (header in test.responseHeaders) {
          is(res.responseHeaders[header], null,
             "wrong response header (" + header + ") in test for " +
             JSON.stringify(test));
        }
      }
    }
  }

  // Test cookie behavior
  tests = [{ pass: 1,
             method: "GET",
             withCred: 1,
             allowCred: 1,
           },
           { pass: 0,
             method: "GET",
             withCred: 1,
             allowCred: 0,
           },
           { pass: 0,
             method: "GET",
             withCred: 1,
             allowCred: 1,
             origin: "*",
           },
           { pass: 1,
             method: "GET",
             withCred: 0,
             allowCred: 1,
             origin: "*",
           },
           { pass: 1,
             method: "GET",
             setCookie: "a=1",
             withCred: 1,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             cookie: "a=1",
             withCred: 1,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             noCookie: 1,
             withCred: 0,
             allowCred: 1,
           },
           { pass: 0,
             method: "GET",
             noCookie: 1,
             withCred: 1,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             setCookie: "a=2",
             withCred: 0,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             cookie: "a=1",
             withCred: 1,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             setCookie: "a=2",
             withCred: 1,
             allowCred: 1,
           },
           { pass: 1,
             method: "GET",
             cookie: "a=2",
             withCred: 1,
             allowCred: 1,
           },
           ];

  if (!runCookieTests) {
    tests = [];
  }

  for (test of tests) {
    req = {
      url: baseURL + "allowOrigin=" + escape(test.origin || origin),
      method: test.method,
      headers: test.headers,
      withCred: test.withCred,
    };

    if (test.allowCred)
      req.url += "&allowCred";

    if (test.setCookie)
      req.url += "&setCookie=" + escape(test.setCookie);
    if (test.cookie)
      req.url += "&cookie=" + escape(test.cookie);
    if (test.noCookie)
      req.url += "&noCookie";

    if ("allowHeaders" in test)
      req.url += "&allowHeaders=" + escape(test.allowHeaders);
    if ("allowMethods" in test)
      req.url += "&allowMethods=" + escape(test.allowMethods);

    loaderWindow.postMessage(JSON.stringify(req), origin);

    res = JSON.parse(yield);
    if (test.pass) {
      is(res.didFail, false,
        "shouldn't have failed in test for " + JSON.stringify(test));
      is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
      is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
      is(res.responseXML, "<res>hello pass</res>",
         "wrong responseXML in test for " + JSON.stringify(test));
      is(res.responseText, "<res>hello pass</res>\n",
         "wrong responseText in test for " + JSON.stringify(test));
      is(res.events.join(","),
         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
         "wrong responseText in test for " + JSON.stringify(test));
    }
    else {
      is(res.didFail, true,
        "should have failed in test for " + JSON.stringify(test));
      is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
      is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
      is(res.responseXML, null,
         "wrong responseXML in test for " + JSON.stringify(test));
      is(res.responseText, "",
         "wrong responseText in test for " + JSON.stringify(test));
      is(res.events.join(","),
         "opening,rs1,sending,loadstart,rs4,error,loadend",
         "wrong events in test for " + JSON.stringify(test));
      is(res.progressEvents, 0,
         "wrong events in test for " + JSON.stringify(test));
    }
  }

  // Make sure to clear cookies to avoid affecting other tests
  document.cookie = "a=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT"
  is(document.cookie, "", "No cookies should be left over");


  // Test redirects
  is(loader.src, "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
  is(origin, "http://example.org");

  tests = [{ pass: 1,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://example.org",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://example.org",
                      allowOrigin: "*"
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://example.org",
                    },
                    ],
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://example.org",
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: origin
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: origin
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: origin
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: "*"
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: "*"
                    },
                    ],
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: "*"
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: "*"
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: "*"
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: origin
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: "x"
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: origin
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: "*"
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin
                    },
                    ],
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin
                    },
                    { server: "http://test2.example.org:8000",
                      allowOrigin: origin
                    },
                    { server: "http://sub2.xn--lt-uia.example.org",
                      allowOrigin: "*"
                    },
                    { server: "http://sub1.test1.example.org",
                    },
                    ],
           },
           { pass: 1,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain" },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                    },
                    ],
           },
           { pass: 1,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 1,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: "*",
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 1,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    { server: "http://example.org",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      noAllowPreflight: 1,
                    },
                    ],
           },
           { pass: 1,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    ],
           },
           { pass: 0,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    ],
           },
           { pass: 1,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: "*",
                      allowMethods: "DELETE",
                    },
                    ],
           },
           { pass: 1,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    ],
           },
           { pass: 0,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    { server: "http://example.org",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                    },
                    ],
           },
           { pass: 0,
             method: "DELETE",
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowMethods: "DELETE",
                      noAllowPreflight: 1,
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.com",
                      allowOrigin: origin,
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin,
                    },
                    ],
           },
           { pass: 0,
             method: "DELETE",
             hops: [{ server: "http://example.com",
                      allowOrigin: origin,
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin,
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.com",
                    },
                    { server: "http://sub1.test1.example.org",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },
           { pass: 1,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain" },
             hops: [{ server: "http://example.org",
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                    },
                    ],
           },
           { pass: 0,
             method: "POST",
             body: "hi there",
             headers: { "Content-Type": "text/plain",
                        "my-header": "myValue",
                      },
             hops: [{ server: "http://example.com",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    { server: "http://example.org",
                      allowOrigin: origin,
                      allowHeaders: "my-header",
                    },
                    ],
           },

           // test redirects with different credentials settings
           {
             // Initialize by setting a cookies for same- and cross- origins.
             pass: 1,
             method: "GET",
             hops: [{ server: origin,
                      setCookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowCred: 1,
                      setCookie: escape("a=2"),
                    },
                    ],
             withCred: 1,
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      noCookie: 1,
                    },
                    ],
             withCred: 0,
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      allowCred: 1,
                      cookie: escape("a=2"),
                    },
                    ],
             withCred: 1,
           },
           // expected fail because allow-credentials CORS header is not set
           { pass: 0,
             method: "GET",
             hops: [{ server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: origin,
                      cookie: escape("a=2"),
                    },
                    ],
             withCred: 1,
           },
           { pass: 1,
             method: "GET",
             hops: [{ server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: '*',
                      noCookie: 1,
                    },
                    ],
             withCred: 0,
           },
           { pass: 0,
             method: "GET",
             hops: [{ server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: origin,
                      cookie: escape("a=1"),
                    },
                    { server: "http://example.com",
                      allowOrigin: '*',
                      allowCred: 1,
                      cookie: escape("a=2"),
                    },
                    ],
             withCred: 1,
           },
           ];

  if (!runRedirectTests) {
    tests = [];
  }

  for (test of tests) {
    req = {
      url: test.hops[0].server + basePath + "hop=1&hops=" +
           escape(JSON.stringify(test.hops)),
      method: test.method,
      headers: test.headers,
      body: test.body,
      withCred: test.withCred,
    };

    if (test.pass) {
      if (test.body)
        req.url += "&body=" + escape(test.body);
    }

    loaderWindow.postMessage(JSON.stringify(req), origin);

    res = JSON.parse(yield);
    if (test.pass) {
      is(res.didFail, false,
        "shouldn't have failed in test for " + JSON.stringify(test));
      is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
      is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
      is(res.responseXML, "<res>hello pass</res>",
         "wrong responseXML in test for " + JSON.stringify(test));
      is(res.responseText, "<res>hello pass</res>\n",
         "wrong responseText in test for " + JSON.stringify(test));
      is(res.events.join(","),
         "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
         "wrong responseText in test for " + JSON.stringify(test));
    }
    else {
      is(res.didFail, true,
        "should have failed in test for " + JSON.stringify(test));
      is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
      is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
      is(res.responseXML, null,
         "wrong responseXML in test for " + JSON.stringify(test));
      is(res.responseText, "",
         "wrong responseText in test for " + JSON.stringify(test));
      is(res.events.join(","),
         "opening,rs1,sending,loadstart,rs4,error,loadend",
         "wrong events in test for " + JSON.stringify(test));
      is(res.progressEvents, 0,
         "wrong progressevents in test for " + JSON.stringify(test));
    }
  }


  SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault");
  SpecialPowers.clearUserPref("browser.contentblocking.category");
  SimpleTest.finish();
}

</script>
</pre>
</body>
</html>
