/*
 * tests:
 *  1. client tries to connect to a http scheme location;
 *  2. assure serialization of the connections;
 *  3. client tries to connect to an non-existent ws server;
 *  4. client tries to connect using a relative url;
 *  5. client uses an invalid protocol value;
 *  6. counter and encoding check;
 *  7. onmessage event origin property check
 *  8. client calls close() and the server sends the close frame (with no code
 *     or reason) in acknowledgement;
 *  9. client closes the connection before the ws connection is established;
 * 10. client sends a message before the ws connection is established;
 * 11. a simple hello echo;
 * 12. client sends a message containing unpaired surrogates
 * 13. server sends an invalid message;
 * 14. server sends the close frame, it doesn't close the tcp connection and
 *     it keeps sending normal ws messages;
 * 15. server closes the tcp connection, but it doesn't send the close frame;
 * 16. client calls close() and tries to send a message;
 * 17. see bug 572975 - all event listeners set - Only in the main thread tests (requires forcegc)
 * 18. client tries to connect to an http resource;
 * 19. server closes the tcp connection before establishing the ws connection;
 * 20. see bug 572975 - only on error and onclose event listeners set - Only in the main thread worker tests (requires forcegc)
 * 21. see bug 572975 - same as test 17, but delete strong event listeners when
 *     receiving the message event - Only in the main thread worker tests (requires forcegc and window)
 * 22. server takes too long to establish the ws connection - Only in the main thread worker tests (requires SpecialPower object)
 * 23. should detect WebSocket on window object - Only in the main thread worker tests (requires window object)
 * 24. server rejects sub-protocol string
 * 25. ctor with valid empty sub-protocol array
 * 26. ctor with invalid sub-protocol array containing 1 empty element
 * 27. ctor with invalid sub-protocol array containing an empty element in list
 * 28. ctor using valid 1 element sub-protocol array
 * 29. ctor using all valid 5 element sub-protocol array
 * 30. ctor using valid 1 element sub-protocol array with element server will
 *     reject
 * 31. ctor using valid 2 element sub-protocol array with 1 element server
 *     will reject and one server will accept.
 * 32. ctor using invalid sub-protocol array that contains duplicate items
 * 33. test for sending/receiving custom close code (but no close reason)
 * 34. test for receiving custom close code and reason
 * 35. test for sending custom close code and reason
 * 36. negative test for sending out of range close code
 * 37. negative test for too long of a close reason
 * 38. ensure extensions attribute is defined
 * 39. a basic wss:// connectivity test
 * 40. negative test for wss:// with no cert
 * 41. HSTS
 * 42. non-char utf-8 sequences
 * 43. Test setting binaryType attribute
 * 44. Test sending/receiving binary ArrayBuffer
 * 45. Test sending/receiving binary Blob  - Only in the main thread worker tests (requires SpecialPower object)
 * 46. Test that we don't dispatch incoming msgs once in CLOSING state
 * 47. Make sure onerror/onclose aren't called during close()
 */

function feedback(msg)
{
    postMessage({type: 'feedback', msg: msg });
}

function ok(status, msg)
{
  postMessage({type: 'status', status: !!status, msg: msg});
}

function is(a, b, msg)
{
  ok(a == b, msg);
}

var first_test = 1;
var last_test = 47;


// Set this to >1 if you want to run the suite multiple times to probe for
// random orange failures. 
// - Do NOT check into mozilla-central with a value != 1.
// - Too large a count will wind up causing tryserver to timeout the test (which
//   is ok, but means all testruns will be orange).  If I set first_test to >22
//   (i.e don't run any of the tests that require waiting) I can get ~250-300
//   iterations of the remaining tests w/o a timeout.
var testsuite_iterations = 1;   


var current_test = first_test;
var testsuite_iteration = 1;

var test_started = new Array(last_test);
var all_ws = [];

function shouldNotOpen(e)
{
  var ws = e.target;
  ok(false, "onopen shouldn't be called on test " + ws._testNumber + "!");
}

