<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=435425
-->
<head>
  <title>Test for Bug 435425</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435425">Mozilla Bug 435425</a>
<p id="display"></p>
<div id="content" style="display: none">

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

/** Test for Bug 435425 **/

var upload = null;
var currentEvents = null;
var expectedResponseText = null;
var uploadTotal = 0;
var currentProgress = -1;

function logEvent(evt) {
  var i = 0;
  while ((currentEvents.length != i) &&
         currentEvents[i].optional &&
         ((currentEvents[i].type != evt.type) ||
          !(evt.target instanceof currentEvents[i].target))) {
    ++i;
  }

  if (evt.target instanceof XMLHttpRequestUpload) {
    if (evt.type == "loadstart") {
      uploadTotal = evt.total
    } else {
      if (evt.type == "progress") {
        is(evt.lengthComputable, evt.total != 0, "event(" + evt.type +  ").lengthComputable should be " + (evt.total != 0) + ".");
      }
      if (evt.total != uploadTotal && evt.total != 0) {
        ok(false, "event(" + evt.type +  ").total should not change during upload except to become 0 on error/abort/timeout.");
      }
    }
  }

  // There can be any number of repeated progress events, so special-case this.
  if (evt.type == "progress") {
    // Progress events can repeat, but their "loaded" value must increase.
    if (currentProgress >= 0) {
      ok(currentProgress < evt.loaded, "Progress should increase, got " +
                                       evt.loaded + " after " + currentProgress);
      currentProgress = evt.loaded;
      return; // stay at the currentEvent, since we got progress instead of it.
    }
    // Starting a new progress event group.
    currentProgress = evt.loaded;
  } else {
    // Reset the progress indicator on any other event type.
    currentProgress = -1;
  }

  ok(i != currentEvents.length, "Extra or wrong event?");
  is(evt.type, currentEvents[i].type, "Wrong event!")
  ok(evt.target instanceof currentEvents[i].target,
     "Wrong event target [" + evt.target + "," + evt.type + "]!");

  // If we handled non-optional event, remove all optional events before the
  // handled event and then the non-optional event from the list.
  if (!currentEvents[i].optional) {
    for (;i != -1; --i) {
      currentEvents.shift();
    }
  }
}

function hasPendingNonOptionalEvent(ev) {
  var i = 0;
  while (i < currentEvents.length) {
    if (!currentEvents[i].optional && currentEvents[i].type == ev)
      return true;
    ++i;
  }
  return false;
}

function maybeStop(evt) {
  logEvent(evt);
  if (!hasPendingNonOptionalEvent("loadend"))
    nextTest();
}

function openXHR(xhr, method, url, privileged) {
  if (privileged)
    SpecialPowers.wrap(xhr).open(method, url);
  else
    xhr.open(method, url);
}

function start(obj) {
  let xhr = new XMLHttpRequest();
  upload = xhr.upload;
  currentEvents = obj.expectedEvents;
  expectedResponseText = obj.withUpload;
  currentProgress = -1;
  xhr.onload =
    function(evt) {
      if (expectedResponseText) {
        is(evt.target.responseText, expectedResponseText, "Wrong responseText");
      }
      logEvent(evt);
    }
  xhr.onerror =
    function(evt) {
      logEvent(evt);
    }
  xhr.onabort =
    function(evt) {
      logEvent(evt);
    }
  xhr.onloadend =
    function (evt) {
      maybeStop(evt);
    }
  xhr.onloadstart =
    function (evt) {
      logEvent(evt);
    }
  xhr.onprogress =
    function (evt) {
      logEvent(evt);
    }

  if ("upload" in xhr) {
    xhr.upload.onloadstart =
      function (evt) {
        logEvent(evt);
      }
    xhr.upload.onprogress =
      function (evt) {
        logEvent(evt);
      }
    xhr.upload.onload =
      function (evt) {
        logEvent(evt);
      }
    xhr.upload.onerror =
      function (evt) {
        logEvent(evt);
      }
    xhr.upload.onabort =
      function (evt) {
        logEvent(evt);
      }
    xhr.upload.onloadend =
      function (evt) {
        maybeStop(evt);
      }
  }

  try {
    var methodIsGet = (obj.method == "GET");
    var url;
    var privileged = false;
    if (obj.testRedirectError) {
      url = "bug435425_redirect.sjs";
    } else if (obj.testNetworkError) {
      url = "http://nosuchdomain.localhost";
      privileged = true;
    } else {
      url = "bug435425.sjs";
      if (obj.withUpload && methodIsGet) {
        url += "?" + obj.withUpload;
      }
    }
    openXHR(xhr, obj.method, url, privileged);
    xhr.send(!methodIsGet ? obj.withUpload : null);
    if (obj.testAbort) {
      xhr.abort();
    }
  } catch (ex) {
    ok(false, ex);
  }
}

var none = null;
var small = "";
var mid = "";
var large = "";


for (var smallLength = 0; smallLength < (0x00000000 + 2); ++smallLength) {
  small += "A";
}

for (var midLength = 0; midLength < (0x00000FFF + 2); ++midLength) {
  mid += "A";
}

for (var largeLength = 0; largeLength < (0x0000FFFF + 2); ++largeLength) {
  large += "A";
}

const XHR = XMLHttpRequest;
const UPLOAD = XMLHttpRequestUpload;

var tests =
  [
    { method: "GET", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: none, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: none, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "GET", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: small, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: small, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "GET", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: mid, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: mid, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "GET", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: large, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: large, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "GET", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "POST", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: none, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: none, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: none, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "POST", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: false},
                       {target: UPLOAD, type: "load", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: small, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "abort", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: small, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: small, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "POST", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: false},
                       {target: UPLOAD, type: "load", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: mid, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "abort", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: mid, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: mid, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},

    { method: "POST", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: false},
                       {target: UPLOAD, type: "load", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: false},
                       {target: XHR, type: "load", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: large, testAbort: true, testRedirectError: false, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "abort", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "progress", optional: true},
                       {target: XHR, type: "abort", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: large, testAbort: false, testRedirectError: true, testNetworkError: false,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "progress", optional: true},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
    { method: "POST", withUpload: large, testAbort: false, testRedirectError: false, testNetworkError: true,
      expectedEvents: [{target: XHR, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "loadstart", optional: false},
                       {target: UPLOAD, type: "error", optional: false},
                       {target: UPLOAD, type: "loadend", optional: false},
                       {target: XHR, type: "error", optional: false},
                       {target: XHR, type: "loadend", optional: false}]},
];

function runTest() {
  var test = tests.shift();
  start(test);
}

function nextTest() {
  if (tests.length) {
    setTimeout("runTest()", 0);
  } else {
    SimpleTest.finish();
  }
}

ok("upload" in (new XMLHttpRequest()), "XMLHttpRequest.upload isn't supported!");
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);

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