function shouldNotReceiveCloseEvent(e)
{
  var ws = e.target;
  var extendedErrorInfo = "";
  if (!ws._testNumber) {
    extendedErrorInfo += "\nws members:\n";
    for (var i in ws) {
      extendedErrorInfo += (i + ": " + ws[i] + "\n");
    }

    extendedErrorInfo += "\ne members:\n";
    for (var i in e) {
      extendedErrorInfo += (i + ": " + e[i] + "\n");
    }
  }
  
  // FIXME: see bug 578276. This should be a test failure, but it's too flaky on the tbox.
  ok(true, "onclose shouldn't be called on test " + ws._testNumber + "!" + extendedErrorInfo);
}

function shouldCloseCleanly(e)
{
  var ws = e.target;
  ok(e.wasClean, "the ws connection in test " + ws._testNumber + " should be closed cleanly");
}

function shouldCloseNotCleanly(e)
{
  var ws = e.target;
  ok(!e.wasClean, "the ws connection in test " + ws._testNumber + " shouldn't be closed cleanly");
}

function ignoreError(e)
{
}

function CreateTestWS(ws_location, ws_protocol, no_increment_test)
{
  var ws;

  try {
    if (ws_protocol == undefined) {
      ws = new WebSocket(ws_location);
    } else {
      ws = new WebSocket(ws_location, ws_protocol);
    }


    ws._testNumber = current_test;
    ws._receivedCloseEvent = false;
    var infoString = "Created websocket for test " + ws._testNumber;
    if (undefined != ws_protocol) {
      infoString += " " + ws_protocol;
    }
    infoString += "\n";
    ok(true, infoString);

    ws.onerror = function(e)
    {
      ok(false, "onerror called on test " + e.target._testNumber + "!");
    };
    ws.addEventListener("close", function(e)
    {
      ws._receivedCloseEvent = true;
    }, false);
  }
  catch (e) {
    throw e;
  }
  finally {
    if (!no_increment_test) {
      current_test++;
    }
  }

  all_ws.push(ws);
  return ws;
}

function doTest(number)
{
  if (number > last_test) {
    ranAllTests = true;
    maybeFinished();
    return;
  }

  if (testsuite_iteration > 1) {
    feedback("Test suite iteration #" + testsuite_iteration + " of " + testsuite_iterations +
      ": Executing test: " + number + " of " + last_test + " tests.");
  } else {
    feedback("Executing test: " + number + " of " + last_test + " tests.");
  }

  var fnTest = eval("test" + number + "");

  if (test_started[number] === true) {
    doTest(number + 1);
    return;
  }

  test_started[number] = true;
  fnTest();
}
doTest.timeoutId = null;

function test1()
{
  try {
    var ws = CreateTestWS("http://mochi.test:8888/tests/dom/base/test/file_websocket");
    ok(false, "test1 failed");
  }
  catch (e) {
    ok(true, "test1 failed");
  }
  doTest(2);
}

// this test expects that the serialization list to connect to the proxy
// is empty. Use different domain so we can run this in the background
// and not delay other tests.

var waitTest2Part1 = false;
var waitTest2Part2 = false;

function test2()
{
  waitTest2Part1 = true;
  waitTest2Part2 = true;

  var ws1 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.1");
  current_test--; // CreateTestWS incremented this
  var ws2 = CreateTestWS("ws://sub2.test2.example.com/tests/dom/base/test/file_websocket", "test-2.2");

  var ws2CanConnect = false;

  // the server will delay ws1 for 5 seconds, but the other tests can
  // proceed in parallel
  doTest(3);

  ws1.onopen = function()
  {
    ok(true, "ws1 open in test 2");
    ws2CanConnect = true;
    ws1.close();
  }

  ws1.onclose = function(e)
  {
    waitTest2Part1 = false;
    maybeFinished();
  };

  ws2.onopen = function()
  {
    ok(ws2CanConnect, "shouldn't connect yet in test-2!");
    ws2.close();
  }

  ws2.onclose = function(e)
  {
    waitTest2Part2 = false;
    maybeFinished();
  };
}

function test3()
{
  var hasError = false;
  var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist");
  ws.onopen = shouldNotOpen;
  ws.onerror = function (e)
  {
    hasError = true;
  }

  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    ok(hasError, "rcvd onerror event");
    ok(e.code == 1006, "test-3 close code should be 1006 but is:" + e.code);
    doTest(4);
  };
}

function test4()
{
  try {
    var ws = CreateTestWS("file_websocket");
    ok(false, "test-4 failed");
  }
  catch (e) {
    ok(true, "test-4 failed");
  }
  doTest(5);
}

function test5()
{
  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "");
    ok(false, "couldn't accept an empty string in the protocol parameter");
  }
  catch (e) {
    ok(true, "couldn't accept an empty string in the protocol parameter");
  }
  current_test--; // CreateTestWS incremented this
  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "\n");
    ok(false, "couldn't accept any not printable ASCII character in the protocol parameter");
  }
  catch (e) {
    ok(true, "couldn't accept any not printable ASCII character in the protocol parameter");
  }
  current_test--; // CreateTestWS incremented this
  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test 5");
    ok(false, "U+0020 not acceptable in protocol parameter");
  }
  catch (e) {
    ok(true, "U+0020 not acceptable in protocol parameter");
  }
  doTest(6);
}

function test6()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-6");
  var counter = 1;
  ws.onopen = function()
  {
    ws.send(counter);
  }
  ws.onmessage = function(e)
  {
    if (counter == 5) {
      ok(e.data == "あいうえお", "test-6 counter 5 data ok");
      ws.close();
    } else {
      ok(e.data == counter+1, "bad counter");
      counter += 2;
      ws.send(counter);
    }
  }
  ws.onclose = function(e)
  {
    shouldCloseCleanly(e);
    doTest(7);
  };
}

function test7()
{
  var ws = CreateTestWS("ws://sub2.test2.example.org/tests/dom/base/test/file_websocket", "test-7");
  var gotmsg = false;

  ws.onopen = function()
  {
    ok(true, "test 7 open");
  }
  ws.onmessage = function(e)
  {
    ok(true, "test 7 message");
    ok(e.origin == "ws://sub2.test2.example.org", "onmessage origin set to ws:// host");
    gotmsg = true;
    ws.close();
  }
  ws.onclose = function(e)
  {
    ok(gotmsg, "recvd message in test 7 before close");
    shouldCloseCleanly(e);
    doTest(8);
  };
}

function test8()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-8");
  ws.onopen = function()
  {
    ok(ws.protocol == "test-8", "test-8 subprotocol selection");
    ws.close();
  }
  ws.onclose = function(e)
  {
    shouldCloseCleanly(e);
    // We called close() with no close code: so pywebsocket will also send no
    // close code, which translates to code 1005 
    ok(e.code == 1005, "test-8 close code has wrong value:" + e.code);
    ok(e.reason == "", "test-8 close reason has wrong value:" + e.reason);
    doTest(9);
  };
}

var waitTest9 = false;

function test9()
{
  waitTest9 = true;

  var ws = CreateTestWS("ws://test2.example.org/tests/dom/base/test/file_websocket", "test-9");
  ws._receivedErrorEvent = false;
  ws.onopen = shouldNotOpen;
  ws.onerror = function(e)
  {
    ws._receivedErrorEvent = true;
  };
  ws.onclose = function(e)
  {
    ok(ws._receivedErrorEvent, "Didn't received the error event in test 9.");
    shouldCloseNotCleanly(e);
    waitTest9 = false;
    maybeFinished();
  };

  ws.close();
  
  // the server injects a delay, so proceed with this in the background
  doTest(10);
}

var waitTest10 = false;

function test10()
{
  waitTest10 = true;

  var ws = CreateTestWS("ws://sub1.test1.example.com/tests/dom/base/test/file_websocket", "test-10");
  ws.onclose = function(e)
  {
    shouldCloseCleanly(e);
    waitTest10 = false;
    maybeFinished();
  }

  try {
    ws.send("client data");
    ok(false, "Couldn't send data before connecting!");
  }
  catch (e) {
    ok(true, "Couldn't send data before connecting!");
  }
  ws.onopen = function()
  {
    ok(true, "test 10 opened");
    ws.close();
  }

  // proceed with this test in the background
  doTest(11);
}

function test11()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-11");
  ok(ws.readyState == 0, "create bad readyState in test-11!");
  ws.onopen = function()
  {
    ok(ws.readyState == 1, "open bad readyState in test-11!");
    ws.send("client data");
  }
  ws.onmessage = function(e)
  {
    ok(e.data == "server data", "bad received message in test-11!");
    ws.close(1000, "Have a nice day");

// this ok() is disabled due to a race condition - it state may have
// advanced through 2 (closing) and into 3 (closed) before it is evald
//    ok(ws.readyState == 2, "onmessage bad readyState in test-11!");
  }
  ws.onclose = function(e)
  {
    ok(ws.readyState == 3, "onclose bad readyState in test-11!");
    shouldCloseCleanly(e);
    ok(e.code == 1000, "test 11 got wrong close code: " + e.code);
    ok(e.reason == "Have a nice day", "test 11 got wrong close reason: " + e.reason);
    doTest(12);
  }
}

function test12()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-12");
  ws.onopen = function()
  {
    try {
      // send an unpaired surrogate
      ws._gotMessage = false;
      ws.send("a\ud800b");
      ok(true, "ok to send an unpaired surrogate");
    }
    catch (e) {
      ok(false, "shouldn't fail any more when sending an unpaired surrogate!");
    }
  }

  ws.onmessage = function(msg)
  {
    ok(msg.data == "SUCCESS", "Unpaired surrogate in UTF-16 not converted in test-12");
    ws._gotMessage = true;
    // Must support unpaired surrogates in close reason, too
    ws.close(1000, "a\ud800b");
  }

  ws.onclose = function(e)
  {
    ok(ws.readyState == 3, "onclose bad readyState in test-12!");
    ok(ws._gotMessage, "didn't receive message!");
    shouldCloseCleanly(e);
    ok(e.code == 1000, "test 12 got wrong close code: " + e.code);
    ok(e.reason == "a\ufffdb", "test 11 didn't get replacement char in close reason: " + e.reason);
    doTest(13);
  }
}

function test13()
{
    // previous versions of this test counted the number of protocol errors returned, but the 
    // protocol stack typically closes down after reporting a protocol level error - trying
    // to resync is too dangerous

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-13");
  ws._timesCalledOnError = 0;
  ws.onerror = function()
  {
    ws._timesCalledOnError++;
  }
  ws.onclose = function(e)
  {
    ok(ws._timesCalledOnError > 0, "no error events");
    doTest(14);
  }
}

function test14()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-14");
  ws.onmessage = function()
  {
    ok(false, "shouldn't received message after the server sent the close frame");
  }
  ws.onclose = function(e)
  {
    shouldCloseCleanly(e);
    // Skip test 15 for now: broken
    doTest(16);
  };
}

/*
 * DISABLED: see comments for test-15 case in file_websocket_wsh.py
 *
function test15()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-15");
  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    doTest(16);
  };

  // termination of the connection might cause an error event if it happens in OPEN
  ws.onerror = function()
  {
  }

}
*/

function test16()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-16");
  ws.onopen = function()
  {
    ws.close();
    ok(!ws.send("client data"), "shouldn't send message after calling close()");
  }
  ws.onmessage = function()
  {
    ok(false, "shouldn't send message after calling close()");
  }

  ws.onerror = function()
  {
  }
  ws.onclose = function()
  {
    doTest(18);
  }
}

// The tests that expects that their websockets neither open nor close MUST
// be in the end of the tests, i.e. HERE, in order to prevent blocking the other
// tests.

function test18()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket_http_resource.txt");
  ws.onopen = shouldNotOpen;
  ws.onerror = ignoreError;
  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    doTest(19);
  };
}

function test19()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-19");
  ws.onopen = shouldNotOpen;
  ws.onerror = ignoreError;
  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    doTest(24);
  };
}

function test24()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-does-not-exist");
  ws.onopen = shouldNotOpen;
  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    doTest(25);
  };
  ws.onerror = function()
  {
  }
}

function test25()
{
  var prots=[];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);

  // This test errors because the server requires a sub-protocol, but
  // the test just wants to ensure that the ctor doesn't generate an
  // exception
  ws.onerror = ignoreError;
  ws.onopen = shouldNotOpen;

  ws.onclose = function(e)
  {
    ok(ws.protocol == "", "test25 subprotocol selection");
    ok(true, "test 25 protocol array close");
    doTest(26);
  };
}

function test26()
{
  var prots=[""];

  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
    ok(false, "testing empty element sub protocol array");
  }
  catch (e) {
    ok(true, "testing empty sub element protocol array");
  }
  doTest(27);
}

function test27()
{
  var prots=["test27", ""];

  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
    ok(false, "testing empty element mixed sub protocol array");
  }
  catch (e) {
    ok(true, "testing empty element mixed sub protocol array");
  }
  doTest(28);
}

function test28()
{
  var prots=["test28"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 28 protocol array open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(ws.protocol == "test28", "test28 subprotocol selection");
    ok(true, "test 28 protocol array close");
    doTest(29);
  };
}

function test29()
{
  var prots=["test29a", "test29b"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 29 protocol array open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 29 protocol array close");
    doTest(30);
  };
}

function test30()
{
  var prots=["test-does-not-exist"];
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);

  ws.onopen = shouldNotOpen;
  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    doTest(31);
  };
  ws.onerror = function()
  {
  }
}

function test31()
{
  var prots=["test-does-not-exist", "test31"];
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);

  ws.onopen = function(e)
  {
    ok(true, "test 31 protocol array open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(ws.protocol == "test31", "test31 subprotocol selection");
    ok(true, "test 31 protocol array close");
    doTest(32);
  };
}

function test32()
{
  var prots=["test32","test32"];

  try {
    var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
    ok(false, "testing duplicated element sub protocol array");
  }
  catch (e) {
    ok(true, "testing duplicated sub element protocol array");
  }
  doTest(33);
}

function test33()
{
  var prots=["test33"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 33 open");
    ws.close(3131);   // pass code but not reason
  };

  ws.onclose = function(e)
  {
    ok(true, "test 33 close");
    shouldCloseCleanly(e);
    ok(e.code == 3131, "test 33 got wrong close code: " + e.code);
    ok(e.reason === "", "test 33 got wrong close reason: " + e.reason);
    doTest(34);
  };
}

function test34()
{
  var prots=["test-34"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 34 open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 34 close");
    ok(e.wasClean, "test 34 closed cleanly");
    ok(e.code == 1001, "test 34 custom server code");
    ok(e.reason == "going away now", "test 34 custom server reason");
    doTest(35);
  };
}

function test35()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35a");

  ws.onopen = function(e)
  {
    ok(true, "test 35a open");
    ws.close(3500, "my code");
  };

  ws.onclose = function(e)
  {
    ok(true, "test 35a close");
    ok(e.wasClean, "test 35a closed cleanly");
    current_test--; // CreateTestWS for 35a incremented this
    var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-35b");

  wsb.onopen = function(e)
  {
    ok(true, "test 35b open");
    wsb.close();
  };

  wsb.onclose = function(e)
  {
    ok(true, "test 35b close");
    ok(e.wasClean, "test 35b closed cleanly");
    ok(e.code == 3501, "test 35 custom server code");
    ok(e.reason == "my code", "test 35 custom server reason");
    doTest(36);
  };
  }
}

function test36()
{
  var prots=["test-36"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 36 open");

    try {
      ws.close(13200);
      ok(false, "testing custom close code out of range");
     }
     catch (e) {
       ok(true, "testing custom close code out of range");
       ws.close(3200);
     }
  };

  ws.onclose = function(e)
  {
    ok(true, "test 36 close");
    ok(e.wasClean, "test 36 closed cleanly");
    doTest(37);
  };
}

function test37()
{
  var prots=["test-37"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 37 open");

    try {
	ws.close(3100,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
      ok(false, "testing custom close reason out of range");
     }
     catch (e) {
       ok(true, "testing custom close reason out of range");
       ws.close(3100,"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012");
     }
  };

  ws.onclose = function(e)
  {
    ok(true, "test 37 close");
    ok(e.wasClean, "test 37 closed cleanly");

    current_test--; // CreateTestWS for 37 incremented this
    var wsb = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37b");

    wsb.onopen = function(e)
    {
      // now test that a rejected close code and reason dont persist
      ok(true, "test 37b open");
      try {
        wsb.close(3101,"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123");
        ok(false, "testing custom close reason out of range 37b");
      }
      catch (e) {
        ok(true, "testing custom close reason out of range 37b");
        wsb.close();
     }
    }

    wsb.onclose = function(e)
    {
      ok(true, "test 37b close");
      ok(e.wasClean, "test 37b closed cleanly");

      current_test--; // CreateTestWS for 37 incremented this
      var wsc = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-37c");

      wsc.onopen = function(e)
      {
        ok(true, "test 37c open");
        wsc.close();
      }

      wsc.onclose = function(e)
      {
         ok(e.code != 3101, "test 37c custom server code not present");
         ok(e.reason == "", "test 37c custom server reason not present");
         doTest(38);  
      }
    }
  }
}

function test38()
{
  var prots=["test-38"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 38 open");
    ok(ws.extensions != undefined, "extensions attribute defined");
//    ok(ws.extensions == "deflate-stream", "extensions attribute deflate-stream");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 38 close");
    doTest(39);
  };
}

function test39()
{
  var prots=["test-39"];

  var ws = CreateTestWS("wss://example.com/tests/dom/base/test/file_websocket", prots);
  status_test39 = "started";
  ws.onopen = function(e)
  {
    status_test39 = "opened";
    ok(true, "test 39 open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 39 close");
    ok(status_test39 == "opened", "test 39 did open"); 
    doTest(40);
  };
}

function test40()
{
  var prots=["test-40"];

  var ws = CreateTestWS("wss://nocert.example.com/tests/dom/base/test/file_websocket", prots);

  status_test40 = "started";
  ws.onerror = ignoreError;

  ws.onopen = function(e)
  {
    status_test40 = "opened";
    ok(false, "test 40 open");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 40 close");
    ok(status_test40 == "started", "test 40 did not open"); 
    doTest(42);
  };
}

function test42()
{
// test some utf-8 non-characters. They should be allowed in the
// websockets context. Test via round trip echo.
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-42");
  var data = ["U+FFFE \ufffe",
		"U+FFFF \uffff",
		"U+10FFFF \udbff\udfff"];
  var index = 0;

  ws.onopen = function()
  {
    ws.send(data[0]);
    ws.send(data[1]);
    ws.send(data[2]);
  }

  ws.onmessage = function(e)
  {
    ok(e.data == data[index], "bad received message in test-42! index="+index);
    index++;
    if (index == 3)
      ws.close();
  }

  ws.onclose = function(e)
  {
    doTest(43);
  }
}

function test43()
{
  var prots=["test-43"];

  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", prots);
  ws.onopen = function(e)
  {
    ok(true, "test 43 open");
    // Test binaryType setting
    ws.binaryType = "arraybuffer";
    ws.binaryType = "blob";
    ws.binaryType = "";  // illegal
    is(ws.binaryType, "blob");
    ws.binaryType = "ArrayBuffer";  // illegal
    is(ws.binaryType, "blob");
    ws.binaryType = "Blob";  // illegal
    is(ws.binaryType, "blob");
    ws.binaryType = "mcfoofluu";  // illegal
    is(ws.binaryType, "blob");
    ws.close();
  };

  ws.onclose = function(e)
  {
    ok(true, "test 43 close");
    doTest(44);
  };
}


function test44()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-44");
  ok(ws.readyState == 0, "bad readyState in test-44!");
  ws.binaryType = "arraybuffer";
  ws.onopen = function()
  {
    ok(ws.readyState == 1, "open bad readyState in test-44!");
    var buf = new ArrayBuffer(3);
    // create byte view
    var view = new Uint8Array(buf);
    view[0] = 5;
    view[1] = 0; // null byte
    view[2] = 7;
    ws.send(buf);
  }
  ws.onmessage = function(e)
  {
    ok(e.data instanceof ArrayBuffer, "Should receive an arraybuffer!");
    var view = new Uint8Array(e.data);
    ok(view.length == 2 && view[0] == 0 && view[1] ==4, "testing Reply arraybuffer" );
    ws.close();
  }
  ws.onclose = function(e)
  {
    ok(ws.readyState == 3, "onclose bad readyState in test-44!");
    shouldCloseCleanly(e);
    doTest(46);
  }
}

function test46()
{
  var ws = CreateTestWS("ws://mochi.test:8888/tests/dom/base/test/file_websocket", "test-46");
  ok(ws.readyState == 0, "create bad readyState in test-46!");
  ws.onopen = function()
  {
    ok(ws.readyState == 1, "open bad readyState in test-46!");
    ws.close()
    ok(ws.readyState == 2, "close must set readyState to 2 in test-46!");
  }
  ws.onmessage = function(e)
  {
    ok(false, "received message after calling close in test-46!");
  }
  ws.onclose = function(e)
  {
    ok(ws.readyState == 3, "onclose bad readyState in test-46!");
    shouldCloseCleanly(e);
    doTest(47);
  }
}

function test47()
{
  var hasError = false;
  var ws = CreateTestWS("ws://another.websocket.server.that.probably.does.not.exist");
  ws.onopen = shouldNotOpen;

  ws.onerror = function (e)
  {
    ok(ws.readyState == 3, "test-47: readyState should be CLOSED(3) in onerror: got " 
       + ws.readyState);
    ok(!ws._withinClose, "onerror() called during close()!");
    hasError = true;
  }

  ws.onclose = function(e)
  {
    shouldCloseNotCleanly(e);
    ok(hasError, "test-47: should have called onerror before onclose");
    ok(ws.readyState == 3, "test-47: readyState should be CLOSED(3) in onclose: got " 
       + ws.readyState);
    ok(!ws._withinClose, "onclose() called during close()!");
    ok(e.code == 1006, "test-47 close code should be 1006 but is:" + e.code);
    doTest(48);
  };

  // Call close before we're connected: throws error
  // Make sure we call onerror/onclose asynchronously
  ws._withinClose = 1;
  ws.close(3333, "Closed before we were open: error");
  ws._withinClose = 0;
  ok(ws.readyState == 2, "test-47: readyState should be CLOSING(2) after close(): got "
     + ws.readyState);
}


var ranAllTests = false;

function maybeFinished()
{
  if (!ranAllTests)
    return;

  if (waitTest2Part1 || waitTest2Part2 || waitTest9 || waitTest10)
    return;

  for (i = 0; i < all_ws.length; ++i) {
    if (all_ws[i] != shouldNotReceiveCloseEvent &&
        !all_ws[i]._receivedCloseEvent) {
      ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
    }
  }

  if (testsuite_iteration++ < testsuite_iterations) {
    // play it again, Sam...
    ok(1, "starting testsuite iteration " + testsuite_iteration);
    test_started = new Array(last_test);
    doTest(current_test = first_test);
  } else {
    // all done
    postMessage({type: 'finish' });
  }
}

onmessage = function()
{
  doTest(first_test);
}
