Backed out 11 changesets (bug 1119593) for zmedia failures on a CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 21 Jan 2015 18:17:22 -0800
changeset 252193 a75426ed6ab016ed033a2ecc8e7fdf59125446a1
parent 252192 c3c9f5e4ab49ea5f19f1128ca0421b24c1ed3aed
child 252194 20cd9b661db19ea9abbde6b849eb91efa042af0c
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1119593
milestone38.0a1
backs out6a3067465821648e0d890288b49f06a08bd3def2
7a2f5bf9e656e03720c171bfed59520e03c74083
544d8d52bbaf45194ac3860e8c386ab7ba1e50cc
3e61d3076385f12a2aa11ecdeea3d1973da0e81e
8b17ccc1d9c688d30f99c9564b2c0f940cde419f
8c9ee98fcce6f40a3599e62fdedd73544e38cc37
cc930e78d1b54647d57b19b7ac0709c2314e7ea6
2198a2cd71a1364575daae15bf0cb6df2c7d2f9c
49e6811407965e840c9d05d0a026135ff1bd54f2
288e9b7efccce29609bc720f20e71246200003f7
33b1f11c878416c858145670e4e611f585a175a5
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 11 changesets (bug 1119593) for zmedia failures on a CLOSED TREE Backed out changeset 6a3067465821 (bug 1119593) Backed out changeset 7a2f5bf9e656 (bug 1119593) Backed out changeset 544d8d52bbaf (bug 1119593) Backed out changeset 3e61d3076385 (bug 1119593) Backed out changeset 8b17ccc1d9c6 (bug 1119593) Backed out changeset 8c9ee98fcce6 (bug 1119593) Backed out changeset cc930e78d1b5 (bug 1119593) Backed out changeset 2198a2cd71a1 (bug 1119593) Backed out changeset 49e681140796 (bug 1119593) Backed out changeset 288e9b7efccc (bug 1119593) Backed out changeset 33b1f11c8784 (bug 1119593)
dom/media/tests/mochitest/constraints.js
dom/media/tests/mochitest/dataChannel.js
dom/media/tests/mochitest/head.js
dom/media/tests/mochitest/identity/identityevent.js
dom/media/tests/mochitest/identity/test_getIdentityAssertion.html
dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html
dom/media/tests/mochitest/identity/test_setIdentityProvider.html
dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html
dom/media/tests/mochitest/mediaStreamPlayback.js
dom/media/tests/mochitest/mochitest.ini
dom/media/tests/mochitest/network.js
dom/media/tests/mochitest/nonTrickleIce.js
dom/media/tests/mochitest/pc.js
dom/media/tests/mochitest/steeplechase.ini
dom/media/tests/mochitest/templates.js
dom/media/tests/mochitest/test_dataChannel_basicAudio.html
dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html
dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html
dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html
dom/media/tests/mochitest/test_dataChannel_basicVideo.html
dom/media/tests/mochitest/test_dataChannel_bug1013809.html
dom/media/tests/mochitest/test_dataChannel_noOffer.html
dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
dom/media/tests/mochitest/test_getUserMedia_callbacks.html
dom/media/tests/mochitest/test_getUserMedia_constraints.html
dom/media/tests/mochitest/test_getUserMedia_constraints_mobile.html
dom/media/tests/mochitest/test_getUserMedia_exceptions.html
dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html
dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html
dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html
dom/media/tests/mochitest/test_peerConnection_basicAudio.html
dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html
dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html
dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html
dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
dom/media/tests/mochitest/test_peerConnection_basicVideo.html
dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html
dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
dom/media/tests/mochitest/test_peerConnection_bug1013809.html
dom/media/tests/mochitest/test_peerConnection_bug1042791.html
dom/media/tests/mochitest/test_peerConnection_bug822674.html
dom/media/tests/mochitest/test_peerConnection_bug825703.html
dom/media/tests/mochitest/test_peerConnection_bug827843.html
dom/media/tests/mochitest/test_peerConnection_bug834153.html
dom/media/tests/mochitest/test_peerConnection_callbacks.html
dom/media/tests/mochitest/test_peerConnection_capturedVideo.html
dom/media/tests/mochitest/test_peerConnection_close.html
dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html
dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html
dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html
dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html
dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html
dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
dom/media/tests/mochitest/test_peerConnection_replaceTrack.html
dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html
dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html
dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html
dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html
dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html
dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html
dom/media/tests/mochitest/test_peerConnection_syncSetDescription.html
dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html
dom/media/tests/mochitest/test_peerConnection_toJSON.html
dom/media/tests/mochitest/test_peerConnection_twoAudioStreams.html
dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreams.html
dom/media/tests/mochitest/test_peerConnection_twoAudioVideoStreamsCombined.html
dom/media/tests/mochitest/test_peerConnection_twoVideoStreams.html
dom/media/tests/mochitest/test_zmedia_cleanup.html
--- a/dom/media/tests/mochitest/constraints.js
+++ b/dom/media/tests/mochitest/constraints.js
@@ -47,20 +47,28 @@ var common_tests = [
 
 
 /**
  * Starts the test run by running through each constraint
  * test by verifying that the right resolution and rejection is fired.
  */
 
 function testConstraints(tests) {
-  function testgum(prev, test) {
-    return prev.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
-      .then(() => is(null, test.error, test.message),
-            reason => is(reason.name, test.error, test.message + ": " + reason.message));
+  function testgum(p, test) {
+    return p.then(function() {
+      return navigator.mediaDevices.getUserMedia(test.constraints);
+    })
+    .then(function() {
+      is(null, test.error, test.message);
+    }, function(reason) {
+      is(reason.name, test.error, test.message + ": " + reason.message);
+    });
   }
 
-  tests.reduce(testgum, Promise.resolve())
-    .catch(reason => {
-      ok(false, "Unexpected failure: " + reason.message);
-    })
-    .then(SimpleTest.finish);
+  var p = new Promise(function(resolve) { resolve(); });
+  tests.forEach(function(test) {
+    p = testgum(p, test);
+  });
+  p.catch(function(reason) {
+    ok(false, "Unexpected failure: " + reason.message);
+  })
+  .then(SimpleTest.finish);
 }
--- a/dom/media/tests/mochitest/dataChannel.js
+++ b/dom/media/tests/mochitest/dataChannel.js
@@ -1,170 +1,230 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/**
- * Returns the contents of a blob as text
- *
- * @param {Blob} blob
-          The blob to retrieve the contents from
- */
-function getBlobContent(blob) {
-  return new Promise(resolve => {
-    var reader = new FileReader();
-    // Listen for 'onloadend' which will always be called after a success or failure
-    reader.onloadend = event => resolve(event.target.result);
-    reader.readAsText(blob);
-  });
-}
-
 function addInitialDataChannel(chain) {
   chain.insertBefore('PC_LOCAL_CREATE_OFFER', [
-    function PC_REMOTE_EXPECT_DATA_CHANNEL(test) {
-      test.pcRemote.expectDataChannel();
-    },
+    ['PC_LOCAL_CREATE_DATA_CHANNEL',
+      function (test) {
+        var channel = test.pcLocal.createDataChannel({});
 
-    function PC_LOCAL_CREATE_DATA_CHANNEL(test) {
-      var channel = test.pcLocal.createDataChannel({});
-      is(channel.binaryType, "blob", channel + " is of binary type 'blob'");
-      is(channel.readyState, "connecting", channel + " is in state: 'connecting'");
+        is(channel.binaryType, "blob", channel + " is of binary type 'blob'");
+        is(channel.readyState, "connecting", channel + " is in state: 'connecting'");
+
+        is(test.pcLocal.signalingState, STABLE,
+           "Create datachannel does not change signaling state");
 
-      is(test.pcLocal.signalingState, STABLE,
-         "Create datachannel does not change signaling state");
-    }
+        test.next();
+      }
+    ]
   ]);
-
+  chain.insertAfter('PC_REMOTE_CREATE_ANSWER', [
+    [
+      'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK',
+      function (test) {
+        test.waitForInitialDataChannel(test.pcLocal, function () {
+          ok(true, test.pcLocal + " dataChannels[0] switched to 'open'");
+        },
+        // At this point a timeout failure will be of no value
+        null);
+        test.next();
+      }
+    ],
+    [
+      'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK',
+      function (test) {
+        test.waitForInitialDataChannel(test.pcRemote, function () {
+          ok(true, test.pcRemote + " dataChannels[0] switched to 'open'");
+        },
+        // At this point a timeout failure will be of no value
+        null);
+        test.next();
+      }
+    ]
+  ]);
   chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', [
-    function PC_LOCAL_VERIFY_DATA_CHANNEL_STATE(test) {
-      return test.pcLocal.dataChannels[0].opened;
-    },
-
-    function PC_REMOTE_VERIFY_DATA_CHANNEL_STATE(test) {
-      return test.pcRemote.nextDataChannel.then(channel => channel.opened);
-    }
+    [
+      'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE',
+      function (test) {
+        test.waitForInitialDataChannel(test.pcLocal, function() {
+          test.next();
+        }, function() {
+          ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'");
+          //TODO: use stopAndExit() once bug 1019323 has landed
+          unexpectedEventAndFinish(this, 'timeout')
+          // to prevent test framework timeouts
+          test.next();
+        });
+      }
+    ],
+    [
+      'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE',
+      function (test) {
+        test.waitForInitialDataChannel(test.pcRemote, function() {
+          test.next();
+        }, function() {
+          ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'");
+          //TODO: use stopAndExit() once bug 1019323 has landed
+          unexpectedEventAndFinish(this, 'timeout');
+          // to prevent test framework timeouts
+          test.next();
+        });
+      }
+    ]
   ]);
   chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS');
   chain.append([
-    function SEND_MESSAGE(test) {
-      var message = "Lorem ipsum dolor sit amet";
+    [
+      'SEND_MESSAGE',
+      function (test) {
+        var message = "Lorem ipsum dolor sit amet";
 
-      return test.send(message).then(result => {
-        is(result.data, message, "Message correctly transmitted from pcLocal to pcRemote.");
-      });
-    },
+        test.send(message, function (channel, data) {
+          is(data, message, "Message correctly transmitted from pcLocal to pcRemote.");
 
-    function SEND_BLOB(test) {
-      var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."];
-      var blob = new Blob(contents, { "type" : "text/plain" });
+          test.next();
+        });
+      }
+    ],
+    [
+      'SEND_BLOB',
+      function (test) {
+        var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."];
+        var blob = new Blob(contents, { "type" : "text/plain" });
 
-      return test.send(blob).then(result => {
-        ok(result.data instanceof Blob, "Received data is of instance Blob");
-        is(result.data.size, blob.size, "Received data has the correct size.");
+        test.send(blob, function (channel, data) {
+          ok(data instanceof Blob, "Received data is of instance Blob");
+          is(data.size, blob.size, "Received data has the correct size.");
 
-        return getBlobContent(result.data);
-      }).then(recv_contents =>
-              is(recv_contents, contents, "Received data has the correct content."));
-    },
+          getBlobContent(data, function (recv_contents) {
+            is(recv_contents, contents, "Received data has the correct content.");
 
-    function CREATE_SECOND_DATA_CHANNEL(test) {
-      return test.createDataChannel({ }).then(result => {
-        var sourceChannel = result.local;
-        var targetChannel = result.remote;
-        is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'");
-        is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'");
-
-        is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'");
-      });
-    },
+            test.next();
+          });
+        });
+      }
+    ],
+    [
+      'CREATE_SECOND_DATA_CHANNEL',
+      function (test) {
+        test.createDataChannel({ }, function (sourceChannel, targetChannel) {
+          is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'");
+          is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'");
 
-    function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL(test) {
-      var channels = test.pcRemote.dataChannels;
-      var message = "I am the Omega";
+          is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'");
+          is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'");
 
-      return test.send(message).then(result => {
-        is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
-        is(result.data, message, "Received message has the correct content.");
-      });
-    },
+          test.next();
+        });
+      }
+    ],
+    [
+      'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL',
+      function (test) {
+        var channels = test.pcRemote.dataChannels;
+        var message = "Lorem ipsum dolor sit amet";
 
+        test.send(message, function (channel, data) {
+          is(channels.indexOf(channel), channels.length - 1, "Last channel used");
+          is(data, message, "Received message has the correct content.");
 
-    function SEND_MESSAGE_THROUGH_FIRST_CHANNEL(test) {
-      var message = "Message through 1st channel";
-      var options = {
-        sourceChannel: test.pcLocal.dataChannels[0],
-        targetChannel: test.pcRemote.dataChannels[0]
-      };
+          test.next();
+        });
+      }
+    ],
+    [
+      'SEND_MESSAGE_THROUGH_FIRST_CHANNEL',
+      function (test) {
+        var message = "Message through 1st channel";
+        var options = {
+          sourceChannel: test.pcLocal.dataChannels[0],
+          targetChannel: test.pcRemote.dataChannels[0]
+        };
 
-      return test.send(message, options).then(result => {
-        is(test.pcRemote.dataChannels.indexOf(result.channel), 0, "1st channel used");
-        is(result.data, message, "Received message has the correct content.");
-      });
-    },
-
+        test.send(message, function (channel, data) {
+          is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used");
+          is(data, message, "Received message has the correct content.");
 
-    function SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL(test) {
-      var message = "Return a message also through 1st channel";
-      var options = {
-        sourceChannel: test.pcRemote.dataChannels[0],
-        targetChannel: test.pcLocal.dataChannels[0]
-      };
+          test.next();
+        }, options);
+      }
+    ],
+    [
+      'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL',
+      function (test) {
+        var message = "Return a message also through 1st channel";
+        var options = {
+          sourceChannel: test.pcRemote.dataChannels[0],
+          targetChannel: test.pcLocal.dataChannels[0]
+        };
 
-      return test.send(message, options).then(result => {
-        is(test.pcLocal.dataChannels.indexOf(result.channel), 0, "1st channel used");
-        is(result.data, message, "Return message has the correct content.");
-      });
-    },
+        test.send(message, function (channel, data) {
+          is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used");
+          is(data, message, "Return message has the correct content.");
 
-    function CREATE_NEGOTIATED_DATA_CHANNEL(test) {
-      var options = {
-        negotiated:true,
-        id: 5,
-        protocol: "foo/bar",
-        ordered: false,
-        maxRetransmits: 500
-      };
-      return test.createDataChannel(options).then(result => {
-        var sourceChannel2 = result.local;
-        var targetChannel2 = result.remote;
-        is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
-        is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
+          test.next();
+        }, options);
+      }
+    ],
+    [
+      'CREATE_NEGOTIATED_DATA_CHANNEL',
+      function (test) {
+        var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false,
+          maxRetransmits:500};
+        test.createDataChannel(options, function (sourceChannel2, targetChannel2) {
+          is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'");
+          is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
-        is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
+          is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'");
+          is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'");
 
-        is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
-        var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
-        is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
-        is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
-        /*
-          These aren't exposed by IDL yet
+          if (options.id != undefined) {
+            is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id);
+          }
+          else {
+            options.id = sourceChannel2.id;
+          }
+          var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime);
+          is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol);
+          is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable);
+  /*
+    These aren't exposed by IDL yet
           is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered);
           is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" +
-          sourceChannel2.maxRetransmits);
+       sourceChannel2.maxRetransmits);
           is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" +
-          sourceChannel2.maxRetransmitTime);
-        */
+       sourceChannel2.maxRetransmitTime);
+  */
 
-        is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id);
-        is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol);
-        is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable);
-        /*
-          These aren't exposed by IDL yet
-          is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered);
+          is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id);
+          is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol);
+          is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable);
+  /*
+    These aren't exposed by IDL yet
+         is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered);
           is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" +
-          targetChannel2.maxRetransmits);
+       targetChannel2.maxRetransmits);
           is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" +
-          targetChannel2.maxRetransmitTime);
-        */
-      });
-    },
+       targetChannel2.maxRetransmitTime);
+  */
 
-    function SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2(test) {
-      var channels = test.pcRemote.dataChannels;
-      var message = "I am the walrus; Goo goo g'joob";
+          test.next();
+        });
+      }
+    ],
+    [
+      'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2',
+      function (test) {
+        var channels = test.pcRemote.dataChannels;
+        var message = "Lorem ipsum dolor sit amet";
 
-      return test.send(message).then(result => {
-        is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
-        is(result.data, message, "Received message has the correct content.");
-      });
-    }
+        test.send(message, function (channel, data) {
+          is(channels.indexOf(channel), channels.length - 1, "Last channel used");
+          is(data, message, "Received message has the correct content.");
+
+          test.next();
+        });
+      }
+    ]
   ]);
 }
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-"use strict";
-
 var Cc = SpecialPowers.Cc;
 var Ci = SpecialPowers.Ci;
+var Cr = SpecialPowers.Cr;
 
 // Specifies whether we are using fake streams to run this automation
 var FAKE_ENABLED = true;
 try {
   var audioDevice = SpecialPowers.getCharPref('media.audio_loopback_dev');
   var videoDevice = SpecialPowers.getCharPref('media.video_loopback_dev');
   dump('TEST DEVICES: Using media devices:\n');
   dump('audio: ' + audioDevice + '\nvideo: ' + videoDevice + '\n');
@@ -28,37 +27,37 @@ try {
  *        Meta information of the test
  * @param {string} meta.title
  *        Description of the test
  * @param {string} [meta.bug]
  *        Bug the test was created for
  * @param {boolean} [meta.visible=false]
  *        Visibility of the media elements
  */
-function realCreateHTML(meta) {
+function createHTML(meta) {
   var test = document.getElementById('test');
 
   // Create the head content
   var elem = document.createElement('meta');
   elem.setAttribute('charset', 'utf-8');
   document.head.appendChild(elem);
 
   var title = document.createElement('title');
   title.textContent = meta.title;
   document.head.appendChild(title);
 
   // Create the body content
   var anchor = document.createElement('a');
-  anchor.textContent = meta.title;
+  anchor.setAttribute('target', '_blank');
+
   if (meta.bug) {
     anchor.setAttribute('href', 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + meta.bug);
-  } else {
-    anchor.setAttribute('target', '_blank');
   }
 
+  anchor.textContent = meta.title;
   document.body.insertBefore(anchor, test);
 
   var display = document.createElement('p');
   display.setAttribute('id', 'display');
   document.body.insertBefore(display, test);
 
   var content = document.createElement('div');
   content.setAttribute('id', 'content');
@@ -77,119 +76,110 @@ function realCreateHTML(meta) {
  *        Description to use for the element
  * @return {HTMLMediaElement} The created HTML media element
  */
 function createMediaElement(type, label) {
   var id = label + '_' + type;
   var element = document.getElementById(id);
 
   // Sanity check that we haven't created the element already
-  if (element) {
+  if (element)
     return element;
-  }
 
   element = document.createElement(type === 'audio' ? 'audio' : 'video');
   element.setAttribute('id', id);
   element.setAttribute('height', 100);
   element.setAttribute('width', 150);
   element.setAttribute('controls', 'controls');
-  element.setAttribute('autoplay', 'autoplay');
   document.getElementById('content').appendChild(element);
 
   return element;
 }
 
 
 /**
  * Wrapper function for mozGetUserMedia to allow a singular area of control
  * for determining whether we run this with fake devices or not.
  *
  * @param {Dictionary} constraints
  *        The constraints for this mozGetUserMedia callback
+ * @param {Function} onSuccess
+ *        The success callback if the stream is successfully retrieved
+ * @param {Function} onError
+ *        The error callback if the stream fails to be retrieved
  */
-function getUserMedia(constraints) {
+function getUserMedia(constraints, onSuccess, onError) {
   if (!("fake" in constraints) && FAKE_ENABLED) {
     constraints["fake"] = FAKE_ENABLED;
   }
 
   info("Call getUserMedia for " + JSON.stringify(constraints));
-  return navigator.mediaDevices.getUserMedia(constraints);
+  navigator.mozGetUserMedia(constraints, onSuccess, onError);
 }
 
-// These are the promises we use to track that the prerequisites for the test
-// are in place before running it.  Users of this file need to ensure that they
-// also provide a promise called `scriptsReady` as well.
-var setTestOptions;
-var testConfigured = new Promise(r => setTestOptions = r);
 
-function setupEnvironment() {
-  if (!window.SimpleTest) {
-    return Promise.resolve();
-  }
-
-  // Running as a Mochitest.
-  SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts");
-  window.finish = () => SimpleTest.finish();
-  SpecialPowers.pushPrefEnv({
-    'set': [
+/**
+ * Setup any Mochitest for WebRTC by enabling the preference for
+ * peer connections. As by bug 797979 it will also enable mozGetUserMedia()
+ * and disable the mozGetUserMedia() permission checking.
+ *
+ * @param {Function} aCallback
+ *        Test method to execute after initialization
+ */
+function runTest(aCallback) {
+  if (window.SimpleTest) {
+    // Running as a Mochitest.
+    SimpleTest.waitForExplicitFinish();
+    SimpleTest.requestFlakyTimeout("WebRTC inherently depends on timeouts");
+    SpecialPowers.pushPrefEnv({'set': [
       ['dom.messageChannel.enabled', true],
       ['media.peerconnection.enabled', true],
       ['media.peerconnection.identity.enabled', true],
       ['media.peerconnection.identity.timeout', 12000],
       ['media.peerconnection.default_iceservers', '[]'],
       ['media.navigator.permission.disabled', true],
       ['media.getusermedia.screensharing.enabled', true],
-      ['media.getusermedia.screensharing.allowed_domains', "mochi.test"]
-    ]
-  }, setTestOptions);
+      ['media.getusermedia.screensharing.allowed_domains', "mochi.test"]]
+    }, function () {
+      try {
+        aCallback();
+      }
+      catch (err) {
+        generateErrorCallback()(err);
+      }
+    });
+  } else {
+    // Steeplechase, let it call the callback.
+    window.run_test = function(is_initiator) {
+      var options = {is_local: is_initiator,
+                     is_remote: !is_initiator};
+      aCallback(options);
+    };
+    // Also load the steeplechase test code.
+    var s = document.createElement("script");
+    s.src = "/test.js";
+    document.head.appendChild(s);
+  }
 }
 
-// This is called by steeplechase; which provides the test configuration options
-// directly to the test through this function.  If we're not on steeplechase,
-// the test is configured directly and immediately.
-function run_test(is_initiator) {
-  var options = { is_local: is_initiator,
-                  is_remote: !is_initiator };
-
-  // Also load the steeplechase test code.
-  var s = document.createElement("script");
-  s.src = "/test.js";
-  s.onload = () => setTestOptions(options);
-  document.head.appendChild(s);
-}
-
-function runTestWhenReady(testFunc) {
-  setupEnvironment();
-  return Promise.all([scriptsReady, testConfigured]).then(() => {
-    try {
-      return testConfigured.then(options => testFunc(options));
-    } catch (e) {
-      ok(false, 'Error executing test: ' + e +
-         ((typeof e.stack === 'string') ?
-          (' ' + e.stack.split('\n').join(' ... ')) : ''));
-    }
-  });
-}
-
-
 /**
  * Checks that the media stream tracks have the expected amount of tracks
  * with the correct kind and id based on the type and constraints given.
  *
  * @param {Object} constraints specifies whether the stream should have
  *                             audio, video, or both
  * @param {String} type the type of media stream tracks being checked
  * @param {sequence<MediaStreamTrack>} mediaStreamTracks the media stream
  *                                     tracks being checked
  */
 function checkMediaStreamTracksByType(constraints, type, mediaStreamTracks) {
-  if (constraints[type]) {
+  if(constraints[type]) {
     is(mediaStreamTracks.length, 1, 'One ' + type + ' track shall be present');
 
-    if (mediaStreamTracks.length) {
+    if(mediaStreamTracks.length) {
       is(mediaStreamTracks[0].kind, type, 'Track kind should be ' + type);
       ok(mediaStreamTracks[0].id, 'Track id should be defined');
     }
   } else {
     is(mediaStreamTracks.length, 0, 'No ' + type + ' tracks shall be present');
   }
 }
 
@@ -203,37 +193,39 @@ function checkMediaStreamTracksByType(co
  */
 function checkMediaStreamTracks(constraints, mediaStream) {
   checkMediaStreamTracksByType(constraints, 'audio',
     mediaStream.getAudioTracks());
   checkMediaStreamTracksByType(constraints, 'video',
     mediaStream.getVideoTracks());
 }
 
-/*** Utility methods */
-
-/** The dreadful setTimeout, use sparingly */
-function wait(time) {
-  return new Promise(r => setTimeout(r, time));
-}
+/**
+ * Utility methods
+ */
 
-/** The even more dreadful setInterval, use even more sparingly */
-function waitUntil(func, time) {
-  return new Promise(resolve => {
-    var interval = setInterval(() => {
-      if (func())  {
-        clearInterval(interval);
-        resolve();
-      }
-    }, time || 200);
-  });
+/**
+ * Returns the contents of a blob as text
+ *
+ * @param {Blob} blob
+          The blob to retrieve the contents from
+ * @param {Function} onSuccess
+          Callback with the blobs content as parameter
+ */
+function getBlobContent(blob, onSuccess) {
+  var reader = new FileReader();
+
+  // Listen for 'onloadend' which will always be called after a success or failure
+  reader.onloadend = function (event) {
+    onSuccess(event.target.result);
+  };
+
+  reader.readAsText(blob);
 }
 
-/*** Test control flow methods */
-
 /**
  * Generates a callback function fired only under unexpected circumstances
  * while running the tests. The generated function kills off the test as well
  * gracefully.
  *
  * @param {String} [message]
  *        An optional message to show if no object gets passed into the
  *        generated callback method.
@@ -241,247 +233,60 @@ function waitUntil(func, time) {
 function generateErrorCallback(message) {
   var stack = new Error().stack.split("\n");
   stack.shift(); // Don't include this instantiation frame
 
   /**
    * @param {object} aObj
    *        The object fired back from the callback
    */
-  return aObj => {
+  return function (aObj) {
     if (aObj) {
       if (aObj.name && aObj.message) {
         ok(false, "Unexpected callback for '" + aObj.name +
            "' with message = '" + aObj.message + "' at " +
            JSON.stringify(stack));
       } else {
         ok(false, "Unexpected callback with = '" + aObj +
            "' at: " + JSON.stringify(stack));
       }
     } else {
       ok(false, "Unexpected callback with message = '" + message +
          "' at: " + JSON.stringify(stack));
     }
-    throw new Error("Unexpected callback");
+    SimpleTest.finish();
   }
 }
 
-var unexpectedEventArrived;
-var rejectOnUnexpectedEvent = new Promise((x, reject) => {
-  unexpectedEventArrived = reject;
-});
-
 /**
  * Generates a callback function fired only for unexpected events happening.
  *
  * @param {String} description
           Description of the object for which the event has been fired
  * @param {String} eventName
           Name of the unexpected event
  */
-function unexpectedEvent(message, eventName) {
+function unexpectedEventAndFinish(message, eventName) {
   var stack = new Error().stack.split("\n");
   stack.shift(); // Don't include this instantiation frame
 
-  return e => {
-    var details = "Unexpected event '" + eventName + "' fired with message = '" +
-        message + "' at: " + JSON.stringify(stack);
-    ok(false, details);
-    unexpectedEventArrived(new Error(details));
+  return function () {
+    ok(false, "Unexpected event '" + eventName + "' fired with message = '" +
+       message + "' at: " + JSON.stringify(stack));
+    SimpleTest.finish();
   }
 }
 
-/**
- * Implements the one-shot event pattern used throughout.  Each of the 'onxxx'
- * attributes on the wrappers can be set with a custom handler.  Prior to the
- * handler being set, if the event fires, it causes the test execution to halt.
- * That handler is used exactly once, after which the original, error-generating
- * handler is re-installed.  Thus, each event handler is used at most once.
- *
- * @param {object} wrapper
- *        The wrapper on which the psuedo-handler is installed
- * @param {object} obj
- *        The real source of events
- * @param {string} event
- *        The name of the event
- */
-function createOneShotEventWrapper(wrapper, obj, event) {
-  var onx = 'on' + event;
-  var unexpected = unexpectedEvent(wrapper, event);
-  wrapper[onx] = unexpected;
-  obj[onx] = e => {
-    info(wrapper + ': "on' + event + '" event fired');
-    e.wrapper = wrapper;
-    wrapper[onx](e);
-    wrapper[onx] = unexpected;
-  };
-}
+function IsMacOSX10_6orOlder() {
+    var is106orOlder = false;
 
-
-/**
- * This class executes a series of functions in a continuous sequence.
- * Promise-bearing functions are executed after the previous promise completes.
- *
- * @constructor
- * @param {object} framework
- *        A back reference to the framework which makes use of the class. It is
- *        passed to each command callback.
- * @param {function[]} commandList
- *        Commands to set during initialization
- */
-function CommandChain(framework, commandList) {
-  this._framework = framework;
-  this.commands = commandList || [ ];
+    if (navigator.platform.indexOf("Mac") == 0) {
+        var version = Cc["@mozilla.org/system-info;1"]
+                        .getService(SpecialPowers.Ci.nsIPropertyBag2)
+                        .getProperty("version");
+        // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x !
+        // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here
+        // is the Darwin version.
+        is106orOlder = (parseFloat(version) < 11.0);
+    }
+    return is106orOlder;
 }
 
-CommandChain.prototype = {
-  /**
-   * Start the command chain.  This returns a promise that always resolves
-   * cleanly (this catches errors and fails the test case).
-   */
-  execute: function () {
-    return this.commands.reduce((prev, next, i) => {
-      if (typeof next !== 'function' || !next.name) {
-        throw new Error('registered non-function' + next);
-      }
-
-      return prev.then(() => {
-        info('Run step ' + (i + 1) + ': ' + next.name);
-        return Promise.race([ next(this._framework), rejectOnUnexpectedEvent ]);
-      });
-    }, Promise.resolve())
-      .catch(e =>
-             ok(false, 'Error in test execution: ' + e +
-                ((typeof e.stack === 'string') ?
-                 (' ' + e.stack.split('\n').join(' ... ')) : '')));
-  },
-
-  /**
-   * Add new commands to the end of the chain
-   */
-  append: function(commands) {
-    this.commands = this.commands.concat(commands);
-  },
-
-  /**
-   * Returns the index of the specified command in the chain.
-   */
-  indexOf: function(functionOrName) {
-    if (typeof functionOrName === 'string') {
-      return this.commands.findIndex(f => f.name === functionOrName);
-    }
-    return this.commands.indexOf(functionOrName);
-  },
-
-  /**
-   * Inserts the new commands after the specified command.
-   */
-  insertAfter: function(functionOrName, commands) {
-    this._insertHelper(functionOrName, commands, 1);
-  },
-
-  /**
-   * Inserts the new commands before the specified command.
-   */
-  insertBefore: function(functionOrName, commands) {
-    this._insertHelper(functionOrName, commands, 0);
-  },
-
-  _insertHelper: function(functionOrName, commands, delta) {
-    var index = this.indexOf(functionOrName);
-
-    if (index >= 0) {
-      this.commands = [].concat(
-        this.commands.slice(0, index + delta),
-        commands,
-        this.commands.slice(index + delta));
-    }
-  },
-
-  /**
-   * Removes the specified command, returns what was removed.
-   */
-  remove: function(functionOrName) {
-    var index = this.indexOf(functionOrName);
-    if (index >= 0) {
-      return this.commands.splice(index, 1);
-    }
-    return [];
-  },
-
-  /**
-   * Removes all commands after the specified one, returns what was removed.
-   */
-  removeAfter: function(functionOrName) {
-    var index = this.indexOf(functionOrName);
-    if (index >= 0) {
-      return this.commands.splice(index + 1);
-    }
-    return [];
-  },
-
-  /**
-   * Removes all commands before the specified one, returns what was removed.
-   */
-  removeBefore: function(functionOrName) {
-    var index = this.indexOf(functionOrName);
-    if (index >= 0) {
-      return this.commands.splice(0, index);
-    }
-    return [];
-  },
-
-  /**
-   * Replaces a single command, returns what was removed.
-   */
-  replace: function(functionOrName, commands) {
-    this.insertBefore(functionOrName, commands);
-    return this.remove(functionOrName);
-  },
-
-  /**
-   * Replaces all commands after the specified one, returns what was removed.
-   */
-  replaceAfter: function(functionOrName, commands) {
-    var oldCommands = this.removeAfter(functionOrName);
-    this.append(commands);
-    return oldCommands;
-  },
-
-  /**
-   * Replaces all commands before the specified one, returns what was removed.
-   */
-  replaceBefore: function(functionOrName, commands) {
-    var oldCommands = this.removeBefore(functionOrName);
-    this.insertBefore(functionOrName, commands);
-    return oldCommands;
-  },
-
-  /**
-   * Remove all commands whose name match the specified regex.
-   */
-  filterOut: function (id_match) {
-    this.commands = this.commands.filter(c => !id_match.test(c.name));
-  }
-};
-
-
-function IsMacOSX10_6orOlder() {
-  if (navigator.platform.indexOf("Mac") !== 0) {
-    return false;
-  }
-
-  var version = Cc["@mozilla.org/system-info;1"]
-      .getService(Ci.nsIPropertyBag2)
-      .getProperty("version");
-  // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x !
-  // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here
-  // is the Darwin version.
-  return (parseFloat(version) < 11.0);
-}
-
-(function(){
-  var el = document.createElement("link");
-  el.rel = "stylesheet";
-  el.type = "text/css";
-  el.href= "/tests/SimpleTest/test.css";
-  document.head.appendChild(el);
-}());
--- a/dom/media/tests/mochitest/identity/identityevent.js
+++ b/dom/media/tests/mochitest/identity/identityevent.js
@@ -1,12 +1,12 @@
 (function(g) {
   'use strict';
 
-  g.trapIdentityEvents = target => {
+  g.trapIdentityEvents = function(target) {
     var state = {};
     var identityEvents = ['idpassertionerror', 'idpvalidationerror',
                           'identityresult', 'peeridentity'];
     identityEvents.forEach(function(name) {
       target.addEventListener(name, function(e) {
         state[name] = e;
       }, false);
     });
--- a/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html
+++ b/dom/media/tests/mochitest/identity/test_getIdentityAssertion.html
@@ -1,110 +1,117 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <script type="application/javascript">var scriptRelativePath = "../";</script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../head.js"></script>
   <script type="application/javascript" src="../pc.js"></script>
+  <script type="application/javascript" src="../templates.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
-    title: "getIdentityAssertion Tests",
-    bug: "942367"
+    title: "getIdentityAssertion Tests"
   });
 
 function checkIdentity(assertion, identity) {
   // here we dig into the payload, which means we need to know something
   // about how the IdP actually works (not good in general, but OK here)
   var assertion = JSON.parse(atob(assertion)).assertion;
   var user = JSON.parse(assertion).username;
   is(user, identity, "id should be '" + identity + "' is '" + user + "'");
 }
 
 var test;
 function theTest() {
   test = new PeerConnectionTest();
   test.setMediaConstraints([{audio: true}], [{audio: true}]);
   test.chain.removeAfter('PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE');
   test.chain.append([
-    function GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER(test) {
-      return new Promise(resolve => {
-        test.pcLocal._pc.onidpassertionerror = function(e) {
-          ok(e, "getIdentityAssertion must fail without provider");
-          resolve();
-        };
-        test.pcLocal._pc.getIdentityAssertion();
-      });
-    },
-    function GET_IDENTITY_ASSERTION_FIRES_EVENTUALLY_AND_SUBSEQUENTLY(test) {
-      return new Promise(resolve => {
-        var fired = 0;
-        test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html');
-        test.pcLocal._pc.onidentityresult = function(e) {
-          fired++;
-          if (fired == 1) {
-            ok(true, "identityresult fired");
-            checkIdentity(e.assertion, 'someone@example.com');
-          } else if (fired == 2) {
-            ok(true, "identityresult fired 2x");
-            checkIdentity(e.assertion, 'someone@example.com');
-            resolve();
-          }
-        };
-        test.pcLocal._pc.onidpassertionerror = function(e) {
-          ok(false, "error event fired");
-          resolve();
-        };
-        test.pcLocal._pc.getIdentityAssertion();
-        test.pcLocal._pc.getIdentityAssertion();
-      });
+  [
+    "GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER",
+    function(test) {
+      test.pcLocal._pc.onidpassertionerror = function(e) {
+        ok(e, "getIdentityAssertion must fail without provider");
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion();
     },
-    function GET_IDENTITY_ASSERTION_FAILS(test) {
-      return new Promise(resolve => {
-        test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error');
-        test.pcLocal._pc.onidentityresult = function(e) {
-          ok(false, "Should not get an identity result");
-          resolve();
-        };
-        test.pcLocal._pc.onidpassertionerror = function(err) {
-          ok(err, "Got error event from getIdentityAssertion");
-          resolve();
-        };
-        test.pcLocal._pc.getIdentityAssertion();
-      });
-    },
-    function GET_IDENTITY_ASSERTION_IDP_NOT_READY(test) {
-      return new Promise(resolve => {
-        test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error:ready');
-        test.pcLocal._pc.onidentityresult = function(e) {
-          ok(false, "Should not get an identity result");
-          resolve();
-        };
-        test.pcLocal._pc.onidpassertionerror = function(e) {
-          ok(e, "Got error callback from getIdentityAssertion");
-          resolve();
-        };
-        test.pcLocal._pc.getIdentityAssertion();
-      });
-    },
-    function GET_IDENTITY_ASSERTION_WITH_SPECIFIC_NAME(test) {
-      return new Promise(resolve => {
-        test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'user@example.com');
-        test.pcLocal._pc.onidentityresult = function(e) {
-          checkIdentity(e.assertion, 'user@example.com');
-          resolve();
-        };
-        test.pcLocal._pc.onidpassertionerror = function(e) {
-          ok(false, "Got error callback from getIdentityAssertion");
-          resolve();
-        };
-        test.pcLocal._pc.getIdentityAssertion();
-      });
+  ],
+  [
+    "GET_IDENTITY_ASSERTION_FIRES_EVENTUALLY_AND_SUBSEQUENTLY",
+    function(test) {
+      var fired = 0;
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html');
+      test.pcLocal._pc.onidentityresult = function(e) {
+        fired++;
+        if (fired == 1) {
+          ok(true, "identityresult fired");
+          checkIdentity(e.assertion, 'someone@example.com');
+        } else if (fired == 2) {
+          ok(true, "identityresult fired 2x");
+          checkIdentity(e.assertion, 'someone@example.com');
+          test.next();
+        }
+      };
+      test.pcLocal._pc.onidpassertionerror = function(e) {
+        ok(false, "error event fired");
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion();
+      test.pcLocal._pc.getIdentityAssertion();
     }
+  ],
+  [
+    "GET_IDENTITY_ASSERTION_FAILS",
+    function(test) {
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error');
+      test.pcLocal._pc.onidentityresult = function(e) {
+        ok(false, "Should not get an identity result");
+        test.next();
+      };
+      test.pcLocal._pc.onidpassertionerror = function(err) {
+        ok(err, "Got error event from getIdentityAssertion");
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion();
+    }
+  ],
+  [
+    "GET_IDENTITY_ASSERTION_IDP_NOT_READY",
+    function(test) {
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error:ready');
+      test.pcLocal._pc.onidentityresult = function(e) {
+        ok(false, "Should not get an identity result");
+        test.next();
+      };
+      test.pcLocal._pc.onidpassertionerror = function(e) {
+        ok(e, "Got error callback from getIdentityAssertion");
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion();
+    }
+  ],
+  [
+    "GET_IDENTITY_ASSERTION_WITH_SPECIFIC_NAME",
+    function(test) {
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'user@example.com');
+      test.pcLocal._pc.onidentityresult = function(e) {
+        checkIdentity(e.assertion, 'user@example.com');
+        test.next();
+      };
+      test.pcLocal._pc.onidpassertionerror = function(e) {
+        ok(false, "Got error callback from getIdentityAssertion");
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion();
+    }
+  ]
   ]);
   test.run();
 }
 runNetworkTest(theTest);
 
 </script>
 </pre>
 </body>
--- a/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html
+++ b/dom/media/tests/mochitest/identity/test_peerConnection_peerIdentity.html
@@ -1,21 +1,26 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <script type="application/javascript">var scriptRelativePath = "../";</script>
+  <meta charset="utf-8"/>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../head.js"></script>
   <script type="application/javascript" src="../pc.js"></script>
+  <script type="application/javascript" src="../templates.js"></script>
   <script type="application/javascript" src="../blacksilence.js"></script>
+  <script type="application/javascript" src="../turnConfig.js"></script>
 </head>
 <body>
+<div id="display"></div>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
-    title: "setIdentityProvider leads to peerIdentity and assertions in SDP",
-    bug: "942367"
+    title: "setIdentityProvider leads to peerIdentity and assertions in SDP"
   });
 
 var test;
 function theTest() {
   var id1 = 'someone@test1.example.com';
   var id2 = 'someone@test2.example.com';
   test = new PeerConnectionTest({
     config_local: {
@@ -38,36 +43,48 @@ function theTest() {
   }, {
     video: true,
     fake: true,
     peerIdentity: id1
   }]);
   test.setIdentityProvider(test.pcLocal, 'test1.example.com', 'idp.html');
   test.setIdentityProvider(test.pcRemote, 'test2.example.com', 'idp.html');
   test.chain.append([
-
-    function PEER_IDENTITY_IS_SET_CORRECTLY(test) {
+  [
+    "PEER_IDENTITY_IS_SET_CORRECTLY",
+    function(test) {
       // no need to wait to check identity in this case,
       // setRemoteDescription should wait for the IdP to complete
       function checkIdentity(pc, pfx, idp, name) {
         is(pc.peerIdentity.idp, idp, pfx + "IdP is correct");
         is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct");
       }
 
       checkIdentity(test.pcLocal._pc, "local: ", "test2.example.com", "someone");
       checkIdentity(test.pcRemote._pc, "remote: ", "test1.example.com", "someone");
-    },
-
-    function REMOTE_STREAMS_ARE_RESTRICTED(test) {
+      test.next();
+    }
+  ],
+  [
+    "REMOTE_STREAMS_ARE_RESTRICTED",
+    function(test) {
       var remoteStream = test.pcLocal._pc.getRemoteStreams()[0];
-      return Promise.all([
-        new Promise(done => audioIsSilence(true, remoteStream, done)),
-        new Promise(done => videoIsBlack(true, remoteStream, done))
-      ]);
+      var oneDone = false;
+      function done() {
+        if (!oneDone) {
+          oneDone = true;
+          return;
+        }
+        test.next();
+      }
+
+      audioIsSilence(true, remoteStream, done);
+      videoIsBlack(true, remoteStream, done);
     }
+  ],
   ]);
   test.run();
 }
 runNetworkTest(theTest);
 
 </script>
 </pre>
 </body>
--- a/dom/media/tests/mochitest/identity/test_setIdentityProvider.html
+++ b/dom/media/tests/mochitest/identity/test_setIdentityProvider.html
@@ -1,95 +1,115 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <script type="application/javascript">var scriptRelativePath = "../";</script>
+  <meta charset="utf-8"/>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../head.js"></script>
   <script type="application/javascript" src="../pc.js"></script>
+  <script type="application/javascript" src="../templates.js"></script>
   <script type="application/javascript" src="identityevent.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
-    title: "setIdentityProvider leads to peerIdentity and assertions in SDP",
-    bug: "942367"
+    title: "setIdentityProvider leads to peerIdentity and assertions in SDP"
   });
 
 var test;
 function theTest() {
   test = new PeerConnectionTest();
   test.setMediaConstraints([{audio: true}], [{audio: true}]);
   test.setIdentityProvider(test.pcLocal, "test1.example.com", "idp.html", "someone");
   test.setIdentityProvider(test.pcRemote, "test2.example.com", "idp.html", "someone");
 
   var localEvents = trapIdentityEvents(test.pcLocal._pc);
   var remoteEvents = trapIdentityEvents(test.pcRemote._pc);
 
   test.chain.append([
-    function PEER_IDENTITY_IS_SET_CORRECTLY(test) {
+  [
+    "PEER_IDENTITY_IS_SET_CORRECTLY",
+    function(test) {
       var outstanding = 0;
       // we have to wait for the identity result in order to get the actual
       // identity information, since the call will complete before the identity
       // provider has a chance to finish verifying... that's OK, but it makes
       // testing more difficult
 
-      function checkOrSetupCheck(pc, prefix, idp, name) {
+      function checkOrSetupCheck(pc, pfx, idp, name) {
         function checkIdentity() {
-          ok(pc.peerIdentity, prefix + "peerIdentity is set");
-          is(pc.peerIdentity.idp, idp, prefix + "IdP is correct");
-          is(pc.peerIdentity.name, name + "@" + idp, prefix + "identity is correct");
+          ok(pc.peerIdentity, pfx + "peerIdentity is set");
+          is(pc.peerIdentity.idp, idp, pfx + "IdP is correct");
+          is(pc.peerIdentity.name, name + "@" + idp, pfx + "identity is correct");
         }
         if (pc.peerIdentity) {
-          info(prefix + "peerIdentity already set");
+          info(pfx + "peerIdentity already set");
           checkIdentity();
-          return Promise.resolve();
+        } else {
+          ++outstanding;
+          info(pfx + "setting onpeeridentity handler");
+          pc.onpeeridentity = function checkIdentityEvent(e) {
+            info(pfx + "checking peerIdentity");
+            checkIdentity();
+            --outstanding;
+            if (outstanding <= 0) {
+              test.next();
+            }
+          };
         }
-
-        return new Promise(resolve => {
-          info(prefix + "setting onpeeridentity handler");
-          pc.onpeeridentity = e => {
-            checkIdentity();
-            resolve();
-          };
-        });
       }
 
-      return Promise.all([
-        checkOrSetupCheck(test.pcLocal._pc, "local: ", "test2.example.com", "someone"),
-        checkOrSetupCheck(test.pcRemote._pc, "remote: ", "test1.example.com", "someone")
-      ]);
-    },
-
-    function CHECK_IDENTITY_EVENTS(test) {
+      checkOrSetupCheck(test.pcLocal._pc, "local: ", "test2.example.com", "someone");
+      checkOrSetupCheck(test.pcRemote._pc, "remote: ", "test1.example.com", "someone");
+      if (outstanding <= 0) {
+        test.next();
+      }
+    }
+  ],
+  [
+    "CHECK_IDENTITY_EVENTS",
+    function(test) {
       ok(!localEvents.idpassertionerror , "No assertion generation errors on local");
       ok(!remoteEvents.idpassertionerror, "No assertion generation errors on remote");
       ok(!localEvents.idpvalidationerror, "No assertion validation errors on local");
-      ok(!remoteEvents.idpvalidationerror, "No assertion validation errors on remote");
+      ok( !remoteEvents.idpvalidationerror, "No assertion validation errors on remote");
       ok(localEvents.identityresult, "local acquired identity assertions");
       ok(remoteEvents.identityresult, "remote acquired identity assertions");
       ok(localEvents.peeridentity, "local got peer identity");
       ok(remoteEvents.peeridentity, "remote got peer identity");
-    },
-
-    function OFFERS_AND_ANSWERS_INCLUDE_IDENTITY(test) {
+      test.next();
+    }
+  ],
+  [
+    "OFFERS_AND_ANSWERS_INCLUDE_IDENTITY",
+    function(test) {
       ok(test.originalOffer.sdp.contains("a=identity"), "a=identity is in the offer SDP");
       ok(test.originalAnswer.sdp.contains("a=identity"), "a=identity is in the answer SDP");
-    },
-
-    function DESCRIPTIONS_CONTAIN_IDENTITY(test) {
+      test.next();
+    }
+  ],
+  [
+    "DESCRIPTIONS_CONTAIN_IDENTITY",
+    function(test) {
       ok(test.pcLocal.localDescription.sdp.contains("a=identity"),
-         "a=identity is in the local copy of the offer");
+                         "a=identity is in the local copy of the offer");
       ok(test.pcRemote.localDescription.sdp.contains("a=identity"),
-         "a=identity is in the remote copy of the offer");
+                         "a=identity is in the remote copy of the offer");
       ok(test.pcLocal.remoteDescription.sdp.contains("a=identity"),
-         "a=identity is in the local copy of the answer");
+                         "a=identity is in the local copy of the answer");
       ok(test.pcRemote.remoteDescription.sdp.contains("a=identity"),
-         "a=identity is in the remote copy of the answer");
+                         "a=identity is in the remote copy of the answer");
+      test.next();
     }
+  ]
   ]);
   test.run();
 }
 runNetworkTest(theTest);
 
+
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html
+++ b/dom/media/tests/mochitest/identity/test_setIdentityProviderWithErrors.html
@@ -1,74 +1,85 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <script type="application/javascript">var scriptRelativePath = "../";</script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="../head.js"></script>
   <script type="application/javascript" src="../pc.js"></script>
+  <script type="application/javascript" src="../templates.js"></script>
   <script type="application/javascript" src="identityevent.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
-    title: "Identity Provider returning errors is handled correctly",
-    bug: "942367"
+    title: "Identity Provider returning errors is handled correctly"
   });
 
+var test;
 runNetworkTest(function () {
-  var test = new PeerConnectionTest();
+  test = new PeerConnectionTest();
   test.setMediaConstraints([{audio: true}], [{audio: true}]);
   // first example generates an error
   test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error', 'nobody');
   // second generates a bad assertion; which fails to validate
   test.setIdentityProvider(test.pcRemote, 'example.com', 'idp.html#bad', 'nobody');
 
   var localEvents = trapIdentityEvents(test.pcLocal._pc);
   var remoteEvents = trapIdentityEvents(test.pcRemote._pc);
 
   test.chain.append([
-    function CHECK_IDENTITY_EVENTS(test) {
-      function checkEvents() {
-        ok(localEvents.idpassertionerror, 'local assertion generation should fail (idpassertionerror)');
-        is(localEvents.idpassertionerror.idp, 'example.com', 'event IdP is correct');
-        is(localEvents.idpassertionerror.protocol, 'idp.html#error', 'event IdP protocol is #error');
-        ok(!remoteEvents.idpassertionerror, 'remote assertion generation should succeed (idpassertionerror)');
-        ok(!localEvents.identityresult, 'local assertion generation should fail (identityresult)');
-        ok(remoteEvents.identityresult, 'remote assertion generation should succeed (identityresult)');
+    [
+      'CHECK_IDENTITY_EVENTS',
+      function(test) {
+        function checkEvents() {
+          ok(localEvents.idpassertionerror, 'local assertion generation should fail (idpassertionerror)');
+          is(localEvents.idpassertionerror.idp, 'example.com', 'event IdP is correct');
+          is(localEvents.idpassertionerror.protocol, 'idp.html#error', 'event IdP protocol is #error');
+          ok(!remoteEvents.idpassertionerror, 'remote assertion generation should succeed (idpassertionerror)');
+          ok(!localEvents.identityresult, 'local assertion generation should fail (identityresult)');
+          ok(remoteEvents.identityresult, 'remote assertion generation should succeed (identityresult)');
 
-        ok(!localEvents.peeridentity, 'no peer identity event for local peer');
-        ok(!remoteEvents.peeridentity, 'no peer identity event for remote peer');
-        ok(localEvents.idpvalidationerror, 'local fails to validate');
-        is(localEvents.idpvalidationerror.idp, 'example.com', 'event IdP is correct');
-        is(localEvents.idpvalidationerror.protocol, 'idp.html#bad', 'event IdP protocol is #bad');
-        ok(!remoteEvents.idpvalidationerror, 'remote doesn\'t even see an assertion');
+          ok(!localEvents.peeridentity, 'no peer identity event for local peer');
+          ok(!remoteEvents.peeridentity, 'no peer identity event for remote peer');
+          ok(localEvents.idpvalidationerror, 'local fails to validate');
+          is(localEvents.idpvalidationerror.idp, 'example.com', 'event IdP is correct');
+          is(localEvents.idpvalidationerror.protocol, 'idp.html#bad', 'event IdP protocol is #bad');
+          ok(!remoteEvents.idpvalidationerror, 'remote doesn\'t even see an assertion');
 
-      }
+          test.next();
+        }
 
-      // we actually have to wait on this because IdP validation happens asynchronously
-      if (localEvents.idpvalidationerror) {
-        checkEvents();
-        return Promise.resolve();
+        // we actually have to wait on this because IdP validation happens asynchronously
+        if (localEvents.idpvalidationerror) {
+          checkEvents();
+        } else {
+          // have to let the other event handler have a chance to record success
+          // before we run the checks that rely on that recording
+          test.pcLocal._pc.onidpvalidationerror = setTimeout.bind(window, checkEvents, 1);
+        }
       }
-      // have to let the other event handler have a chance to record success
-      // before we run the checks that rely on that recording
-      return new Promise(resolve => {
-        test.pcLocal._pc.onidpvalidationerror = resolve;
-      }).then(checkEvents);
-    },
-
-    function PEER_IDENTITY_IS_EMPTY(test) {
-      ok(!test.pcLocal._pc.peerIdentity, 'local peerIdentity is not set');
-      ok(!test.pcRemote._pc.peerIdentity, 'remote peerIdentity is not set');
-    },
-
-    function ONLY_REMOTE_SDP_INCLUDES_IDENTITY_ASSERTION(test) {
-      ok(!test.originalOffer.sdp.contains('a=identity'), 'a=identity not contained in the offer SDP');
-      ok(test.originalAnswer.sdp.contains('a=identity'), 'a=identity is contained in the answer SDP');
-    }
+    ],
+    [
+      'PEER_IDENTITY_IS_EMPTY',
+      function(test) {
+        ok(!test.pcLocal._pc.peerIdentity, 'local peerIdentity is not set');
+        ok(!test.pcRemote._pc.peerIdentity, 'remote peerIdentity is not set');
+        test.next();
+      }
+    ],
+    [
+      'ONLY_REMOTE_SDP_INCLUDES_IDENTITY_ASSERTION',
+      function(test) {
+        ok(!test.originalOffer.sdp.contains('a=identity'), 'a=identity not contained in the offer SDP');
+        ok(test.originalAnswer.sdp.contains('a=identity'), 'a=identity is contained in the answer SDP');
+        test.next();
+      }
+    ]
   ]);
   test.run();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -32,123 +32,137 @@ MediaStreamPlayback.prototype = {
    *
    * @param {Boolean} isResume specifies if this media element is being resumed
    *                           from a previous run
    * @param {Function} onSuccess the success callback if the media playback
    *                             start and stop cycle completes successfully
    * @param {Function} onError the error callback if the media playback
    *                           start and stop cycle fails
    */
-  playMedia : function(isResume) {
-    return this.startMedia(isResume)
-      .then(() => this.stopMediaElement());
+  playMedia : function MSP_playMedia(isResume, onSuccess, onError) {
+    var self = this;
+
+    this.startMedia(isResume, function() {
+      self.stopMediaElement();
+      onSuccess();
+    }, onError);
   },
 
   /**
    * Starts the media with the associated stream.
    *
    * @param {Boolean} isResume specifies if the media element playback
    *                           is being resumed from a previous run
+   * @param {Function} onSuccess the success function call back
+   *                             if media starts correctly
+   * @param {Function} onError the error function call back
+   *                           if media fails to start
    */
-  startMedia : function(isResume) {
+  startMedia : function MSP_startMedia(isResume, onSuccess, onError) {
+    var self = this;
     var canPlayThroughFired = false;
 
     // If we're initially running this media, check that the time is zero
     if (!isResume) {
       is(this.mediaStream.currentTime, 0,
          "Before starting the media element, currentTime = 0");
     }
 
-    return new Promise((resolve, reject) => {
-      /**
-       * Callback fired when the canplaythrough event is fired. We only
-       * run the logic of this function once, as this event can fire
-       * multiple times while a HTMLMediaStream is playing content from
-       * a real-time MediaStream.
-       */
-      var canPlayThroughCallback = () => {
-        // Disable the canplaythrough event listener to prevent multiple calls
-        canPlayThroughFired = true;
-        this.mediaElement.removeEventListener('canplaythrough',
-                                              canPlayThroughCallback, false);
+    /**
+     * Callback fired when the canplaythrough event is fired. We only
+     * run the logic of this function once, as this event can fire
+     * multiple times while a HTMLMediaStream is playing content from
+     * a real-time MediaStream.
+     */
+    var canPlayThroughCallback = function() {
+      // Disable the canplaythrough event listener to prevent multiple calls
+      canPlayThroughFired = true;
+      self.mediaElement.removeEventListener('canplaythrough',
+        canPlayThroughCallback, false);
 
-        is(this.mediaElement.paused, false,
-           "Media element should be playing");
-        is(this.mediaElement.duration, Number.POSITIVE_INFINITY,
-           "Duration should be infinity");
+      is(self.mediaElement.paused, false,
+        "Media element should be playing");
+      is(self.mediaElement.duration, Number.POSITIVE_INFINITY,
+        "Duration should be infinity");
 
-        // When the media element is playing with a real-time stream, we
-        // constantly switch between having data to play vs. queuing up data,
-        // so we can only check that the ready state is one of those two values
-        ok(this.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA ||
-           this.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA,
-           "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA");
-
-        is(this.mediaElement.seekable.length, 0,
-           "Seekable length shall be zero");
-        is(this.mediaElement.buffered.length, 0,
-           "Buffered length shall be zero");
+      // When the media element is playing with a real-time stream, we
+      // constantly switch between having data to play vs. queuing up data,
+      // so we can only check that the ready state is one of those two values
+      ok(self.mediaElement.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA ||
+         self.mediaElement.readyState === HTMLMediaElement.HAVE_CURRENT_DATA,
+         "Ready state shall be HAVE_ENOUGH_DATA or HAVE_CURRENT_DATA");
 
-        is(this.mediaElement.seeking, false,
-           "MediaElement is not seekable with MediaStream");
-        ok(isNaN(this.mediaElement.startOffsetTime),
-           "Start offset time shall not be a number");
-        is(this.mediaElement.loop, false, "Loop shall be false");
-        is(this.mediaElement.preload, "", "Preload should not exist");
-        is(this.mediaElement.src, "", "No src should be defined");
-        is(this.mediaElement.currentSrc, "",
-           "Current src should still be an empty string");
+      is(self.mediaElement.seekable.length, 0,
+         "Seekable length shall be zero");
+      is(self.mediaElement.buffered.length, 0,
+         "Buffered length shall be zero");
 
-        var timeUpdateCallback = () => {
-          if (this.mediaStream.currentTime > 0 &&
-              this.mediaElement.currentTime > 0) {
-            this.mediaElement.removeEventListener('timeupdate',
-                                                  timeUpdateCallback, false);
-            resolve();
-          }
-        };
+      is(self.mediaElement.seeking, false,
+         "MediaElement is not seekable with MediaStream");
+      ok(isNaN(self.mediaElement.startOffsetTime),
+         "Start offset time shall not be a number");
+      is(self.mediaElement.loop, false, "Loop shall be false");
+      is(self.mediaElement.preload, "", "Preload should not exist");
+      is(self.mediaElement.src, "", "No src should be defined");
+      is(self.mediaElement.currentSrc, "",
+         "Current src should still be an empty string");
 
-        // When timeupdate fires, we validate time has passed and move
-        // onto the success condition
-        this.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
-                                           false);
+      var timeUpdateFired = false;
 
-        // If timeupdate doesn't fire in enough time, we fail the test
-        setTimeout(() => {
-          this.mediaElement.removeEventListener('timeupdate',
-                                                timeUpdateCallback, false);
-          reject(new Error("timeUpdate event never fired"));
-        }, TIMEUPDATE_TIMEOUT_LENGTH);
+      var timeUpdateCallback = function() {
+        if (self.mediaStream.currentTime > 0 &&
+            self.mediaElement.currentTime > 0) {
+          timeUpdateFired = true;
+          self.mediaElement.removeEventListener('timeupdate',
+            timeUpdateCallback, false);
+          onSuccess();
+        }
       };
 
-      // Adds a listener intended to be fired when playback is available
-      // without further buffering.
-      this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
-                                         false);
+      // When timeupdate fires, we validate time has passed and move
+      // onto the success condition
+      self.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
+        false);
+
+      // If timeupdate doesn't fire in enough time, we fail the test
+      setTimeout(function() {
+        if (!timeUpdateFired) {
+          self.mediaElement.removeEventListener('timeupdate',
+            timeUpdateCallback, false);
+          onError("timeUpdate event never fired");
+        }
+      }, TIMEUPDATE_TIMEOUT_LENGTH);
+    };
 
-      // Hooks up the media stream to the media element and starts playing it
-      this.mediaElement.mozSrcObject = this.mediaStream;
-      this.mediaElement.play();
+    // Adds a listener intended to be fired when playback is available
+    // without further buffering.
+    this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
+      false);
+
+    // Hooks up the media stream to the media element and starts playing it
+    this.mediaElement.mozSrcObject = this.mediaStream;
+    this.mediaElement.play();
 
-      // If canplaythrough doesn't fire in enough time, we fail the test
-      setTimeout(() => {
-        this.mediaElement.removeEventListener('canplaythrough',
-                                              canPlayThroughCallback, false);
-        reject(new Error("canplaythrough event never fired"));
-      }, CANPLAYTHROUGH_TIMEOUT_LENGTH);
-    });
+    // If canplaythrough doesn't fire in enough time, we fail the test
+    setTimeout(function() {
+      if (!canPlayThroughFired) {
+        self.mediaElement.removeEventListener('canplaythrough',
+          canPlayThroughCallback, false);
+        onError("canplaythrough event never fired");
+      }
+    }, CANPLAYTHROUGH_TIMEOUT_LENGTH);
   },
 
   /**
    * Stops the media with the associated stream.
    *
    * Precondition: The media stream and element should both be actively
    *               being played.
    */
-  stopMediaElement : function() {
+  stopMediaElement : function MSP_stopMediaElement() {
     this.mediaElement.pause();
     this.mediaElement.mozSrcObject = null;
   }
 }
 
 
 /**
  * This class is basically the same as MediaStreamPlayback except
@@ -167,70 +181,69 @@ function LocalMediaStreamPlayback(mediaE
 LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype, {
 
   /**
    * Starts media with a media stream, runs it until a canplaythrough and
    * timeupdate event fires, and calls stop() on the stream.
    *
    * @param {Boolean} isResume specifies if this media element is being resumed
    *                           from a previous run
+   * @param {Function} onSuccess the success callback if the media element
+   *                             successfully fires ended on a stop() call
+   *                             on the stream
+   * @param {Function} onError the error callback if the media element fails
+   *                           to fire an ended callback on a stop() call
+   *                           on the stream
    */
   playMediaWithStreamStop : {
-    value: function(isResume) {
-      return this.startMedia(isResume)
-        .then(() => this.stopStreamInMediaPlayback())
-        .then(() => this.stopMediaElement());
+    value: function (isResume, onSuccess, onError) {
+      var self = this;
+
+      this.startMedia(isResume, function() {
+        self.stopStreamInMediaPlayback(function() {
+          self.stopMediaElement();
+          onSuccess();
+        }, onError);
+      }, onError);
     }
   },
 
   /**
    * Stops the local media stream while it's currently in playback in
    * a media element.
    *
    * Precondition: The media stream and element should both be actively
    *               being played.
    *
+   * @param {Function} onSuccess the success callback if the media element
+   *                             fires an ended event from stop() being called
+   * @param {Function} onError the error callback if the media element
+   *                           fails to fire an ended event from stop() being
+   *                           called
    */
   stopStreamInMediaPlayback : {
-    value: function () {
-      return new Promise((resolve, reject) => {
-        /**
-         * Callback fired when the ended event fires when stop() is called on the
-         * stream.
-         */
-        var endedCallback = () => {
-          this.mediaElement.removeEventListener('ended', endedCallback, false);
-          ok(true, "ended event successfully fired");
-          resolve();
-        };
+    value: function (onSuccess, onError) {
+      var endedFired = false;
+      var self = this;
 
-        this.mediaElement.addEventListener('ended', endedCallback, false);
-        this.mediaStream.stop();
+      /**
+       * Callback fired when the ended event fires when stop() is called on the
+       * stream.
+       */
+      var endedCallback = function() {
+        endedFired = true;
+        self.mediaElement.removeEventListener('ended', endedCallback, false);
+        ok(true, "ended event successfully fired");
+        onSuccess();
+      };
 
-        // If ended doesn't fire in enough time, then we fail the test
-        setTimeout(() => {
-          reject(new Error("ended event never fired"));
-        }, ENDED_TIMEOUT_LENGTH);
-      });
+      this.mediaElement.addEventListener('ended', endedCallback, false);
+      this.mediaStream.stop();
+
+      // If ended doesn't fire in enough time, then we fail the test
+      setTimeout(function() {
+        if (!endedFired) {
+          onError("ended event never fired");
+        }
+      }, ENDED_TIMEOUT_LENGTH);
     }
   }
 });
-
-// haxx to prevent SimpleTest from failing at window.onload
-function addLoadEvent() {}
-
-var scriptsReady = Promise.all([
-  "/tests/SimpleTest/SimpleTest.js",
-  "head.js"
-].map(script  => {
-  var el = document.createElement("script");
-  el.src = script;
-  document.head.appendChild(el);
-  return new Promise(r => el.onload = r);
-}));
-
-function createHTML(options) {
-  return scriptsReady.then(() => realCreateHTML(options));
-}
-
-function runTest(f) {
-  return scriptsReady.then(() => runTestWhenReady(f));
-}
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -1,17 +1,16 @@
 [DEFAULT]
 # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881
 skip-if = (os == 'win' && strictContentSandbox) || android_version == '10'
 support-files =
   head.js
   constraints.js
   dataChannel.js
   mediaStreamPlayback.js
-  network.js
   nonTrickleIce.js
   pc.js
   templates.js
   NetworkPreparationChromeScript.js
   blacksilence.js
   turnConfig.js
 
 [test_dataChannel_basicAudio.html]
@@ -40,17 +39,16 @@ skip-if = buildapp == 'b2g' || toolkit =
 skip-if = buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
 [test_getUserMedia_constraints.html]
 skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_constraints_mobile.html]
 skip-if = toolkit != 'gonk' && toolkit != 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_exceptions.html]
-[test_getUserMedia_callbacks.html]
 [test_getUserMedia_gumWithinGum.html]
 [test_getUserMedia_playAudioTwice.html]
 [test_getUserMedia_playVideoAudioTwice.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure; bug 926558
 [test_getUserMedia_playVideoTwice.html]
 [test_getUserMedia_stopAudioStream.html]
 [test_getUserMedia_stopAudioStreamWithFollowupAudio.html]
 [test_getUserMedia_stopVideoAudioStream.html]
@@ -109,18 +107,16 @@ skip-if = toolkit == 'gonk' # b2g (Bug 1
 [test_peerConnection_offerRequiresReceiveAudio.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_promiseSendOnly.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
-[test_peerConnection_callbacks.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_replaceTrack.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_syncSetDescription.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_setLocalAnswerInHaveLocalOffer.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_setLocalAnswerInStable.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
deleted file mode 100644
--- a/dom/media/tests/mochitest/network.js
+++ /dev/null
@@ -1,121 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-/**
- * Query function for determining if any IP address is available for
- * generating SDP.
- *
- * @return false if required additional network setup.
- */
-function isNetworkReady() {
-  // for gonk platform
-  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
-    var listService = SpecialPowers.Cc["@mozilla.org/network/interface-list-service;1"]
-                        .getService(SpecialPowers.Ci.nsINetworkInterfaceListService);
-    var itfList = listService.getDataInterfaceList(
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_MMS_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_SUPL_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_IMS_INTERFACES |
-          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_DUN_INTERFACES);
-    var num = itfList.getNumberOfInterface();
-    for (var i = 0; i < num; i++) {
-      var ips = {};
-      var prefixLengths = {};
-      var length = itfList.getInterface(i).getAddresses(ips, prefixLengths);
-
-      for (var j = 0; j < length; j++) {
-        var ip = ips.value[j];
-        // skip IPv6 address until bug 797262 is implemented
-        if (ip.indexOf(":") < 0) {
-          safeInfo("Network interface is ready with address: " + ip);
-          return true;
-        }
-      }
-    }
-    // ip address is not available
-    safeInfo("Network interface is not ready, required additional network setup");
-    return false;
-  }
-  safeInfo("Network setup is not required");
-  return true;
-}
-
-/**
- * Network setup utils for Gonk
- *
- * @return {object} providing functions for setup/teardown data connection
- */
-function getNetworkUtils() {
-  var url = SimpleTest.getTestFileURL("NetworkPreparationChromeScript.js");
-  var script = SpecialPowers.loadChromeScript(url);
-
-  var utils = {
-    /**
-     * Utility for setting up data connection.
-     *
-     * @param aCallback callback after data connection is ready.
-     */
-    prepareNetwork: function() {
-      return new Promise(resolve => {
-        script.addMessageListener('network-ready', () =>  {
-          info("Network interface is ready");
-          resolve();
-        });
-        info("Setting up network interface");
-        script.sendAsyncMessage("prepare-network", true);
-      });
-    },
-    /**
-     * Utility for tearing down data connection.
-     *
-     * @param aCallback callback after data connection is closed.
-     */
-    tearDownNetwork: function() {
-      if (!isNetworkReady()) {
-        info("No network to tear down");
-        return Promise.resolve();
-      }
-      return new Promise(resolve => {
-        script.addMessageListener('network-disabled', message => {
-          info("Network interface torn down");
-          script.destroy();
-          resolve();
-        });
-        info("Tearing down network interface");
-        script.sendAsyncMessage("network-cleanup", true);
-      });
-    }
-  };
-
-  return utils;
-}
-
-/**
- * Setup network on Gonk if needed and execute test once network is up
- *
- */
-function startNetworkAndTest() {
-  if (isNetworkReady()) {
-    return Promise.resolve();
-  }
-  var utils = getNetworkUtils();
-  // Trigger network setup to obtain IP address before creating any PeerConnection.
-  return utils.prepareNetwork();
-}
-
-/**
- * A wrapper around SimpleTest.finish() to handle B2G network teardown
- */
-function networkTestFinished() {
-  var p;
-  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
-    var utils = getNetworkUtils();
-    p = utils.tearDownNetwork();
-  } else {
-    p = Promise.resolve();
-  }
-  return p.then(() => finish());
-}
--- a/dom/media/tests/mochitest/nonTrickleIce.js
+++ b/dom/media/tests/mochitest/nonTrickleIce.js
@@ -1,60 +1,130 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function makeOffererNonTrickle(chain) {
   chain.replace('PC_LOCAL_SETUP_ICE_HANDLER', [
-    function PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER(test) {
-      // We need to install this callback before calling setLocalDescription
-      // otherwise we might miss callbacks
-      test.pcLocal.setupIceCandidateHandler(test, () => {});
-      // We ignore ICE candidates because we want the full offer
-    }
+    ['PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER',
+      function (test) {
+        test.pcLocalWaitingForEndOfTrickleIce = false;
+        // We need to install this callback before calling setLocalDescription
+        // otherwise we might miss callbacks
+        test.pcLocal.setupIceCandidateHandler(test, function () {
+            // We ignore ICE candidates because we want the full offer
+          } , function (label) {
+            if (test.pcLocalWaitingForEndOfTrickleIce) {
+              // This callback is needed for slow environments where ICE
+              // trickling has not finished before the other side needs the
+              // full SDP. In this case, this call to test.next() will complete
+              // the PC_REMOTE_WAIT_FOR_OFFER step (see below).
+              info("Looks like we were still waiting for Trickle to finish");
+              // TODO replace this with a Promise
+              test.next();
+            }
+          });
+        // We can't wait for trickle to finish here as it will only start once
+        // we have called setLocalDescription in the next step
+        test.next();
+      }
+    ]
   ]);
   chain.replace('PC_REMOTE_GET_OFFER', [
-    function PC_REMOTE_GET_FULL_OFFER(test) {
-      return test.pcLocal.endOfTrickleIce.then(() => {
+    ['PC_REMOTE_WAIT_FOR_OFFER',
+      function (test) {
+        if (test.pcLocal.endOfTrickleIce) {
+          info("Trickle ICE finished already");
+          test.next();
+        } else {
+          info("Waiting for trickle ICE to finish");
+          test.pcLocalWaitingForEndOfTrickleIce = true;
+          // In this case we rely on the callback from
+          // PC_LOCAL_SETUP_NOTRICKLE_ICE_HANDLER above to proceed to the next
+          // step once trickle is finished.
+        }
+      }
+    ],
+    ['PC_REMOTE_GET_FULL_OFFER',
+      function (test) {
         test._local_offer = test.pcLocal.localDescription;
         test._offer_constraints = test.pcLocal.constraints;
         test._offer_options = test.pcLocal.offerOptions;
-      });
-    }
+        test.next();
+      }
+    ]
   ]);
   chain.insertAfter('PC_REMOTE_SANE_REMOTE_SDP', [
-    function PC_REMOTE_REQUIRE_REMOTE_SDP_CANDIDATES(test) {
-      info("test.pcLocal.localDescription.sdp: " + JSON.stringify(test.pcLocal.localDescription.sdp));
-      info("test._local_offer.sdp" + JSON.stringify(test._local_offer.sdp));
-      ok(!test.localRequiresTrickleIce, "Local does NOT require trickle");
-      ok(test._local_offer.sdp.contains("a=candidate"), "offer has ICE candidates")
-      ok(test._local_offer.sdp.contains("a=end-of-candidates"), "offer has end-of-candidates");
-    }
+    ['PC_REMOTE_REQUIRE_REMOTE_SDP_CANDIDATES',
+      function (test) {
+        info("test.pcLocal.localDescription.sdp: " + JSON.stringify(test.pcLocal.localDescription.sdp));
+        info("test._local_offer.sdp" + JSON.stringify(test._local_offer.sdp));
+        ok(!test.localRequiresTrickleIce, "Local does NOT require trickle");
+        ok(test._local_offer.sdp.contains("a=candidate"), "offer has ICE candidates")
+        // TODO check for a=end-of-candidates once implemented
+        test.next();
+      }
+    ]
   ]);
 }
 
 function makeAnswererNonTrickle(chain) {
   chain.replace('PC_REMOTE_SETUP_ICE_HANDLER', [
-    function PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER(test) {
-      // We need to install this callback before calling setLocalDescription
-      // otherwise we might miss callbacks
-      test.pcRemote.setupIceCandidateHandler(test, () => {});
-      // We ignore ICE candidates because we want the full offer
-    }
+    ['PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER',
+      function (test) {
+        test.pcRemoteWaitingForEndOfTrickleIce = false;
+        // We need to install this callback before calling setLocalDescription
+        // otherwise we might miss callbacks
+        test.pcRemote.setupIceCandidateHandler(test, function () {
+          // We ignore ICE candidates because we want the full answer
+          }, function (label) {
+            if (test.pcRemoteWaitingForEndOfTrickleIce) {
+              // This callback is needed for slow environments where ICE
+              // trickling has not finished before the other side needs the
+              // full SDP. In this case this callback will call the step after
+              // PC_LOCAL_WAIT_FOR_ANSWER
+              info("Looks like we were still waiting for Trickle to finish");
+              // TODO replace this with a Promise
+              test.next();
+            }
+          });
+        // We can't wait for trickle to finish here as it will only start once
+        // we have called setLocalDescription in the next step
+        test.next();
+      }
+    ]
   ]);
   chain.replace('PC_LOCAL_GET_ANSWER', [
-    function PC_LOCAL_GET_FULL_ANSWER(test) {
-      return test.pcRemote.endOfTrickleIce.then(() => {
+    ['PC_LOCAL_WAIT_FOR_ANSWER',
+      function (test) {
+        if (test.pcRemote.endOfTrickleIce) {
+          info("Trickle ICE finished already");
+          test.next();
+        } else {
+          info("Waiting for trickle ICE to finish");
+          test.pcRemoteWaitingForEndOfTrickleIce = true;
+          // In this case we rely on the callback from
+          // PC_REMOTE_SETUP_NOTRICKLE_ICE_HANDLER above to proceed to the next
+          // step once trickle is finished.
+        }
+      }
+    ],
+    ['PC_LOCAL_GET_FULL_ANSWER',
+      function (test) {
         test._remote_answer = test.pcRemote.localDescription;
         test._answer_constraints = test.pcRemote.constraints;
-      });
-    }
+        test.next();
+      }
+    ]
   ]);
   chain.insertAfter('PC_LOCAL_SANE_REMOTE_SDP', [
-    function PC_LOCAL_REQUIRE_REMOTE_SDP_CANDIDATES(test) {
-      info("test.pcRemote.localDescription.sdp: " + JSON.stringify(test.pcRemote.localDescription.sdp));
-      info("test._remote_answer.sdp" + JSON.stringify(test._remote_answer.sdp));
-      ok(!test.remoteRequiresTrickleIce, "Remote does NOT require trickle");
-      ok(test._remote_answer.sdp.contains("a=candidate"), "answer has ICE candidates")
-      ok(test._remote_answer.sdp.contains("a=end-of-candidates"), "answer has end-of-candidates");
-    }
+    ['PC_LOCAL_REQUIRE_REMOTE_SDP_CANDIDATES',
+      function (test) {
+        info("test.pcRemote.localDescription.sdp: " + JSON.stringify(test.pcRemote.localDescription.sdp));
+        info("test._remote_answer.sdp" + JSON.stringify(test._remote_answer.sdp));
+        ok(!test.remoteRequiresTrickleIce, "Remote does NOT require trickle");
+        ok(test._remote_answer.sdp.contains("a=candidate"), "answer has ICE candidates")
+        // TODO check for a=end-of-candidates once implemented
+        test.next();
+      }
+    ]
   ]);
 }
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -24,91 +24,355 @@ const signalingStateTransitions = {
   "have-local-offer": ["have-remote-pranswer", "stable", "closed", "have-local-offer"],
   "have-remote-pranswer": ["stable", "closed", "have-remote-pranswer"],
   "have-remote-offer": ["have-local-pranswer", "stable", "closed", "have-remote-offer"],
   "have-local-pranswer": ["stable", "closed", "have-local-pranswer"],
   "closed": []
 }
 
 /**
+ * This class mimics a state machine and handles a list of commands by
+ * executing them synchronously.
+ *
+ * @constructor
+ * @param {object} framework
+ *        A back reference to the framework which makes use of the class. It's
+ *        getting passed in as parameter to each command callback.
+ * @param {Array[]} [commandList=[]]
+ *        Default commands to set during initialization
+ */
+function CommandChain(framework, commandList) {
+  this._framework = framework;
+
+  this._commands = commandList || [ ];
+  this._current = 0;
+
+  this.onFinished = null;
+}
+
+CommandChain.prototype = {
+
+  /**
+   * Returns the index of the current command of the chain
+   *
+   * @returns {number} Index of the current command
+   */
+  get current() {
+    return this._current;
+  },
+
+  /**
+   * Checks if the chain has already processed all the commands
+   *
+   * @returns {boolean} True, if all commands have been processed
+   */
+  get finished() {
+    return this._current === this._commands.length;
+  },
+
+  /**
+   * Returns the assigned commands of the chain.
+   *
+   * @returns {Array[]} Commands of the chain
+   */
+  get commands() {
+    return this._commands;
+  },
+
+  /**
+   * Sets new commands for the chain. All existing commands will be replaced.
+   *
+   * @param {Array[]} commands
+   *        List of commands
+   */
+  set commands(commands) {
+    this._commands = commands;
+  },
+
+  /**
+   * Execute the next command in the chain.
+   */
+  executeNext : function () {
+    var self = this;
+
+    function _executeNext() {
+      if (!self.finished) {
+        var step = self._commands[self._current];
+        self._current++;
+
+        self.currentStepLabel = step[0];
+        info("Run step: " + self.currentStepLabel);
+        step[1](self._framework);      // Execute step
+      }
+      else if (typeof(self.onFinished) === 'function') {
+        self.onFinished();
+      }
+    }
+
+    // To prevent building up the stack we have to execute the next
+    // step asynchronously
+    window.setTimeout(_executeNext, 0);
+  },
+
+  /**
+   * Add new commands to the end of the chain
+   *
+   * @param {Array[]} commands
+   *        List of commands
+   */
+  append: function (commands) {
+    this._commands = this._commands.concat(commands);
+  },
+
+  /**
+   * Returns the index of the specified command in the chain.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {number} Index of the command
+   */
+  indexOf: function (id) {
+    for (var i = 0; i < this._commands.length; i++) {
+      if (this._commands[i][0] === id) {
+        return i;
+      }
+    }
+
+    return -1;
+  },
+
+  /**
+   * Inserts the new commands after the specified command.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @param {Array[]} commands
+   *        List of commands
+   */
+  insertAfter: function (id, commands) {
+    var index = this.indexOf(id);
+
+    if (index > -1) {
+      var tail = this.removeAfter(id);
+
+      this.append(commands);
+      this.append(tail);
+    }
+  },
+
+  /**
+   * Inserts the new commands before the specified command.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @param {Array[]} commands
+   *        List of commands
+   */
+  insertBefore: function (id, commands) {
+    var index = this.indexOf(id);
+
+    if (index > -1) {
+      var tail = this.removeAfter(id);
+      var object = this.remove(id);
+
+      this.append(commands);
+      this.append(object);
+      this.append(tail);
+    }
+  },
+
+  /**
+   * Removes the specified command
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {object[]} Removed command
+   */
+  remove : function (id) {
+    return this._commands.splice(this.indexOf(id), 1);
+  },
+
+  /**
+   * Removes all commands after the specified one.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {object[]} Removed commands
+   */
+  removeAfter : function (id) {
+    var index = this.indexOf(id);
+
+    if (index > -1) {
+      return this._commands.splice(index + 1);
+    }
+
+    return null;
+  },
+
+  /**
+   * Removes all commands before the specified one.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {object[]} Removed commands
+   */
+  removeBefore : function (id) {
+    var index = this.indexOf(id);
+
+    if (index > -1) {
+      return this._commands.splice(0, index);
+    }
+
+    return null;
+  },
+
+  /**
+   * Replaces a single command.
+   *
+   * @param {string} id
+   *        Identifier of the command to be replaced
+   * @param {Array[]} commands
+   *        List of commands
+   * @returns {object[]} Removed commands
+   */
+  replace : function (id, commands) {
+    this.insertBefore(id, commands);
+    return this.remove(id);
+  },
+
+  /**
+   * Replaces all commands after the specified one.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {object[]} Removed commands
+   */
+  replaceAfter : function (id, commands) {
+    var oldCommands = this.removeAfter(id);
+    this.append(commands);
+
+    return oldCommands;
+  },
+
+  /**
+   * Replaces all commands before the specified one.
+   *
+   * @param {string} id
+   *        Identifier of the command
+   * @returns {object[]} Removed commands
+   */
+  replaceBefore : function (id, commands) {
+    var oldCommands = this.removeBefore(id);
+    this.insertBefore(id, commands);
+
+    return oldCommands;
+  },
+
+  /**
+   * Remove all commands whose identifiers match the specified regex.
+   *
+   * @param {regex} id_match
+   *        Regular expression to match command identifiers.
+   */
+  filterOut : function (id_match) {
+    for (var i = this._commands.length - 1; i >= 0; i--) {
+      if (id_match.test(this._commands[i][0])) {
+        this._commands.splice(i, 1);
+      }
+    }
+  }
+};
+
+/**
  * This class provides a state checker for media elements which store
  * a media stream to check for media attribute state and events fired.
  * When constructed by a caller, an object instance is created with
  * a media element, event state checkers for canplaythrough, timeupdate, and
  * time changing on the media element and stream.
  *
  * @param {HTMLMediaElement} element the media element being analyzed
  */
 function MediaElementChecker(element) {
   this.element = element;
   this.canPlayThroughFired = false;
   this.timeUpdateFired = false;
   this.timePassed = false;
 
-  var elementId = this.element.getAttribute('id');
+  var self = this;
+  var elementId = self.element.getAttribute('id');
 
   // When canplaythrough fires, we track that it's fired and remove the
   // event listener.
-  var canPlayThroughCallback = () => {
+  var canPlayThroughCallback = function() {
     info('canplaythrough fired for media element ' + elementId);
-    this.canPlayThroughFired = true;
-    this.element.removeEventListener('canplaythrough', canPlayThroughCallback,
+    self.canPlayThroughFired = true;
+    self.element.removeEventListener('canplaythrough', canPlayThroughCallback,
                                      false);
   };
 
   // When timeupdate fires, we track that it's fired and check if time
   // has passed on the media stream and media element.
-  var timeUpdateCallback = () => {
-    this.timeUpdateFired = true;
+  var timeUpdateCallback = function() {
+    self.timeUpdateFired = true;
     info('timeupdate fired for media element ' + elementId);
 
     // If time has passed, then track that and remove the timeupdate event
     // listener.
     if(element.mozSrcObject && element.mozSrcObject.currentTime > 0 &&
        element.currentTime > 0) {
       info('time passed for media element ' + elementId);
-      this.timePassed = true;
-      this.element.removeEventListener('timeupdate', timeUpdateCallback,
+      self.timePassed = true;
+      self.element.removeEventListener('timeupdate', timeUpdateCallback,
                                        false);
     }
   };
 
   element.addEventListener('canplaythrough', canPlayThroughCallback, false);
   element.addEventListener('timeupdate', timeUpdateCallback, false);
 }
 
 MediaElementChecker.prototype = {
 
   /**
    * Waits until the canplaythrough & timeupdate events to fire along with
    * ensuring time has passed on the stream and media element.
+   *
+   * @param {Function} onSuccess the success callback when media flow is
+   *                             established
    */
-  waitForMediaFlow: function() {
-    var elementId = this.element.getAttribute('id');
+  waitForMediaFlow : function MEC_WaitForMediaFlow(onSuccess) {
+    var self = this;
+    var elementId = self.element.getAttribute('id');
     info('Analyzing element: ' + elementId);
 
-    return waitUntil(() => this.canPlayThroughFired && this.timeUpdateFired && this.timePassed)
-      .then(() => ok(true, 'Media flowing for ' + elementId));
+    if(self.canPlayThroughFired && self.timeUpdateFired && self.timePassed) {
+      ok(true, 'Media flowing for ' + elementId);
+      onSuccess();
+    } else {
+      setTimeout(function() {
+        self.waitForMediaFlow(onSuccess);
+      }, 100);
+    }
   },
 
   /**
    * Checks if there is no media flow present by checking that the ready
    * state of the media element is HAVE_METADATA.
    */
-  checkForNoMediaFlow: function() {
+  checkForNoMediaFlow : function MEC_CheckForNoMediaFlow() {
     ok(this.element.readyState === HTMLMediaElement.HAVE_METADATA,
        'Media element has a ready state of HAVE_METADATA');
   }
 };
 
 /**
  * Only calls info() if SimpleTest.info() is available
  */
 function safeInfo(message) {
-  if (typeof info === "function") {
+  if (typeof(info) === "function") {
     info(message);
   }
 }
 
 // Also remove mode 0 if it's offered
 // Note, we don't bother removing the fmtp lines, which makes a good test
 // for some SDP parsing issues.
 function removeVP8(sdp) {
@@ -117,16 +381,138 @@ function removeVP8(sdp) {
   updated_sdp = updated_sdp.replace("RTP/SAVPF 120 126\r\n","RTP/SAVPF 126\r\n");
   updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack\r\n","");
   updated_sdp = updated_sdp.replace("a=rtcp-fb:120 nack pli\r\n","");
   updated_sdp = updated_sdp.replace("a=rtcp-fb:120 ccm fir\r\n","");
   return updated_sdp;
 }
 
 /**
+ * Query function for determining if any IP address is available for
+ * generating SDP.
+ *
+ * @return false if required additional network setup.
+ */
+function isNetworkReady() {
+  // for gonk platform
+  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
+    var listService = SpecialPowers.Cc["@mozilla.org/network/interface-list-service;1"]
+                        .getService(SpecialPowers.Ci.nsINetworkInterfaceListService);
+    var itfList = listService.getDataInterfaceList(
+          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_MMS_INTERFACES |
+          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_SUPL_INTERFACES |
+          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_IMS_INTERFACES |
+          SpecialPowers.Ci.nsINetworkInterfaceListService.LIST_NOT_INCLUDE_DUN_INTERFACES);
+    var num = itfList.getNumberOfInterface();
+    for (var i = 0; i < num; i++) {
+      var ips = {};
+      var prefixLengths = {};
+      var length = itfList.getInterface(i).getAddresses(ips, prefixLengths);
+
+      for (var j = 0; j < length; j++) {
+        var ip = ips.value[j];
+        // skip IPv6 address until bug 797262 is implemented
+        if (ip.indexOf(":") < 0) {
+          safeInfo("Network interface is ready with address: " + ip);
+          return true;
+        }
+      }
+    }
+    // ip address is not available
+    safeInfo("Network interface is not ready, required additional network setup");
+    return false;
+  }
+  safeInfo("Network setup is not required");
+  return true;
+}
+
+/**
+ * Network setup utils for Gonk
+ *
+ * @return {object} providing functions for setup/teardown data connection
+ */
+function getNetworkUtils() {
+  var url = SimpleTest.getTestFileURL("NetworkPreparationChromeScript.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  var utils = {
+    /**
+     * Utility for setting up data connection.
+     *
+     * @param aCallback callback after data connection is ready.
+     */
+    prepareNetwork: function(onSuccess) {
+      script.addMessageListener('network-ready', function (message) {
+        info("Network interface is ready");
+        onSuccess();
+      });
+      info("Setting up network interface");
+      script.sendAsyncMessage("prepare-network", true);
+    },
+    /**
+     * Utility for tearing down data connection.
+     *
+     * @param aCallback callback after data connection is closed.
+     */
+    tearDownNetwork: function(onSuccess, onFailure) {
+      if (isNetworkReady()) {
+        script.addMessageListener('network-disabled', function (message) {
+          info("Network interface torn down");
+          script.destroy();
+          onSuccess();
+        });
+        info("Tearing down network interface");
+        script.sendAsyncMessage("network-cleanup", true);
+      } else {
+        info("No network to tear down");
+        onFailure();
+      }
+    }
+  };
+
+  return utils;
+}
+
+/**
+ * Setup network on Gonk if needed and execute test once network is up
+ *
+ */
+function startNetworkAndTest(onSuccess) {
+  if (!isNetworkReady()) {
+    SimpleTest.waitForExplicitFinish();
+    var utils = getNetworkUtils();
+    // Trigger network setup to obtain IP address before creating any PeerConnection.
+    utils.prepareNetwork(onSuccess);
+  } else {
+    onSuccess();
+  }
+}
+
+/**
+ * A wrapper around SimpleTest.finish() to handle B2G network teardown
+ */
+function networkTestFinished() {
+  if ("nsINetworkInterfaceListService" in SpecialPowers.Ci) {
+    var utils = getNetworkUtils();
+    utils.tearDownNetwork(SimpleTest.finish, SimpleTest.finish);
+  } else {
+    SimpleTest.finish();
+  }
+}
+
+/**
+ * A wrapper around runTest() which handles B2G network setup and teardown
+ */
+function runNetworkTest(testFunction) {
+  startNetworkAndTest(function() {
+    runTest(testFunction);
+  });
+}
+
+/**
  * This class handles tests for peer connections.
  *
  * @constructor
  * @param {object} [options={}]
  *        Optional options for the peer connection test
  * @param {object} [options.commands=commandsPeerConnection]
  *        Commands to run for the test
  * @param {bool}   [options.is_local=true]
@@ -180,337 +566,637 @@ function PeerConnectionTest(options) {
   // Create command chain instance and assign default commands
   this.chain = new CommandChain(this, options.commands);
   if (!options.is_local) {
     this.chain.filterOut(/^PC_LOCAL/);
   }
   if (!options.is_remote) {
     this.chain.filterOut(/^PC_REMOTE/);
   }
-}
 
-/** TODO: consider removing this dependency on timeouts */
-function timerGuard(p, time, message) {
-  return Promise.race([
-    p,
-    wait(time).then(() => {
-      throw new Error('timeout after ' + (time / 1000) + 's: ' + message);
-    })
-  ]);
+  var self = this;
+  this.chain.onFinished = function () {
+    self.teardown();
+  };
 }
 
 /**
  * Closes the peer connection if it is active
  *
  * @param {Function} onSuccess
  *        Callback to execute when the peer connection has been closed successfully
  */
-PeerConnectionTest.prototype.closePC = function() {
+PeerConnectionTest.prototype.closePC = function PCT_closePC(onSuccess) {
   info("Closing peer connections");
 
-  var closeIt = pc => {
-    if (!pc || pc.signalingState === "closed") {
-      return Promise.resolve();
+  var self = this;
+  var closeTimeout = null;
+  var waitingForLocal = false;
+  var waitingForRemote = false;
+  var everythingClosed = false;
+
+  function verifyClosed() {
+    if ((self.waitingForLocal || self.waitingForRemote) ||
+      (self.pcLocal && (self.pcLocal.signalingState !== "closed")) ||
+      (self.pcRemote && (self.pcRemote.signalingState !== "closed"))) {
+      info("still waiting for closure");
     }
+    else if (!everythingClosed) {
+      info("No closure pending");
+      if (self.pcLocal) {
+        is(self.pcLocal.signalingState, "closed", "pcLocal is in 'closed' state");
+      }
+      if (self.pcRemote) {
+        is(self.pcRemote.signalingState, "closed", "pcRemote is in 'closed' state");
+      }
+      clearTimeout(closeTimeout);
+      everythingClosed = true;
+      onSuccess();
+    }
+  }
+
+  function signalingstatechangeLocalClose(e) {
+    info("'signalingstatechange' event received");
+    is(e.target.signalingState, "closed", "signalingState is closed");
+    self.waitingForLocal = false;
+    verifyClosed();
+  }
 
-    return new Promise(resolve => {
-      pc.onsignalingstatechange = e => {
-        is(e.target.signalingState, "closed", "signalingState is closed");
-        resolve();
-      };
-      pc.close();
-    });
-  };
+  function signalingstatechangeRemoteClose(e) {
+    info("'signalingstatechange' event received");
+    is(e.target.signalingState, "closed", "signalingState is closed");
+    self.waitingForRemote = false;
+    verifyClosed();
+  }
 
-  return timerGuard(Promise.all([
-    closeIt(this.pcLocal),
-    closeIt(this.pcRemote)
-  ]), 60000, "failed to close peer connection");
+  function closeEverything() {
+    if ((self.pcLocal) && (self.pcLocal.signalingState !== "closed")) {
+      info("Closing pcLocal");
+      self.pcLocal.onsignalingstatechange = signalingstatechangeLocalClose;
+      self.waitingForLocal = true;
+      self.pcLocal.close();
+    }
+    if ((self.pcRemote) && (self.pcRemote.signalingState !== "closed")) {
+      info("Closing pcRemote");
+      self.pcRemote.onsignalingstatechange = signalingstatechangeRemoteClose;
+      self.waitingForRemote = true;
+      self.pcRemote.close();
+    }
+    // give the signals handlers time to fire
+    setTimeout(verifyClosed, 1000);
+  }
+
+  closeTimeout = setTimeout(function() {
+    var closed = ((self.pcLocal && (self.pcLocal.signalingState === "closed")) &&
+      (self.pcRemote && (self.pcRemote.signalingState === "closed")));
+    ok(closed, "Closing PeerConnections timed out");
+    // it is not a success, but the show must go on
+    onSuccess();
+  }, 60000);
+
+  closeEverything();
 };
 
 /**
  * Close the open data channels, followed by the underlying peer connection
+ *
+ * @param {Function} onSuccess
+ *        Callback to execute when all connections have been closed
  */
-PeerConnectionTest.prototype.close = function() {
-  var allChannels = (this.pcLocal || this.pcRemote).dataChannels;
-  return timerGuard(
-    Promise.all(allChannels.map((channel, i) => this.closeDataChannels(i))),
-    60000, "failed to close data channels")
-    .then(() => this.closePC());
+PeerConnectionTest.prototype.close = function PCT_close(onSuccess) {
+  var self = this;
+  var pendingDcClose = []
+  var closeTimeout = null;
+
+  info("PeerConnectionTest.close() called");
+
+  function _closePeerConnection() {
+    info("Now closing PeerConnection");
+    self.closePC.call(self, onSuccess);
+  }
+
+  function _closePeerConnectionCallback(index) {
+    info("_closePeerConnection called with index " + index);
+    var pos = pendingDcClose.indexOf(index);
+    if (pos != -1) {
+      pendingDcClose.splice(pos, 1);
+    }
+    else {
+      info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose);
+    }
+    if (pendingDcClose.length === 0) {
+      clearTimeout(closeTimeout);
+      _closePeerConnection();
+    }
+  }
+
+  var myDataChannels = null;
+  if (self.pcLocal) {
+    myDataChannels = self.pcLocal.dataChannels;
+  }
+  else if (self.pcRemote) {
+    myDataChannels = self.pcRemote.dataChannels;
+  }
+  var length = myDataChannels.length;
+  for (var i = 0; i < length; i++) {
+    var dataChannel = myDataChannels[i];
+    if (dataChannel.readyState !== "closed") {
+      pendingDcClose.push(i);
+      self.closeDataChannels(i, _closePeerConnectionCallback);
+    }
+  }
+  if (pendingDcClose.length === 0) {
+    _closePeerConnection();
+  }
+  else {
+    closeTimeout = setTimeout(function() {
+      ok(false, "Failed to properly close data channels: " +
+        pendingDcClose);
+      _closePeerConnection();
+    }, 60000);
+  }
 };
 
 /**
  * Close the specified data channels
  *
  * @param {Number} index
  *        Index of the data channels to close on both sides
+ * @param {Function} onSuccess
+ *        Callback to execute when the data channels has been closed
  */
-PeerConnectionTest.prototype.closeDataChannels = function(index) {
+PeerConnectionTest.prototype.closeDataChannels = function PCT_closeDataChannels(index, onSuccess) {
   info("closeDataChannels called with index: " + index);
   var localChannel = null;
   if (this.pcLocal) {
     localChannel = this.pcLocal.dataChannels[index];
   }
   var remoteChannel = null;
   if (this.pcRemote) {
     remoteChannel = this.pcRemote.dataChannels[index];
   }
 
-  // We need to setup all the close listeners before calling close
-  var setupClosePromise = channel => {
-    if (!channel) {
-      return Promise.resolve();
-    }
-    return new Promise(resolve => {
-      channel.onclose = () => {
-        is(channel.readyState, "closed", name + " channel " + index + " closed");
-        resolve();
-      };
-    });
-  };
+  var self = this;
+  var wait = false;
+  var pollingMode = false;
+  var everythingClosed = false;
+  var verifyInterval = null;
+  var remoteCloseTimer = null;
 
-  // make sure to setup close listeners before triggering any actions
-  var allClosed = Promise.all([
-    setupClosePromise(localChannel),
-    setupClosePromise(remoteChannel)
-  ]);
-  var complete = timerGuard(allClosed, 60000, "failed to close data channel pair");
+  function _allChannelsAreClosed() {
+    var ret = null;
+    if (localChannel) {
+      ret = (localChannel.readyState === "closed");
+    }
+    if (remoteChannel) {
+      if (ret !== null) {
+        ret = (ret && (remoteChannel.readyState === "closed"));
+      }
+      else {
+        ret = (remoteChannel.readyState === "closed");
+      }
+    }
+    return ret;
+  }
 
-  // triggering close on one side should suffice
-  if (remoteChannel) {
-    remoteChannel.close();
-  } else if (localChannel) {
-    localChannel.close();
+  function verifyClosedChannels() {
+    if (everythingClosed) {
+      // safety protection against events firing late
+      return;
+    }
+    if (_allChannelsAreClosed()) {
+      ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index);
+      if (remoteCloseTimer !== null) {
+        clearTimeout(remoteCloseTimer);
+      }
+      if (verifyInterval !== null) {
+        clearInterval(verifyInterval);
+      }
+      everythingClosed = true;
+      onSuccess(index);
+    }
+    else {
+      info("Still waiting for DataChannel closure");
+    }
   }
 
-  return complete;
+  if ((localChannel) && (localChannel.readyState !== "closed")) {
+    // in case of steeplechase there is no far end, so we can only poll
+    if (remoteChannel) {
+      remoteChannel.onclose = function () {
+        is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'");
+        verifyClosedChannels();
+      };
+    }
+    else {
+      pollingMode = true;
+      verifyInterval = setInterval(verifyClosedChannels, 1000);
+    }
+
+    localChannel.close();
+    wait = true;
+  }
+  if ((remoteChannel) && (remoteChannel.readyState !== "closed")) {
+    if (localChannel) {
+      localChannel.onclose = function () {
+        is(localChannel.readyState, "closed", "localChannel is in state 'closed'");
+        verifyClosedChannels();
+      };
+
+      // Apparently we are running a local test which has both ends of the
+      // data channel locally available, so by default lets wait for the
+      // remoteChannel.onclose handler from above to confirm closure on both
+      // ends.
+      remoteCloseTimer = setTimeout(function() {
+        todo(false, "localChannel.close() did not resulted in close signal on remote side");
+        remoteChannel.close();
+        verifyClosedChannels();
+      }, 30000);
+    }
+    else {
+      pollingMode = true;
+      verifyTimer = setInterval(verifyClosedChannels, 1000);
+
+      remoteChannel.close();
+    }
+
+    wait = true;
+  }
+
+  if (!wait) {
+    onSuccess(index);
+  }
+};
+
+
+/**
+ * Wait for the initial data channel to get into the open state
+ *
+ * @param {PeerConnectionWrapper} peer
+ *        The peer connection wrapper to run the command on
+ * @param {Function} onSuccess
+ *        Callback when the creation was successful
+ */
+PeerConnectionTest.prototype.waitForInitialDataChannel =
+        function PCT_waitForInitialDataChannel(peer, onSuccess, onFailure) {
+  var dcConnectionTimeout = null;
+  var dcOpened = false;
+
+  function dataChannelConnected(channel) {
+    // in case the switch statement below had called onSuccess already we
+    // don't want to call it again
+    if (!dcOpened) {
+      clearTimeout(dcConnectionTimeout);
+      is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'");
+      dcOpened = true;
+      onSuccess();
+    } else {
+      info("dataChannelConnected() called, but data channel was open already");
+    }
+  }
+
+  // TODO: drno: convert dataChannels into an object and make
+  //             registerDataChannelOpenEvent a generic function
+  if (peer == this.pcLocal) {
+    peer.dataChannels[0].onopen = dataChannelConnected;
+  } else {
+    peer.registerDataChannelOpenEvents(dataChannelConnected);
+  }
+
+  if (peer.dataChannels.length >= 1) {
+    // snapshot of the live value as it might change during test execution
+    const readyState = peer.dataChannels[0].readyState;
+    switch (readyState) {
+      case "open": {
+        is(readyState, "open", peer + " dataChannels[0] is already in state: 'open'");
+        dcOpened = true;
+        onSuccess();
+        break;
+      }
+      case "connecting": {
+        is(readyState, "connecting", peer + " dataChannels[0] is in state: 'connecting'");
+        if (onFailure) {
+          dcConnectionTimeout = setTimeout(function () {
+            is(peer.dataChannels[0].readyState, "open", peer + " timed out while waiting for dataChannels[0] to open");
+            onFailure();
+          }, 60000);
+        }
+        break;
+      }
+      default: {
+        ok(false, "dataChannels[0] is in unexpected state " + readyState);
+        if (onFailure) {
+          onFailure()
+        }
+      }
+    }
+  }
 };
 
 /**
  * Send data (message or blob) to the other peer
  *
  * @param {String|Blob} data
  *        Data to send to the other peer. For Blobs the MIME type will be lost.
+ * @param {Function} onSuccess
+ *        Callback to execute when data has been sent
  * @param {Object} [options={ }]
  *        Options to specify the data channels to be used
  * @param {DataChannelWrapper} [options.sourceChannel=pcLocal.dataChannels[length - 1]]
  *        Data channel to use for sending the message
  * @param {DataChannelWrapper} [options.targetChannel=pcRemote.dataChannels[length - 1]]
  *        Data channel to use for receiving the message
  */
-PeerConnectionTest.prototype.send = function(data, options) {
+PeerConnectionTest.prototype.send = function PCT_send(data, onSuccess, options) {
   options = options || { };
   var source = options.sourceChannel ||
            this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1];
   var target = options.targetChannel ||
            this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1];
 
-  return new Promise(resolve => {
-    // Register event handler for the target channel
-    target.onmessage = e => {
-      resolve({ channel: target, data: e.data });
-    };
+  // Register event handler for the target channel
+  target.onmessage = function (recv_data) {
+    onSuccess(target, recv_data);
+  };
 
-    source.send(data);
-  });
+  source.send(data);
 };
 
 /**
  * Create a data channel
  *
  * @param {Dict} options
  *        Options for the data channel (see nsIPeerConnection)
+ * @param {Function} onSuccess
+ *        Callback when the creation was successful
  */
-PeerConnectionTest.prototype.createDataChannel = function(options) {
-  var remotePromise;
-  if (!options.negotiated) {
-    this.pcRemote.expectDataChannel();
-    remotePromise = this.pcRemote.nextDataChannel;
+PeerConnectionTest.prototype.createDataChannel = function DCT_createDataChannel(options, onSuccess) {
+  var localChannel = null;
+  var remoteChannel = null;
+  var self = this;
+
+  // Method to synchronize all asynchronous events.
+  function check_next_test() {
+    if (localChannel && remoteChannel) {
+      onSuccess(localChannel, remoteChannel);
+    }
   }
 
-  // Create the datachannel
-  var localChannel = this.pcLocal.createDataChannel(options)
-  var localPromise = localChannel.opened;
-
-  if (options.negotiated) {
-    remotePromise = localPromise.then(localChannel => {
-      // externally negotiated - we need to open from both ends
-      options.id = options.id || channel.id;  // allow for no id on options
-      var remoteChannel = this.pcRemote.createDataChannel(options);
-      return remoteChannel.opened;
+  if (!options.negotiated) {
+    // Register handlers for the remote peer
+    this.pcRemote.registerDataChannelOpenEvents(function (channel) {
+      remoteChannel = channel;
+      check_next_test();
     });
   }
 
-  return Promise.all([localPromise, remotePromise]).then(result => {
-    return { local: result[0], remote: result[1] };
+  // Create the datachannel and handle the local 'onopen' event
+  this.pcLocal.createDataChannel(options, function (channel) {
+    localChannel = channel;
+
+    if (options.negotiated) {
+      // externally negotiated - we need to open from both ends
+      options.id = options.id || channel.id;  // allow for no id to let the impl choose
+      self.pcRemote.createDataChannel(options, function (channel) {
+        remoteChannel = channel;
+        check_next_test();
+      });
+    } else {
+      check_next_test();
+    }
   });
 };
 
 /**
+ * Executes the next command.
+ */
+PeerConnectionTest.prototype.next = function PCT_next() {
+  if (this._stepTimeout) {
+    clearTimeout(this._stepTimeout);
+    this._stepTimeout = null;
+  }
+  this.chain.executeNext();
+};
+
+/**
+ * Set a timeout for the current step.
+ * @param {long] ms the number of milliseconds to allow for this step
+ */
+PeerConnectionTest.prototype.setStepTimeout = function(ms) {
+  this._stepTimeout = setTimeout(function() {
+    ok(false, "Step timed out: " + this.chain.currentStepLabel);
+    this.next();
+  }.bind(this), ms);
+};
+
+/**
+ * Set a timeout for the over all PeerConnectionTest
+ * @param {long] ms the number of milliseconds to allow for the test
+ */
+PeerConnectionTest.prototype.setTimeout = function(ms) {
+  this._timeout = setTimeout(function() {
+    ok(false, "PeerConnectionTest timed out");
+    this.teardown();
+  }.bind(this), ms);
+};
+
+/**
  * Creates an answer for the specified peer connection instance
  * and automatically handles the failure case.
  *
  * @param {PeerConnectionWrapper} peer
  *        The peer connection wrapper to run the command on
+ * @param {function} onSuccess
+ *        Callback to execute if the offer was created successfully
  */
-PeerConnectionTest.prototype.createAnswer = function(peer) {
-  return peer.createAnswer().then(answer => {
+PeerConnectionTest.prototype.createAnswer =
+function PCT_createAnswer(peer, onSuccess) {
+  var self = this;
+
+  peer.createAnswer(function (answer) {
     // make a copy so this does not get updated with ICE candidates
-    this.originalAnswer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(answer)));
-    return answer;
+    self.originalAnswer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(answer)));
+    onSuccess(answer);
   });
 };
 
 /**
  * Creates an offer for the specified peer connection instance
  * and automatically handles the failure case.
  *
  * @param {PeerConnectionWrapper} peer
  *        The peer connection wrapper to run the command on
+ * @param {function} onSuccess
+ *        Callback to execute if the offer was created successfully
  */
-PeerConnectionTest.prototype.createOffer = function(peer) {
-  return peer.createOffer().then(offer => {
+PeerConnectionTest.prototype.createOffer =
+function PCT_createOffer(peer, onSuccess) {
+  var self = this;
+
+  peer.createOffer(function (offer) {
     // make a copy so this does not get updated with ICE candidates
-    this.originalOffer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(offer)));
-    return offer;
+    self.originalOffer = new mozRTCSessionDescription(JSON.parse(JSON.stringify(offer)));
+    onSuccess(offer);
   });
 };
 
 PeerConnectionTest.prototype.setIdentityProvider =
 function(peer, provider, protocol, identity) {
   peer.setIdentityProvider(provider, protocol, identity);
 };
 
 /**
  * Sets the local description for the specified peer connection instance
  * and automatically handles the failure case.
  *
  * @param {PeerConnectionWrapper} peer
           The peer connection wrapper to run the command on
  * @param {mozRTCSessionDescription} desc
  *        Session description for the local description request
+ * @param {function} onSuccess
+ *        Callback to execute if the local description was set successfully
  */
 PeerConnectionTest.prototype.setLocalDescription =
-function(peer, desc, stateExpected) {
-  var eventFired = new Promise(resolve => {
-    peer.onsignalingstatechange = e => {
-      info(peer + ": 'signalingstatechange' event received");
-      var state = e.target.signalingState;
-      if (stateExpected === state) {
-        peer.setLocalDescStableEventDate = new Date();
-        resolve();
-      } else {
-        ok(false, "This event has either already fired or there has been a " +
-           "mismatch between event received " + state +
-           " and event expected " + stateExpected);
-      }
-    };
+function PCT_setLocalDescription(peer, desc, stateExpected, onSuccess) {
+  var eventFired = false;
+  var stateChanged = false;
+
+  function check_next_test() {
+    if (eventFired && stateChanged) {
+      onSuccess();
+    }
+  }
+
+  peer.onsignalingstatechange = function (e) {
+    info(peer + ": 'signalingstatechange' event received");
+    var state = e.target.signalingState;
+    if(stateExpected === state && !eventFired) {
+      eventFired = true;
+      peer.setLocalDescStableEventDate = new Date();
+      check_next_test();
+    } else {
+      ok(false, "This event has either already fired or there has been a " +
+                "mismatch between event received " + state +
+                " and event expected " + stateExpected);
+    }
+  };
+
+  peer.setLocalDescription(desc, function () {
+    stateChanged = true;
+    peer.setLocalDescDate = new Date();
+    check_next_test();
   });
-
-  var stateChanged = peer.setLocalDescription(desc).then(() => {
-    peer.setLocalDescDate = new Date();
-  });
-
-  return Promise.all([eventFired, stateChanged]);
 };
 
 /**
  * Sets the media constraints for both peer connection instances.
  *
  * @param {object} constraintsLocal
  *        Media constrains for the local peer connection instance
  * @param constraintsRemote
  */
 PeerConnectionTest.prototype.setMediaConstraints =
-function(constraintsLocal, constraintsRemote) {
-  if (this.pcLocal) {
+function PCT_setMediaConstraints(constraintsLocal, constraintsRemote) {
+  if (this.pcLocal)
     this.pcLocal.constraints = constraintsLocal;
-  }
-  if (this.pcRemote) {
+  if (this.pcRemote)
     this.pcRemote.constraints = constraintsRemote;
-  }
 };
 
 /**
  * Sets the media options used on a createOffer call in the test.
  *
  * @param {object} options the media constraints to use on createOffer
  */
-PeerConnectionTest.prototype.setOfferOptions = function(options) {
-  if (this.pcLocal) {
+PeerConnectionTest.prototype.setOfferOptions =
+function PCT_setOfferOptions(options) {
+  if (this.pcLocal)
     this.pcLocal.offerOptions = options;
-  }
 };
 
 /**
  * Sets the remote description for the specified peer connection instance
  * and automatically handles the failure case.
  *
  * @param {PeerConnectionWrapper} peer
           The peer connection wrapper to run the command on
  * @param {mozRTCSessionDescription} desc
  *        Session description for the remote description request
+ * @param {function} onSuccess
+ *        Callback to execute if the local description was set successfully
  */
 PeerConnectionTest.prototype.setRemoteDescription =
-function(peer, desc, stateExpected) {
-  var eventFired = new Promise(resolve => {
-    peer.onsignalingstatechange = e => {
-      info(peer + ": 'signalingstatechange' event received");
-      var state = e.target.signalingState;
-      if (stateExpected === state) {
-        peer.setRemoteDescStableEventDate = new Date();
-        resolve();
-      } else {
-        ok(false, "This event has either already fired or there has been a " +
-           "mismatch between event received " + state +
-           " and event expected " + stateExpected);
-      }
-    };
+function PCT_setRemoteDescription(peer, desc, stateExpected, onSuccess) {
+  var eventFired = false;
+  var stateChanged = false;
+
+  function check_next_test() {
+    if (eventFired && stateChanged) {
+      onSuccess();
+    }
+  }
+
+  peer.onsignalingstatechange = function(e) {
+    info(peer + ": 'signalingstatechange' event received");
+    var state = e.target.signalingState;
+    if(stateExpected === state && !eventFired) {
+      eventFired = true;
+      peer.setRemoteDescStableEventDate = new Date();
+      check_next_test();
+    } else {
+      ok(false, "This event has either already fired or there has been a " +
+                "mismatch between event received " + state +
+                " and event expected " + stateExpected);
+    }
+  };
+
+  peer.setRemoteDescription(desc, function () {
+    stateChanged = true;
+    peer.setRemoteDescDate = new Date();
+    check_next_test();
   });
-
-  var stateChanged = peer.setRemoteDescription(desc).then(() => {
-    peer.setRemoteDescDate = new Date();
-  });
-
-  return Promise.all([eventFired, stateChanged]);
 };
 
 /**
  * Start running the tests as assigned to the command chain.
  */
-PeerConnectionTest.prototype.run = function() {
-  return this.chain.execute()
-    .then(() => this.close())
-    .then(() => {
-      if (window.SimpleTest) {
-        networkTestFinished();
-      } else {
-        finish();
-      }
-    })
-    .catch(e =>
-           ok(false, 'Error in test execution: ' + e +
-              ((typeof e.stack === 'string') ?
-               (' ' + e.stack.split('\n').join(' ... ')) : '')));
+PeerConnectionTest.prototype.run = function PCT_run() {
+  this.next();
+};
+
+/**
+ * Clean up the objects used by the test
+ */
+PeerConnectionTest.prototype.teardown = function PCT_teardown() {
+  this.close(function () {
+    info("Test finished");
+    if (window.SimpleTest)
+      networkTestFinished();
+    else
+      finish();
+  });
 };
 
 /**
  * Routes ice candidates from one PCW to the other PCW
  */
-PeerConnectionTest.prototype.iceCandidateHandler = function(caller, candidate) {
+PeerConnectionTest.prototype.iceCandidateHandler = function
+PCT_iceCandidateHandler(caller, candidate) {
+  var self = this;
+
   info("Received: " + JSON.stringify(candidate) + " from " + caller);
 
   var target = null;
   if (caller.contains("pcLocal")) {
-    if (this.pcRemote) {
-      target = this.pcRemote;
+    if (self.pcRemote) {
+      target = self.pcRemote;
     }
   } else if (caller.contains("pcRemote")) {
-    if (this.pcLocal) {
-      target = this.pcLocal;
+    if (self.pcLocal) {
+      target = self.pcLocal;
     }
   } else {
     ok(false, "received event from unknown caller: " + caller);
     return;
   }
 
   if (target) {
     target.storeOrAddIceCandidate(candidate);
@@ -519,83 +1205,110 @@ PeerConnectionTest.prototype.iceCandidat
     send_message({"type": "ice_candidate", "ice_candidate": candidate});
   }
 };
 
 /**
  * Installs a polling function for the socket.io client to read
  * all messages from the chat room into a message queue.
  */
-PeerConnectionTest.prototype.setupSignalingClient = function() {
-  this.signalingMessageQueue = [];
-  this.signalingCallbacks = {};
-  this.signalingLoopRun = true;
+PeerConnectionTest.prototype.setupSignalingClient = function
+PCT_setupSignalingClient() {
+  var self = this;
 
-  var queueMessage = message => {
+  self.signalingMessageQueue = [];
+  self.signalingCallbacks = {};
+  self.signalingLoopRun = true;
+
+  function queueMessage(message) {
     info("Received signaling message: " + JSON.stringify(message));
     var fired = false;
-    Object.keys(this.signalingCallbacks).forEach(name => {
+    Object.keys(self.signalingCallbacks).forEach(function(name) {
       if (name === message.type) {
         info("Invoking callback for message type: " + name);
-        this.signalingCallbacks[name](message);
+        self.signalingCallbacks[name](message);
         fired = true;
       }
     });
     if (!fired) {
-      this.signalingMessageQueue.push(message);
-      info("signalingMessageQueue.length: " + this.signalingMessageQueue.length);
+      self.signalingMessageQueue.push(message);
+      info("signalingMessageQueue.length: " + self.signalingMessageQueue.length);
     }
-    if (this.signalingLoopRun) {
+    if (self.signalingLoopRun) {
       wait_for_message().then(queueMessage);
     } else {
       info("Exiting signaling message event loop");
     }
-  };
+  }
+
   wait_for_message().then(queueMessage);
 }
 
 /**
  * Sets a flag to stop reading further messages from the chat room.
  */
-PeerConnectionTest.prototype.signalingMessagesFinished = function() {
+PeerConnectionTest.prototype.signalingMessagesFinished = function
+PCT_signalingMessagesFinished() {
   this.signalingLoopRun = false;
 }
 
 /**
+ * Callback to stop reading message from chat room once trickle ICE
+ * on the far end is over.
+ *
+ * @param {string} caller
+ *        The lable of the caller of the function
+ */
+PeerConnectionTest.prototype.signalEndOfTrickleIce = function
+PCT_signalEndOfTrickleIce(caller) {
+  if (this.steeplechase) {
+    send_message({"type": "end_of_trickle_ice"});
+  }
+};
+
+/**
  * Register a callback function to deliver messages from the chat room
  * directly instead of storing them in the message queue.
  *
  * @param {string} messageType
  *        For which message types should the callback get invoked.
  *
  * @param {function} onMessage
  *        The function which gets invoked if a message of the messageType
  *        has been received from the chat room.
  */
-PeerConnectionTest.prototype.registerSignalingCallback = function(messageType, onMessage) {
+PeerConnectionTest.prototype.registerSignalingCallback = function
+PCT_registerSignalingCallback(messageType, onMessage) {
   this.signalingCallbacks[messageType] = onMessage;
-};
+}
 
 /**
  * Searches the message queue for the first message of a given type
  * and invokes the given callback function, or registers the callback
  * function for future messages if the queue contains no such message.
  *
  * @param {string} messageType
  *        The type of message to search and register for.
+ *
+ * @param {function} onMessage
+ *        The callback function which gets invoked with the messages
+ *        of the given mesage type.
  */
-PeerConnectionTest.prototype.getSignalingMessage = function(messageType) {
-    var i = this.signalingMessageQueue.findIndex(m => m.type === messageType);
-  if (i >= 0) {
-    info("invoking callback on message " + i + " from message queue, for message type:" + messageType);
-    return Promise.resolve(this.signalingMessageQueue.splice(i, 1)[0]);
+PeerConnectionTest.prototype.getSignalingMessage = function
+PCT_getSignalingMessage(messageType, onMessage) {
+  for(var i=0; i < this.signalingMessageQueue.length; i++) {
+    if (messageType === this.signalingMessageQueue[i].type) {
+      //FIXME
+      info("invoking callback on message " + i + " from message queue, for message type:" + messageType);
+      onMessage(this.signalingMessageQueue.splice(i, 1)[0]);
+      return;
+    }
   }
-  return new Promise(resolve =>
-                     this.registerSignalingCallback(messageType, resolve));
-};
+  this.registerSignalingCallback(messageType, onMessage);
+}
 
 
 /**
  * This class acts as a wrapper around a DataChannel instance.
  *
  * @param dataChannel
  * @param peerConnectionWrapper
  * @constructor
@@ -604,27 +1317,62 @@ function DataChannelWrapper(dataChannel,
   this._channel = dataChannel;
   this._pc = peerConnectionWrapper;
 
   info("Creating " + this);
 
   /**
    * Setup appropriate callbacks
    */
-  createOneShotEventWrapper(this, this._channel, 'close');
-  createOneShotEventWrapper(this, this._channel, 'error');
-  createOneShotEventWrapper(this, this._channel, 'message');
+
+  this.onclose = unexpectedEventAndFinish(this, 'onclose');
+  this.onerror = unexpectedEventAndFinish(this, 'onerror');
+  this.onmessage = unexpectedEventAndFinish(this, 'onmessage');
+  this.onopen = unexpectedEventAndFinish(this, 'onopen');
+
+  var self = this;
+
+  /**
+   * Callback for native data channel 'onclose' events. If no custom handler
+   * has been specified via 'this.onclose', a failure will be raised if an
+   * event of this type gets caught.
+   */
+  this._channel.onclose = function () {
+    info(self + ": 'onclose' event fired");
+
+    self.onclose(self);
+    self.onclose = unexpectedEventAndFinish(self, 'onclose');
+  };
 
-  this.opened = timerGuard(new Promise(resolve => {
-    this._channel.onopen = () => {
-      this._channel.onopen = unexpectedEvent(this, 'onopen');
-      is(this.readyState, "open", "data channel is 'open' after 'onopen'");
-      resolve(this);
-    };
-  }), 60000, "channel didn't open in time");
+  /**
+   * Callback for native data channel 'onmessage' events. If no custom handler
+   * has been specified via 'this.onmessage', a failure will be raised if an
+   * event of this type gets caught.
+   *
+   * @param {Object} event
+   *        Event data which includes the sent message
+   */
+  this._channel.onmessage = function (event) {
+    info(self + ": 'onmessage' event fired for '" + event.data + "'");
+
+    self.onmessage(event.data);
+    self.onmessage = unexpectedEventAndFinish(self, 'onmessage');
+  };
+
+  /**
+   * Callback for native data channel 'onopen' events. If no custom handler
+   * has been specified via 'this.onopen', a failure will be raised if an
+   * event of this type gets caught.
+   */
+  this._channel.onopen = function () {
+    info(self + ": 'onopen' event fired");
+
+    self.onopen(self);
+    self.onopen = unexpectedEventAndFinish(self, 'onopen');
+  };
 }
 
 DataChannelWrapper.prototype = {
   /**
    * Returns the binary type of the channel
    *
    * @returns {String} The binary type
    */
@@ -698,27 +1446,27 @@ DataChannelWrapper.prototype = {
   },
 
   /**
    * Send data through the data channel
    *
    * @param {String|Object} data
    *        Data which has to be sent through the data channel
    */
-  send: function(data) {
+  send: function DCW_send(data) {
     info(this + ": Sending data '" + data + "'");
     this._channel.send(data);
   },
 
   /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
    */
-  toString: function() {
+  toString: function DCW_toString() {
     return "DataChannelWrapper (" + this._pc.label + '_' + this._channel.label + ")";
   }
 };
 
 
 /**
  * This class acts as a wrapper around a PeerConnection instance.
  *
@@ -735,73 +1483,119 @@ function PeerConnectionWrapper(label, co
 
   this.constraints = [ ];
   this.offerOptions = {};
   this.streams = [ ];
   this.mediaCheckers = [ ];
 
   this.dataChannels = [ ];
 
-  this.addStreamCounter = {audio: 0, video: 0 };
+  this.onAddStreamAudioCounter = 0;
+  this.onAddStreamVideoCounter = 0;
+  this.addStreamCallbacks = {};
 
   this._local_ice_candidates = [];
   this._remote_ice_candidates = [];
-  this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
+  this._ice_candidates_to_add = [];
+  this.holdIceCandidates = true;
+  this.endOfTrickleIce = false;
   this.localRequiresTrickleIce = false;
-  this.remoteRequiresTrickleIce = false;
-  this.localMediaElements = [];
+  this.remoteRequiresTrickleIce  = false;
 
   this.h264 = typeof h264 !== "undefined" ? true : false;
 
   info("Creating " + this);
   this._pc = new mozRTCPeerConnection(this.configuration);
 
   /**
    * Setup callback handlers
    */
+  var self = this;
+  // This enables tests to validate that the next ice state is the one they expect to happen
+  this.next_ice_state = ""; // in most cases, the next state will be "checking", but in some tests "closed"
   // This allows test to register their own callbacks for ICE connection state changes
   this.ice_connection_callbacks = {};
 
-  this._pc.oniceconnectionstatechange = e => {
-    isnot(typeof this._pc.iceConnectionState, "undefined",
-          "iceConnectionState should not be undefined");
-    info(this + ": oniceconnectionstatechange fired, new state is: " + this._pc.iceConnectionState);
-    Object.keys(this.ice_connection_callbacks).forEach(name => {
-      this.ice_connection_callbacks[name]();
+  this._pc.oniceconnectionstatechange = function() {
+    ok(self._pc.iceConnectionState !== undefined, "iceConnectionState should not be undefined");
+    info(self + ": oniceconnectionstatechange fired, new state is: " + self._pc.iceConnectionState);
+    Object.keys(self.ice_connection_callbacks).forEach(function(name) {
+      self.ice_connection_callbacks[name]();
     });
+    if (self.next_ice_state !== "") {
+      is(self._pc.iceConnectionState, self.next_ice_state, "iceConnectionState changed to '" +
+         self.next_ice_state + "'");
+      self.next_ice_state = "";
+    }
   };
 
   /**
    * Callback for native peer connection 'onaddstream' events.
    *
    * @param {Object} event
    *        Event data which includes the stream to be added
    */
-  this._pc.onaddstream = event => {
-    info(this + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
+  this._pc.onaddstream = function (event) {
+    info(self + ": 'onaddstream' event fired for " + JSON.stringify(event.stream));
 
     var type = '';
     if (event.stream.getAudioTracks().length > 0) {
       type = 'audio';
-      this.addStreamCounter.audio += this.countTracksInStreams('audio', [event.stream]);
+      self.onAddStreamAudioCounter += event.stream.getAudioTracks().length;
     }
     if (event.stream.getVideoTracks().length > 0) {
       type += 'video';
-      this.addStreamCounter.video += this.countTracksInStreams('video', [event.stream]);
+      self.onAddStreamVideoCounter += event.stream.getVideoTracks().length;
     }
-    this.attachMedia(event.stream, type, 'remote');
+    self.attachMedia(event.stream, type, 'remote');
+
+    Object.keys(self.addStreamCallbacks).forEach(function(name) {
+      info(self + " calling addStreamCallback " + name);
+      self.addStreamCallbacks[name]();
+    });
+   };
+
+  this.ondatachannel = unexpectedEventAndFinish(this, 'ondatachannel');
+
+  /**
+   * Callback for native peer connection 'ondatachannel' events. If no custom handler
+   * has been specified via 'this.ondatachannel', a failure will be raised if an
+   * event of this type gets caught.
+   *
+   * @param {Object} event
+   *        Event data which includes the newly created data channel
+   */
+  this._pc.ondatachannel = function (event) {
+    info(self + ": 'ondatachannel' event fired for " + event.channel.label);
+
+    self.ondatachannel(new DataChannelWrapper(event.channel, self));
+    self.ondatachannel = unexpectedEventAndFinish(self, 'ondatachannel');
   };
 
-  createOneShotEventWrapper(this, this._pc, 'datachannel');
-  this._pc.addEventListener('datachannel', e => {
-    var wrapper = new DataChannelWrapper(e.channel, this);
-    this.dataChannels.push(wrapper);
-  });
+  this.onsignalingstatechange = unexpectedEventAndFinish(this, 'onsignalingstatechange');
+  this.signalingStateCallbacks = {};
 
-  createOneShotEventWrapper(this, this._pc, 'signalingstatechange');
+  /**
+   * Callback for native peer connection 'onsignalingstatechange' events. If no
+   * custom handler has been specified via 'this.onsignalingstatechange', a
+   * failure will be raised if an event of this type is caught.
+   *
+   * @param {Object} aEvent
+   */
+  this._pc.onsignalingstatechange = function (anEvent) {
+    info(self + ": 'onsignalingstatechange' event fired");
+
+    Object.keys(self.signalingStateCallbacks).forEach(function(name) {
+      self.signalingStateCallbacks[name](anEvent);
+    });
+    // this calls the eventhandler only once and then overwrites it with the
+    // default unexpectedEvent handler
+    self.onsignalingstatechange(anEvent);
+    self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange');
+  };
 }
 
 PeerConnectionWrapper.prototype = {
 
   /**
    * Returns the local description.
    *
    * @returns {object} The local description
@@ -866,380 +1660,489 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {MediaStream} stream
    *        Media stream to handle
    * @param {string} type
    *        The type of media stream ('audio' or 'video')
    * @param {string} side
    *        The location the stream is coming from ('local' or 'remote')
    */
-  attachMedia : function(stream, type, side) {
+  attachMedia : function PCW_attachMedia(stream, type, side) {
+    function isSenderOfTrack(sender) {
+      return sender.track == this;
+    }
+
     info("Got media stream: " + type + " (" + side + ")");
     this.streams.push(stream);
 
     if (side === 'local') {
       // In order to test both the addStream and addTrack APIs, we do video one
       // way and audio + audiovideo the other.
       if (type == "video") {
         this._pc.addStream(stream);
-        ok(this._pc.getSenders().find(sender => sender.track == stream.getVideoTracks()[0]),
+        ok(this._pc.getSenders().find(isSenderOfTrack,
+                                      stream.getVideoTracks()[0]),
            "addStream adds sender");
       } else {
-        stream.getTracks().forEach(track => {
+        stream.getTracks().forEach(function(track) {
           var sender = this._pc.addTrack(track, stream);
           is(sender.track, track, "addTrack returns sender");
-        });
+        }.bind(this));
       }
     }
 
-    var element = createMediaElement(type, this.label + '_' + side + this.streams.length);
+    var element = createMediaElement(type, this.label + '_' + side);
     this.mediaCheckers.push(new MediaElementChecker(element));
     element.mozSrcObject = stream;
     element.play();
-
-    // Store local media elements so that we can stop them when done.
-    // Don't store remote ones because they should stop when the PC does.
-    if (side === 'local') {
-      this.localMediaElements.push(element);
-    }
   },
 
   /**
    * Requests all the media streams as specified in the constrains property.
    *
+   * @param {function} onSuccess
+   *        Callback to execute if all media has been requested successfully
    * @param {array} constraintsList
    *        Array of constraints for GUM calls
    */
-  getAllUserMedia : function(constraintsList) {
-    if (constraintsList.length === 0) {
-      info("Skipping GUM: no UserMedia requested");
-      return Promise.resolve();
+  getAllUserMedia : function PCW_GetAllUserMedia(constraintsList, onSuccess) {
+    var self = this;
+
+    function _getAllUserMedia(index) {
+      if (index < constraintsList.length) {
+        var constraints = constraintsList[index];
+
+        getUserMedia(constraints, function (stream) {
+          var type = '';
+
+          if (constraints.audio) {
+            type = 'audio';
+          }
+
+          if (constraints.video) {
+            type += 'video';
+          }
+
+          self.attachMedia(stream, type, 'local');
+
+          _getAllUserMedia(index + 1);
+        }, generateErrorCallback());
+      } else {
+        onSuccess();
+      }
     }
 
-    info("Get " + constraintsList.length + " local streams");
-    return Promise.all(constraintsList.map(constraints => {
-      return getUserMedia(constraints).then(stream => {
-        var type = '';
-        if (constraints.audio) {
-          type = 'audio';
-        }
-        if (constraints.video) {
-          type += 'video';
-        }
-        this.attachMedia(stream, type, 'local');
-      });
-    }));
-  },
-
-  /**
-   * Create a new data channel instance.  Also creates a promise called
-   * `this.nextDataChannel` that resolves when the next data channel arrives.
-   */
-  expectDataChannel: function(message) {
-    this.nextDataChannel = new Promise(resolve => {
-      this.ondatachannel = e => {
-        ok(e.channel, message);
-        resolve(e.channel);
-      };
-    });
+    if (constraintsList.length === 0) {
+      info("Skipping GUM: no UserMedia requested");
+      onSuccess();
+    }
+    else {
+      info("Get " + constraintsList.length + " local streams");
+      _getAllUserMedia(0);
+    }
   },
 
   /**
    * Create a new data channel instance
    *
    * @param {Object} options
    *        Options which get forwarded to nsIPeerConnection.createDataChannel
+   * @param {function} [onCreation=undefined]
+   *        Callback to execute when the local data channel has been created
    * @returns {DataChannelWrapper} The created data channel
    */
-  createDataChannel : function(options) {
+  createDataChannel : function PCW_createDataChannel(options, onCreation) {
     var label = 'channel_' + this.dataChannels.length;
     info(this + ": Create data channel '" + label);
 
     var channel = this._pc.createDataChannel(label, options);
     var wrapper = new DataChannelWrapper(channel, this);
+
+    if (onCreation) {
+      wrapper.onopen = function () {
+        onCreation(wrapper);
+      };
+    }
+
     this.dataChannels.push(wrapper);
     return wrapper;
   },
 
   /**
    * Creates an offer and automatically handles the failure case.
    *
    * @param {function} onSuccess
    *        Callback to execute if the offer was created successfully
    */
-  createOffer : function() {
-    return this._pc.createOffer(this.offerOptions).then(offer => {
+  createOffer : function PCW_createOffer(onSuccess) {
+    var self = this;
+
+    this._pc.createOffer(function (offer) {
       info("Got offer: " + JSON.stringify(offer));
       // note: this might get updated through ICE gathering
-      this._latest_offer = offer;
-      if (this.h264) {
+      self._latest_offer = offer;
+      if (self.h264) {
         isnot(offer.sdp.search("H264/90000"), -1, "H.264 should be present in the SDP offer");
         offer.sdp = removeVP8(offer.sdp);
       }
-      return offer;
-    });
+      onSuccess(offer);
+    }, generateErrorCallback(), this.offerOptions);
   },
 
   /**
    * Creates an answer and automatically handles the failure case.
+   *
+   * @param {function} onSuccess
+   *        Callback to execute if the answer was created successfully
    */
-  createAnswer : function() {
-    return this._pc.createAnswer().then(answer => {
-      info(this + ": Got answer: " + JSON.stringify(answer));
-      this._last_answer = answer;
-      return answer;
-    });
+  createAnswer : function PCW_createAnswer(onSuccess) {
+    var self = this;
+
+    this._pc.createAnswer(function (answer) {
+      info(self + ": Got answer: " + JSON.stringify(answer));
+      self._last_answer = answer;
+      onSuccess(answer);
+    }, generateErrorCallback());
   },
 
   /**
    * Sets the local description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
+   * @param {function} onSuccess
+   *        Callback to execute if the local description was set successfully
    */
-  setLocalDescription : function(desc) {
-    return this._pc.setLocalDescription(desc).then(() => {
-      info(this + ": Successfully set the local description");
-    });
+  setLocalDescription : function PCW_setLocalDescription(desc, onSuccess) {
+    var self = this;
+
+    if (onSuccess) {
+      this._pc.setLocalDescription(desc, function () {
+        info(self + ": Successfully set the local description");
+        onSuccess();
+      }, generateErrorCallback());
+    } else {
+      this._pc.setLocalDescription(desc);
+    }
   },
 
   /**
    * Tries to set the local description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
-   * @returns {Promise}
-   *        A promise that resolves to the expected error
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
    */
-  setLocalDescriptionAndFail : function(desc) {
-    return this._pc.setLocalDescription(desc).then(
+  setLocalDescriptionAndFail : function PCW_setLocalDescriptionAndFail(desc, onFailure) {
+    var self = this;
+    this._pc.setLocalDescription(desc,
       generateErrorCallback("setLocalDescription should have failed."),
-      err => {
-        info(this + ": As expected, failed to set the local description");
-        return err;
-      });
+      function (err) {
+        info(self + ": As expected, failed to set the local description");
+        onFailure(err);
+    });
   },
 
   /**
    * Sets the remote description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
+   * @param {function} onSuccess
+   *        Callback to execute if the remote description was set successfully
    */
-  setRemoteDescription : function(desc) {
-    return this._pc.setRemoteDescription(desc).then(() => {
-      info(this + ": Successfully set remote description");
-      this.releaseIceCandidates();
-    });
+  setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) {
+    var self = this;
+
+    if (!onSuccess) {
+      this._pc.setRemoteDescription(desc);
+      this.addStoredIceCandidates();
+      return;
+    }
+    this._pc.setRemoteDescription(desc, function () {
+      info(self + ": Successfully set remote description");
+      self.addStoredIceCandidates();
+      onSuccess();
+    }, generateErrorCallback());
   },
 
   /**
    * Tries to set the remote description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
-   * @returns {Promise}
-   *        a promise that resolve to the returned error
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
    */
-  setRemoteDescriptionAndFail : function(desc) {
-    return this._pc.setRemoteDescription(desc).then(
+  setRemoteDescriptionAndFail : function PCW_setRemoteDescriptionAndFail(desc, onFailure) {
+    var self = this;
+    this._pc.setRemoteDescription(desc,
       generateErrorCallback("setRemoteDescription should have failed."),
-      err => {
-        info(this + ": As expected, failed to set the remote description");
-        return err;
+      function (err) {
+        info(self + ": As expected, failed to set the remote description");
+        onFailure(err);
     });
   },
 
   /**
    * Registers a callback for the signaling state change and
    * appends the new state to an array for logging it later.
    */
-  logSignalingState: function() {
-    this.signalingStateLog = [this._pc.signalingState];
-    this._pc.addEventListener('signalingstatechange', e => {
-      var newstate = this._pc.signalingState;
-      var oldstate = this.signalingStateLog[this.signalingStateLog.length - 1]
-      if (Object.keys(signalingStateTransitions).indexOf(oldstate) >= 0) {
-        ok(signalingStateTransitions[oldstate].indexOf(newstate) >= 0, this + ": legal signaling state transition from " + oldstate + " to " + newstate);
+  logSignalingState: function PCW_logSignalingState() {
+    var self = this;
+
+    function _logSignalingState(e) {
+      var newstate = self._pc.signalingState;
+      var oldstate = self.signalingStateLog[self.signalingStateLog.length - 1]
+      if (Object.keys(signalingStateTransitions).indexOf(oldstate) != -1) {
+        ok(signalingStateTransitions[oldstate].indexOf(newstate) != -1, self + ": legal signaling state transition from " + oldstate + " to " + newstate);
       } else {
-        ok(false, this + ": old signaling state " + oldstate + " missing in signaling transition array");
+        ok(false, self + ": old signaling state " + oldstate + " missing in signaling transition array");
       }
-      this.signalingStateLog.push(newstate);
-    });
+      self.signalingStateLog.push(newstate);
+    }
+
+    self.signalingStateLog = [self._pc.signalingState];
+    self.signalingStateCallbacks.logSignalingStatus = _logSignalingState;
   },
 
   /**
    * Either adds a given ICE candidate right away or stores it to be added
    * later, depending on the state of the PeerConnection.
    *
    * @param {object} candidate
    *        The mozRTCIceCandidate to be added or stored
    */
-  storeOrAddIceCandidate : function(candidate) {
-    this._remote_ice_candidates.push(candidate);
-    if (this.signalingState === 'closed') {
+  storeOrAddIceCandidate : function PCW_storeOrAddIceCandidate(candidate) {
+    var self = this;
+
+    self._remote_ice_candidates.push(candidate);
+    if (self.signalingState === 'closed') {
       info("Received ICE candidate for closed PeerConnection - discarding");
       return;
     }
-    this.holdIceCandidates.then(() => {
-      this.addIceCandidate(candidate);
-    });
+    if (!self.holdIceCandidates) {
+      self.addIceCandidate(candidate);
+    } else {
+      self._ice_candidates_to_add.push(candidate);
+    }
+  },
+
+  addStoredIceCandidates : function PCW_addStoredIceCandidates() {
+    var self = this;
+
+    self.holdIceCandidates = false;
+    if ((self._ice_candidates_to_add) &&
+        (self._ice_candidates_to_add.length > 0)) {
+      info("adding stored ice candidates");
+      for (var i = 0; i < self._ice_candidates_to_add.length; i++) {
+        self.addIceCandidate(self._ice_candidates_to_add[i]);
+      }
+      self._ice_candidates_to_add = [];
+    }
   },
 
   /**
    * Adds an ICE candidate and automatically handles the failure case.
    *
    * @param {object} candidate
    *        SDP candidate
+   * @param {function} onSuccess
+   *        Callback to execute if the local description was set successfully
    */
-  addIceCandidate : function(candidate) {
-    info(this + ": adding ICE candidate " + JSON.stringify(candidate));
-    return this._pc.addIceCandidate(candidate).then(() => {
-      info(this + ": Successfully added an ICE candidate");
-    });
+  addIceCandidate : function PCW_addIceCandidate(candidate, onSuccess) {
+    var self = this;
+
+    info(self + ": adding ICE candidate " + JSON.stringify(candidate));
+    this._pc.addIceCandidate(candidate, function () {
+      info(self + ": Successfully added an ICE candidate");
+      if (onSuccess) {
+        onSuccess();
+      }
+    }, generateErrorCallback());
+  },
+
+  /**
+   * Tries to add an ICE candidate and expects failure. Automatically
+   * causes the test case to fail if the call succeeds.
+   *
+   * @param {object} candidate
+   *        SDP candidate
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
+   */
+  addIceCandidateAndFail : function PCW_addIceCandidateAndFail(candidate, onFailure) {
+    var self = this;
+
+    this._pc.addIceCandidate(candidate,
+      generateErrorCallback("addIceCandidate should have failed."),
+      function (err) {
+        info(self + ": As expected, failed to add an ICE candidate");
+        onFailure(err);
+    }) ;
   },
 
   /**
    * Returns if the ICE the connection state is "connected".
    *
    * @returns {boolean} True if the connection state is "connected", otherwise false.
    */
-  isIceConnected : function() {
+  isIceConnected : function PCW_isIceConnected() {
     info(this + ": iceConnectionState = " + this.iceConnectionState);
     return this.iceConnectionState === "connected";
   },
 
   /**
    * Returns if the ICE the connection state is "checking".
    *
    * @returns {boolean} True if the connection state is "checking", otherwise false.
    */
-  isIceChecking : function() {
+  isIceChecking : function PCW_isIceChecking() {
     return this.iceConnectionState === "checking";
   },
 
   /**
    * Returns if the ICE the connection state is "new".
    *
    * @returns {boolean} True if the connection state is "new", otherwise false.
    */
-  isIceNew : function() {
+  isIceNew : function PCW_isIceNew() {
     return this.iceConnectionState === "new";
   },
 
   /**
    * Checks if the ICE connection state still waits for a connection to get
    * established.
    *
    * @returns {boolean} True if the connection state is "checking" or "new",
    *  otherwise false.
    */
-  isIceConnectionPending : function() {
+  isIceConnectionPending : function PCW_isIceConnectionPending() {
     return (this.isIceChecking() || this.isIceNew());
   },
 
   /**
    * Registers a callback for the ICE connection state change and
    * appends the new state to an array for logging it later.
    */
-  logIceConnectionState: function() {
-    this.iceConnectionLog = [this._pc.iceConnectionState];
-    this.ice_connection_callbacks.logIceStatus = () => {
-      var newstate = this._pc.iceConnectionState;
-      var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1]
+  logIceConnectionState: function PCW_logIceConnectionState() {
+    var self = this;
+
+    function logIceConState () {
+      var newstate = self._pc.iceConnectionState;
+      var oldstate = self.iceConnectionLog[self.iceConnectionLog.length - 1]
       if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) {
-        ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate);
+        ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, self + ": legal ICE state transition from " + oldstate + " to " + newstate);
       } else {
-        ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array");
+        ok(false, self + ": old ICE state " + oldstate + " missing in ICE transition array");
       }
-      this.iceConnectionLog.push(newstate);
-    };
+      self.iceConnectionLog.push(newstate);
+    }
+
+    self.iceConnectionLog = [self._pc.iceConnectionState];
+    self.ice_connection_callbacks.logIceStatus = logIceConState;
   },
 
   /**
    * Registers a callback for the ICE connection state change and
    * reports success (=connected) or failure via the callbacks.
    * States "new" and "checking" are ignored.
    *
-   * @returns {Promise}
-   *          resolves when connected, rejects on failure
+   * @param {function} onSuccess
+   *        Callback if ICE connection status is "connected".
+   * @param {function} onFailure
+   *        Callback if ICE connection reaches a different state than
+   *        "new", "checking" or "connected".
    */
-  waitForIceConnected : function() {
-    return new Promise((resolve, reject) => {
-      var iceConnectedChanged = () => {
-        if (this.isIceConnected()) {
-          delete this.ice_connection_callbacks.waitForIceConnected;
-          resolve();
-        } else if (! this.isIceConnectionPending()) {
-          delete this.ice_connection_callbacks.waitForIceConnected;
-          resolve();
-        }
+  waitForIceConnected : function PCW_waitForIceConnected(onSuccess, onFailure) {
+    var self = this;
+    var mySuccess = onSuccess;
+    var myFailure = onFailure;
+
+    function iceConnectedChanged () {
+      if (self.isIceConnected()) {
+        delete self.ice_connection_callbacks.waitForIceConnected;
+        mySuccess();
+      } else if (! self.isIceConnectionPending()) {
+        delete self.ice_connection_callbacks.waitForIceConnected;
+        myFailure();
       }
+    }
 
-      this.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged;
-    });
+    self.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged;
   },
 
   /**
    * Setup a onicecandidate handler
    *
    * @param {object} test
    *        A PeerConnectionTest object to which the ice candidates gets
    *        forwarded.
    */
-  setupIceCandidateHandler : function(test, candidateHandler) {
-    candidateHandler = candidateHandler || test.iceCandidateHandler.bind(test);
-
-    var resolveEndOfTrickle;
-    this.endOfTrickleIce = new Promise(r => resolveEndOfTrickle = r);
+  setupIceCandidateHandler : function
+    PCW_setupIceCandidateHandler(test, candidateHandler, endHandler) {
+    var self = this;
 
-    this.endOfTrickleIce.then(() => {
-      this._pc.onicecandidate = () =>
-        ok(false, this.label + " received ICE candidate after end of trickle");
-    });
+    candidateHandler = candidateHandler || test.iceCandidateHandler.bind(test);
+    endHandler = endHandler || test.signalEndOfTrickleIce.bind(test);
 
-    this._pc.onicecandidate = anEvent => {
+    function iceCandidateCallback (anEvent) {
+      info(self.label + ": received iceCandidateEvent");
       if (!anEvent.candidate) {
-        info(this.label + ": received end of trickle ICE event");
-        resolveEndOfTrickle(this.label);
-        return;
+        info(self.label + ": received end of trickle ICE event");
+        self.endOfTrickleIce = true;
+        endHandler(self.label);
+      } else {
+        if (self.endOfTrickleIce) {
+          ok(false, "received ICE candidate after end of trickle");
+        }
+        info(self.label + ": iceCandidate = " + JSON.stringify(anEvent.candidate));
+        ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate");
+        // we don't support SDP MID's yet
+        ok(anEvent.candidate.sdpMid.length === 0, "SDP MID has length zero");
+        ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist");
+        self._local_ice_candidates.push(anEvent.candidate);
+        candidateHandler(self.label, anEvent.candidate);
       }
+    }
 
-      info(this.label + ": iceCandidate = " + JSON.stringify(anEvent.candidate));
-      ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate");
-      // we don't support SDP MID's yet
-      ok(anEvent.candidate.sdpMid.length === 0, "SDP MID has length zero");
-      ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist");
-      this._local_ice_candidates.push(anEvent.candidate);
-      candidateHandler(this.label, anEvent.candidate);
-    };
+    self._pc.onicecandidate = iceCandidateCallback;
   },
 
   /**
    * Counts the amount of audio tracks in a given media constraint.
    *
    * @param constraints
    *        The contraint to be examined.
    */
-  countTracksInConstraint : function(type, constraints) {
-    if (!Array.isArray(constraints)) {
+  countAudioTracksInMediaConstraint : function
+    PCW_countAudioTracksInMediaConstraint(constraints) {
+    if ((!constraints) || (constraints.length === 0)) {
       return 0;
     }
-    return constraints.reduce((sum, c) => sum + (c[type] ? 1 : 0), 0);
+    var numAudioTracks = 0;
+    for (var i = 0; i < constraints.length; i++) {
+      if (constraints[i].audio) {
+        numAudioTracks++;
+      }
+    }
+    return numAudioTracks;
   },
 
   /**
    * Checks for audio in given offer options.
    *
    * @param options
    *        The options to be examined.
    */
-  audioInOfferOptions : function(options) {
+  audioInOfferOptions : function
+    PCW_audioInOfferOptions(options) {
     if (!options) {
       return 0;
     }
 
     var offerToReceiveAudio = options.offerToReceiveAudio;
 
     // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223).
     if (options.mandatory && options.mandatory.OfferToReceiveAudio !== undefined) {
@@ -1252,22 +2155,43 @@ PeerConnectionWrapper.prototype = {
     if (offerToReceiveAudio) {
       return 1;
     } else {
       return 0;
     }
   },
 
   /**
+   * Counts the amount of video tracks in a given media constraint.
+   *
+   * @param constraint
+   *        The contraint to be examined.
+   */
+  countVideoTracksInMediaConstraint : function
+    PCW_countVideoTracksInMediaConstraint(constraints) {
+    if ((!constraints) || (constraints.length === 0)) {
+      return 0;
+    }
+    var numVideoTracks = 0;
+    for (var i = 0; i < constraints.length; i++) {
+      if (constraints[i].video) {
+        numVideoTracks++;
+      }
+    }
+    return numVideoTracks;
+  },
+
+  /**
    * Checks for video in given offer options.
    *
    * @param options
    *        The options to be examined.
    */
-  videoInOfferOptions : function(options) {
+  videoInOfferOptions : function
+    PCW_videoInOfferOptions(options) {
     if (!options) {
       return 0;
     }
 
     var offerToReceiveVideo = options.offerToReceiveVideo;
 
     // TODO: Remove tests of old constraint-like RTCOptions soon (Bug 1064223).
     if (options.mandatory && options.mandatory.OfferToReceiveVideo !== undefined) {
@@ -1280,140 +2204,168 @@ PeerConnectionWrapper.prototype = {
     if (offerToReceiveVideo) {
       return 1;
     } else {
       return 0;
     }
   },
 
   /*
-   * Counts the amount of tracks of the given type in a set of streams.
+   * Counts the amount of audio tracks in a given set of streams.
    *
-   * @param type audio|video
    * @param streams
    *        An array of streams (as returned by getLocalStreams()) to be
    *        examined.
    */
-  countTracksInStreams: function(type, streams) {
-    if (!Array.isArray(streams)) {
+  countAudioTracksInStreams : function PCW_countAudioTracksInStreams(streams) {
+    if (!streams || (streams.length === 0)) {
       return 0;
     }
-    var f = (type === 'video') ? "getVideoTracks" : "getAudioTracks";
+    var numAudioTracks = 0;
+    streams.forEach(function(st) {
+      numAudioTracks += st.getAudioTracks().length;
+    });
+    return numAudioTracks;
+  },
 
-    return streams.reduce((count, st) => {
-      return count + st[f]().length;
-    }, 0);
+  /*
+   * Counts the amount of video tracks in a given set of streams.
+   *
+   * @param streams
+   *        An array of streams (as returned by getLocalStreams()) to be
+   *        examined.
+   */
+  countVideoTracksInStreams: function PCW_countVideoTracksInStreams(streams) {
+    if (!streams || (streams.length === 0)) {
+      return 0;
+    }
+    var numVideoTracks = 0;
+    streams.forEach(function(st) {
+      numVideoTracks += st.getVideoTracks().length;
+    });
+    return numVideoTracks;
   },
 
   /**
    * Checks that we are getting the media tracks we expect.
    *
-   * @param {object} constraints
-   *        The media constraints of the remote peer connection object
+   * @param {object} constraintsRemote
+   *        The media constraints of the local and remote peer connection object
    */
-  checkMediaTracks : function(remoteConstraints) {
-    var waitForExpectedTracks = type => {
-      var outstandingCount = this.countTracksInConstraint(type, remoteConstraints);
-      outstandingCount -= this.addStreamCounter[type];
-      if (outstandingCount <= 0) {
-        return Promise.resolve();
-      }
+  checkMediaTracks : function PCW_checkMediaTracks(constraintsRemote, onSuccess) {
+    var self = this;
+
+    function _checkMediaTracks(constraintsRemote, onSuccess) {
+
+      var localConstraintAudioTracks =
+        self.countAudioTracksInMediaConstraint(self.constraints);
+      var localStreams = self._pc.getLocalStreams();
+      var localAudioTracks = self.countAudioTracksInStreams(localStreams, false);
+      is(localAudioTracks, localConstraintAudioTracks, self + ' has ' +
+        localAudioTracks + ' local audio tracks');
+
+      var localConstraintVideoTracks =
+        self.countVideoTracksInMediaConstraint(self.constraints);
+      var localVideoTracks = self.countVideoTracksInStreams(localStreams, false);
+      is(localVideoTracks, localConstraintVideoTracks, self + ' has ' +
+        localVideoTracks + ' local video tracks');
+
+      var remoteConstraintAudioTracks =
+        self.countAudioTracksInMediaConstraint(constraintsRemote);
+      var remoteStreams = self._pc.getRemoteStreams();
+      var remoteAudioTracks = self.countAudioTracksInStreams(remoteStreams, false);
+      is(remoteAudioTracks, remoteConstraintAudioTracks, self + ' has ' +
+        remoteAudioTracks + ' remote audio tracks');
+
+      var remoteConstraintVideoTracks =
+        self.countVideoTracksInMediaConstraint(constraintsRemote);
+      var remoteVideoTracks = self.countVideoTracksInStreams(remoteStreams, false);
+      is(remoteVideoTracks, remoteConstraintVideoTracks, self + ' has ' +
+        remoteVideoTracks + ' remote video tracks');
 
-      return new Promise(resolve => {
-        this._pc.addEventListener('addstream', e => {
-          outstandingCount -= this.countTracksInStreams(type, [e.stream]);
-          if (outstandingCount <= 0) {
-            resolve();
-          }
-        });
-      });
-    };
+      onSuccess();
+    }
+
+    // we have to do this check as the onaddstream never fires if the remote
+    // stream has no track at all!
+    var expectedRemoteTracks =
+      self.countAudioTracksInMediaConstraint(constraintsRemote) +
+      self.countVideoTracksInMediaConstraint(constraintsRemote);
+
+    // TODO: this whole counting of streams should be replaced with comparing
+    //       media stream objects IDs and what we got in the SDP (bug 1089798)
+    function _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess) {
+      var receivedRemoteTracks =
+        self.onAddStreamAudioCounter + self.onAddStreamVideoCounter;
 
-    var checkTrackCounts = (side, streams, constraints) => {
-      ['audio', 'video'].forEach(type => {
-        var actual = this.countTracksInStreams(type, streams);
-        var expected = this.countTracksInConstraint(type, constraints);
-        is(actual, expected, this + ' has ' + actual + ' ' +
-           side + ' ' + type + ' tracks');
-      });
-    };
+      if (receivedRemoteTracks === expectedRemoteTracks) {
+        _checkMediaTracks(constraintsRemote, onSuccess);
+      } else if (receivedRemoteTracks > expectedRemoteTracks) {
+        ok(false, "Received more streams " + receivedRemoteTracks +
+            " then expected " + expectedRemoteTracks);
+        _checkMediaTracks(constraintsRemote, onSuccess);
+      } else {
+        info("Still waiting for more remote streams to arrive (" +
+            receivedRemoteTracks + " vs " + expectedRemoteTracks + ")");
+      }
+    }
 
-    info(this + " checkMediaTracks() got called before onAddStream fired");
-    var checkPromise = Promise.all([
-      waitForExpectedTracks('audio'),
-      waitForExpectedTracks('video')
-    ]).then(() => {
-      checkTrackCounts('local', this._pc.getLocalStreams(), this.constraints);
-      checkTrackCounts('remote', this._pc.getRemoteStreams(), remoteConstraints);
-    });
-    return timerGuard(checkPromise, 60000, "onaddstream never fired");
+    if (expectedRemoteTracks > (self.onAddStreamAudioCounter +
+        self.onAddStreamVideoCounter)) {
+      // This installs a callback handler for every time onaddstrem fires.
+      // We rely on the outer mochitest timeout to catch the case where
+      // onaddstream never fires
+      self.addStreamCallbacks.checkMediaTracks = function() {
+        _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess);
+      };
+    }
+    _compareReceivedAndExpectedTracks(constraintsRemote, onSuccess);
+
   },
 
-  checkMsids: function() {
-    var checkSdpForMsids = (desc, streams, side) => {
-      streams.forEach(stream => {
-        stream.getTracks().forEach(track => {
-          // TODO(bug 1089798): Once DOMMediaStream has an id field, we
-          // should be verifying that the SDP contains
-          // a=msid:<stream-id> <track-id>
-          ok(desc.sdp.match(new RegExp("a=msid:[^ ]+ " + track.id)),
-             side + " SDP contains track id " + track.id );
-        });
-      });
-    };
-
-    checkSdpForMsids(this.localDescription, this._pc.getLocalStreams(),
-                     "local");
-    checkSdpForMsids(this.remoteDescription, this._pc.getRemoteStreams(),
-                     "remote");
-   },
-
-  verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions, isLocal) {
+  verifySdp : function PCW_verifySdp(desc, expectedType, offerConstraintsList,
+      offerOptions, trickleIceCallback) {
     info("Examining this SessionDescription: " + JSON.stringify(desc));
     info("offerConstraintsList: " + JSON.stringify(offerConstraintsList));
     info("offerOptions: " + JSON.stringify(offerOptions));
     ok(desc, "SessionDescription is not null");
     is(desc.type, expectedType, "SessionDescription type is " + expectedType);
     ok(desc.sdp.length > 10, "SessionDescription body length is plausible");
     ok(desc.sdp.contains("a=ice-ufrag"), "ICE username is present in SDP");
     ok(desc.sdp.contains("a=ice-pwd"), "ICE password is present in SDP");
     ok(desc.sdp.contains("a=fingerprint"), "ICE fingerprint is present in SDP");
     //TODO: update this for loopback support bug 1027350
     ok(!desc.sdp.contains(LOOPBACK_ADDR), "loopback interface is absent from SDP");
-    var requiresTrickleIce = !desc.sdp.contains("a=candidate");
-    if (requiresTrickleIce) {
-      info("at least one ICE candidate is present in SDP");
+    if (desc.sdp.contains("a=candidate")) {
+      ok(true, "at least one ICE candidate is present in SDP");
+      trickleIceCallback(false);
     } else {
       info("No ICE candidate in SDP -> requiring trickle ICE");
+      trickleIceCallback(true);
     }
-    if (isLocal) {
-      this.localRequiresTrickleIce = requiresTrickleIce;
-    } else {
-      this.remoteRequiresTrickleIce = requiresTrickleIce;
-    }
-
     //TODO: how can we check for absence/presence of m=application?
 
     var audioTracks =
-        this.countTracksInConstraint('audio', offerConstraintsList) ||
+      this.countAudioTracksInMediaConstraint(offerConstraintsList) ||
       this.audioInOfferOptions(offerOptions);
 
     info("expected audio tracks: " + audioTracks);
     if (audioTracks == 0) {
       ok(!desc.sdp.contains("m=audio"), "audio m-line is absent from SDP");
     } else {
       ok(desc.sdp.contains("m=audio"), "audio m-line is present in SDP");
       ok(desc.sdp.contains("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP");
       //TODO: ideally the rtcp-mux should be for the m=audio, and not just
       //      anywhere in the SDP (JS SDP parser bug 1045429)
       ok(desc.sdp.contains("a=rtcp-mux"), "RTCP Mux is offered in SDP");
+
     }
 
     var videoTracks =
-        this.countTracksInConstraint('video', offerConstraintsList) ||
+      this.countVideoTracksInMediaConstraint(offerConstraintsList) ||
       this.videoInOfferOptions(offerOptions);
 
     info("expected video tracks: " + videoTracks);
     if (videoTracks == 0) {
       ok(!desc.sdp.contains("m=video"), "video m-line is absent from SDP");
     } else {
       ok(desc.sdp.contains("m=video"), "video m-line is present in SDP");
       if (this.h264) {
@@ -1424,45 +2376,69 @@ PeerConnectionWrapper.prototype = {
       ok(desc.sdp.contains("a=rtcp-mux"), "RTCP Mux is offered in SDP");
     }
 
   },
 
   /**
    * Check that media flow is present on all media elements involved in this
    * test by waiting for confirmation that media flow is present.
+   *
+   * @param {Function} onSuccess the success callback when media flow
+   *                             is confirmed on all media elements
    */
-  checkMediaFlowPresent : function() {
-    return Promise.all(this.mediaCheckers.map(checker => checker.waitForMediaFlow()));
+  checkMediaFlowPresent : function PCW_checkMediaFlowPresent(onSuccess) {
+    var self = this;
+
+    function _checkMediaFlowPresent(index, onSuccess) {
+      if(index >= self.mediaCheckers.length) {
+        onSuccess();
+      } else {
+        var mediaChecker = self.mediaCheckers[index];
+        mediaChecker.waitForMediaFlow(function() {
+          _checkMediaFlowPresent(index + 1, onSuccess);
+        });
+      }
+    }
+
+    _checkMediaFlowPresent(0, onSuccess);
   },
 
   /**
    * Check that stats are present by checking for known stats.
+   *
+   * @param {Function} onSuccess the success callback to return stats to
    */
-  getStats : function(selector) {
-    return this._pc.getStats(selector).then(stats => {
-      info(this + ": Got stats: " + JSON.stringify(stats));
-      this._last_stats = stats;
-      return stats;
-    });
+  getStats : function PCW_getStats(selector, onSuccess) {
+    var self = this;
+
+    this._pc.getStats(selector, function(stats) {
+      info(self + ": Got stats: " + JSON.stringify(stats));
+      self._last_stats = stats;
+      onSuccess(stats);
+    }, generateErrorCallback());
   },
 
   /**
    * Checks that we are getting the media streams we expect.
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    */
-  checkStats : function(stats, twoMachines) {
-    var toNum = obj => obj? obj : 0;
-    var numTracks = streams =>
-        streams.reduce((count, stream) => count +
-                       stream.getAudioTracks().length +
-                       stream.getVideoTracks().length,
-                       0);
+  checkStats : function PCW_checkStats(stats, twoMachines) {
+    function toNum(obj) {
+      return obj? obj : 0;
+    }
+    function numTracks(streams) {
+      var n = 0;
+      streams.forEach(function(stream) {
+          n += stream.getAudioTracks().length + stream.getVideoTracks().length;
+        });
+      return n;
+    }
 
     const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
 
     // Use spec way of enumerating stats
     var counters = {};
     for (var key in stats) {
       if (stats.hasOwnProperty(key)) {
         var res = stats[key];
@@ -1537,17 +2513,17 @@ PeerConnectionWrapper.prototype = {
             break;
           }
         }
       }
     }
 
     // Use MapClass way of enumerating stats
     var counters2 = {};
-    stats.forEach(res => {
+    stats.forEach(function(res) {
         if (!res.isRemote) {
           counters2[res.type] = toNum(counters2[res.type]) + 1;
         }
       });
     is(JSON.stringify(counters), JSON.stringify(counters2),
        "Spec and MapClass variant of RTCStatsReport enumeration agree");
     var nin = numTracks(this._pc.getRemoteStreams());
     var nout = numTracks(this._pc.getLocalStreams());
@@ -1573,20 +2549,21 @@ PeerConnectionWrapper.prototype = {
 
   /**
    * Compares the Ice server configured for this PeerConnectionWrapper
    * with the ICE candidates received in the RTCP stats.
    *
    * @param {object} stats
    *        The stats to be verified for relayed vs. direct connection.
    */
-  checkStatsIceConnectionType : function(stats) {
+  checkStatsIceConnectionType : function PCW_checkStatsIceConnectionType(stats)
+  {
     var lId;
     var rId;
-    Object.keys(stats).forEach(name => {
+    Object.keys(stats).forEach(function(name) {
       if ((stats[name].type === "candidatepair") &&
           (stats[name].selected)) {
         lId = stats[name].localCandidateId;
         rId = stats[name].remoteCandidateId;
       }
     });
     info("checkStatsIceConnectionType verifying: local=" +
          JSON.stringify(stats[lId]) + " remote=" + JSON.stringify(stats[rId]));
@@ -1619,36 +2596,36 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {object} stats
    *        The stats to check for ICE candidate pairs
    * @param {object} counters
    *        The counters for media and data tracks based on constraints
    * @param {object} answer
    *        The SDP answer to check for SDP bundle support
    */
-  checkStatsIceConnections : function(stats,
+  checkStatsIceConnections : function PCW_checkStatsIceConnections(stats,
       offerConstraintsList, offerOptions, answer) {
     var numIceConnections = 0;
-    Object.keys(stats).forEach(key => {
+    Object.keys(stats).forEach(function(key) {
       if ((stats[key].type === "candidatepair") && stats[key].selected) {
         numIceConnections += 1;
       }
     });
     info("ICE connections according to stats: " + numIceConnections);
     if (answer.sdp.contains('a=group:BUNDLE')) {
       is(numIceConnections, 1, "stats reports exactly 1 ICE connection");
     } else {
       // This code assumes that no media sections have been rejected due to
       // codec mismatch or other unrecoverable negotiation failures.
       var numAudioTracks =
-          this.countTracksInConstraint('audio', offerConstraintsList) ||
+        this.countAudioTracksInMediaConstraint(offerConstraintsList) ||
         this.audioInOfferOptions(offerOptions);
 
       var numVideoTracks =
-          this.countTracksInConstraint('video', offerConstraintsList) ||
+        this.countVideoTracksInMediaConstraint(offerConstraintsList) ||
         this.videoInOfferOptions(offerOptions);
 
       var numDataTracks = this.dataChannels.length;
 
       var numAudioVideoDataTracks = numAudioTracks + numVideoTracks + numDataTracks;
       info("expected audio + video + data tracks: " + numAudioVideoDataTracks);
       is(numAudioVideoDataTracks, numIceConnections, "stats ICE connections matches expected A/V tracks");
     }
@@ -1658,17 +2635,17 @@ PeerConnectionWrapper.prototype = {
    * Property-matching function for finding a certain stat in passed-in stats
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    * @param {object} props
    *        The properties to look for
    * @returns {boolean} Whether an entry containing all match-props was found.
    */
-  hasStat : function(stats, props) {
+  hasStat : function PCW_hasStat(stats, props) {
     for (var key in stats) {
       if (stats.hasOwnProperty(key)) {
         var res = stats[key];
         var match = true;
         for (var prop in props) {
           if (res[prop] !== props[prop]) {
             match = false;
             break;
@@ -1680,53 +2657,39 @@ PeerConnectionWrapper.prototype = {
       }
     }
     return false;
   },
 
   /**
    * Closes the connection
    */
-  close : function() {
+  close : function PCW_close() {
+    this._ice_candidates_to_add = [];
     this._pc.close();
-    this.localMediaElements.forEach(e => e.pause());
     info(this + ": Closed connection.");
   },
 
   /**
+   * Register all events during the setup of the data channel
+   *
+   * @param {Function} onDataChannelOpened
+   *        Callback to execute when the data channel has been opened
+   */
+  registerDataChannelOpenEvents : function (onDataChannelOpened) {
+    info(this + ": Register callback for 'ondatachannel'");
+
+    this.ondatachannel = function (targetChannel) {
+      this.dataChannels.push(targetChannel);
+      info(this + ": 'ondatachannel' fired, registering 'onopen' callback");
+      targetChannel.onopen = onDataChannelOpened;
+    };
+  },
+
+  /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
    */
-  toString : function() {
+  toString : function PCW_toString() {
     return "PeerConnectionWrapper (" + this.label + ")";
   }
 };
-
-// haxx to prevent SimpleTest from failing at window.onload
-function addLoadEvent() {}
-
-var scriptsReady = Promise.all([
-  "/tests/SimpleTest/SimpleTest.js",
-  "head.js",
-  "templates.js",
-  "turnConfig.js",
-  "dataChannel.js",
-  "network.js"
-].map(script  => {
-  var el = document.createElement("script");
-  if (typeof scriptRelativePath === 'string' && script.charAt(0) !== '/') {
-    script = scriptRelativePath + script;
-  }
-  el.src = script;
-  document.head.appendChild(el);
-  return new Promise(r => { el.onload = r; el.onerror = r; });
-}));
-
-function createHTML(options) {
-  return scriptsReady.then(() => realCreateHTML(options));
-}
-
-function runNetworkTest(testFunction) {
-  return scriptsReady
-    .then(() => startNetworkAndTest())
-    .then(() => runTestWhenReady(testFunction));
-}
--- a/dom/media/tests/mochitest/steeplechase.ini
+++ b/dom/media/tests/mochitest/steeplechase.ini
@@ -1,10 +1,9 @@
 [DEFAULT]
 support-files =
   head.js
   mediaStreamPlayback.js
-  network.js
   pc.js
   templates.js
   turnConfig.js
 
 [test_peerConnection_basicAudio.html]
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -57,407 +57,668 @@ function dumpSdp(test) {
   if ((test.pcLocal) && (test.pcRemote) &&
     (typeof test.pcRemote.setLocalDescDate !== 'undefined') &&
     (typeof test.pcRemote.setLocalDescStableEventDate !== 'undefined')) {
     var delta = deltaSeconds(test.pcRemote.setLocalDescDate, test.pcRemote.setLocalDescStableEventDate);
     dump("Delay between pcRemote.setLocal <-> pcRemote.signalingStateStable: " + delta + "\n");
   }
 }
 
-function waitForIceConnected(test, pc) {
-  if (pc.isIceConnected()) {
-    info(pc + ": ICE connection state log: " + pc.iceConnectionLog);
-    ok(true, pc + ": ICE is in connected state");
-    return Promise.resolve();
-  }
-
-  if (!pc.isIceConnectionPending()) {
-    dumpSdp(test);
-    var details = pc + ": ICE is already in bad state: " + pc.iceConnectionState;
-    ok(false, details);
-    return Promise.reject(new Error(details));
-  }
-
-  return pc.waitForIceConnected()
-    .then(() => {
-      info(pc + ": ICE connection state log: " + pc.iceConnectionLog);
-      ok(pc.isIceConnected(), pc + ": ICE switched to 'connected' state");
-    });
-}
-
-// We need to verify that at least one candidate has been (or will be) gathered.
-function waitForAnIceCandidate(pc) {
-  return new Promise(resolve => {
-    if (!pc.localRequiresTrickleIce ||
-        pc._local_ice_candidates.length > 0) {
-      resolve();
-    } else {
-      // In some circumstances, especially when both PCs are on the same
-      // browser, even though we are connected, the connection can be
-      // established without receiving a single candidate from one or other
-      // peer.  So we wait for at least one...
-      pc._pc.addEventListener('icecandidate', resolve);
+var commandsPeerConnection = [
+  [
+    'PC_SETUP_SIGNALING_CLIENT',
+    function (test) {
+      if (test.steeplechase) {
+        test.setTimeout(30000);
+        test.setupSignalingClient();
+        test.registerSignalingCallback("ice_candidate", function (message) {
+          var pc = test.pcRemote ? test.pcRemote : test.pcLocal;
+          pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate));
+        });
+        test.registerSignalingCallback("end_of_trickle_ice", function (message) {
+          test.signalingMessagesFinished();
+        });
+      }
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_SETUP_ICE_LOGGER',
+    function (test) {
+      test.pcLocal.logIceConnectionState();
+      test.next();
     }
-  }).then(() => {
-    ok(pc._local_ice_candidates.length > 0,
-       pc + " received local trickle ICE candidates");
-    isnot(pc._pc.iceGatheringState, GATH_NEW,
-          pc + " ICE gathering state is not 'new'");
-  });
-}
-
-function checkTrackStats(pc, audio, outbound) {
-  var stream = outbound ? pc._pc.getLocalStreams()[0] : pc._pc.getRemoteStreams()[0];
-  if (!stream) {
-    return Promise.resolve();
-  }
-  var track = audio ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];
-  if (!track) {
-    return Promise.resolve();
-  }
-  var msg = pc + " stats " + (outbound ? "outbound " : "inbound ") +
-      (audio ? "audio" : "video") + " rtp ";
-  return pc.getStats(track).then(stats => {
-    ok(pc.hasStat(stats, {
-      type: outbound ? "outboundrtp" : "inboundrtp",
-      isRemote: false,
-      mediaType: audio ? "audio" : "video"
-    }), msg + "1");
-    ok(!pc.hasStat(stats, {
-      type: outbound ? "inboundrtp" : "outboundrtp",
-      isRemote: false
-    }), msg + "2");
-    ok(!pc.hasStat(stats, {
-      mediaType: audio ? "video" : "audio"
-    }), msg + "3");
-  });
-}
-
-// checks all stats combinations inbound/outbound, audio/video
-var checkAllTrackStats = pc =>
-    Promise.all([0, 1, 2, 3].map(i => checkTrackStats(pc, i & 1, i & 2)));
-
-var commandsPeerConnection = [
-  function PC_SETUP_SIGNALING_CLIENT(test) {
-    if (test.steeplechase) {
-      setTimeout(() => {
-        ok(false, "PeerConnectionTest timed out");
-        test.teardown();
-      }, 30000);
-      test.setupSignalingClient();
-      test.registerSignalingCallback("ice_candidate", function (message) {
-        var pc = test.pcRemote ? test.pcRemote : test.pcLocal;
-        pc.storeOrAddIceCandidate(new mozRTCIceCandidate(message.ice_candidate));
+  ],
+  [
+    'PC_REMOTE_SETUP_ICE_LOGGER',
+    function (test) {
+      test.pcRemote.logIceConnectionState();
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_SETUP_SIGNALING_LOGGER',
+    function (test) {
+      test.pcLocal.logSignalingState();
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SETUP_SIGNALING_LOGGER',
+    function (test) {
+      test.pcRemote.logSignalingState();
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_GUM',
+    function (test) {
+      test.pcLocal.getAllUserMedia(test.pcLocal.constraints, function () {
+        test.next();
       });
-      test.registerSignalingCallback("end_of_trickle_ice", function (message) {
-        test.signalingMessagesFinished();
+    }
+  ],
+  [
+    'PC_REMOTE_GUM',
+    function (test) {
+      test.pcRemote.getAllUserMedia(test.pcRemote.constraints, function () {
+        test.next();
       });
     }
-  },
-
-  function PC_LOCAL_SETUP_ICE_LOGGER(test) {
-    test.pcLocal.logIceConnectionState();
-  },
-
-  function PC_REMOTE_SETUP_ICE_LOGGER(test) {
-    test.pcRemote.logIceConnectionState();
-  },
-
-  function PC_LOCAL_SETUP_SIGNALING_LOGGER(test) {
-    test.pcLocal.logSignalingState();
-  },
-
-  function PC_REMOTE_SETUP_SIGNALING_LOGGER(test) {
-    test.pcRemote.logSignalingState();
-  },
-
-  function PC_LOCAL_GUM(test) {
-    return test.pcLocal.getAllUserMedia(test.pcLocal.constraints);
-  },
-
-  function PC_REMOTE_GUM(test) {
-    return test.pcRemote.getAllUserMedia(test.pcRemote.constraints);
-  },
-
-  function PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE(test) {
-    is(test.pcLocal.signalingState, STABLE,
-       "Initial local signalingState is 'stable'");
-  },
-
-  function PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE(test) {
-    is(test.pcRemote.signalingState, STABLE,
-       "Initial remote signalingState is 'stable'");
-  },
-
-  function PC_LOCAL_CHECK_INITIAL_ICE_STATE(test) {
-    is(test.pcLocal.iceConnectionState, ICE_NEW,
-       "Initial local ICE connection state is 'new'");
-  },
-
-  function PC_REMOTE_CHECK_INITIAL_ICE_STATE(test) {
-    is(test.pcRemote.iceConnectionState, ICE_NEW,
-       "Initial remote ICE connection state is 'new'");
-  },
-
-  function PC_LOCAL_SETUP_ICE_HANDLER(test) {
-    test.pcLocal.setupIceCandidateHandler(test);
-    if (test.steeplechase) {
-      test.pcLocal.endOfTrickleIce.then(() => {
-        send_message({"type": "end_of_trickle_ice"});
-      });
+  ],
+  [
+    'PC_LOCAL_CHECK_INITIAL_SIGNALINGSTATE',
+    function (test) {
+      is(test.pcLocal.signalingState, STABLE,
+         "Initial local signalingState is 'stable'");
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_INITIAL_SIGNALINGSTATE',
+    function (test) {
+      is(test.pcRemote.signalingState, STABLE,
+         "Initial remote signalingState is 'stable'");
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_INITIAL_ICE_STATE',
+    function (test) {
+      is(test.pcLocal.iceConnectionState, ICE_NEW,
+        "Initial local ICE connection state is 'new'");
+      test.next();
     }
-  },
-
-  function PC_REMOTE_SETUP_ICE_HANDLER(test) {
-    test.pcRemote.setupIceCandidateHandler(test);
-    if (test.steeplechase) {
-      test.pcRemote.endOfTrickleIce.then(() => {
-        send_message({"type": "end_of_trickle_ice"});
+  ],
+  [
+    'PC_REMOTE_CHECK_INITIAL_ICE_STATE',
+    function (test) {
+      is(test.pcRemote.iceConnectionState, ICE_NEW,
+        "Initial remote ICE connection state is 'new'");
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_SETUP_ICE_HANDLER',
+    function (test) {
+      test.pcLocal.setupIceCandidateHandler(test);
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SETUP_ICE_HANDLER',
+    function (test) {
+      test.pcRemote.setupIceCandidateHandler(test);
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_CREATE_OFFER',
+    function (test) {
+      test.createOffer(test.pcLocal, function (offer) {
+        is(test.pcLocal.signalingState, STABLE,
+           "Local create offer does not change signaling state");
+        test.next();
       });
     }
-  },
-
-  function PC_LOCAL_CREATE_OFFER(test) {
-    return test.createOffer(test.pcLocal).then(offer => {
-      is(test.pcLocal.signalingState, STABLE,
-         "Local create offer does not change signaling state");
-    });
-  },
-
-  function PC_LOCAL_STEEPLECHASE_SIGNAL_OFFER(test) {
-    if (test.steeplechase) {
-      send_message({"type": "offer",
-                    "offer": test.originalOffer,
-                    "offer_constraints": test.pcLocal.constraints,
-                    "offer_options": test.pcLocal.offerOptions});
-      test._local_offer = test.originalOffer;
-      test._offer_constraints = test.pcLocal.constraints;
-      test._offer_options = test.pcLocal.offerOptions;
+  ],
+  [
+    'PC_LOCAL_STEEPLECHASE_SIGNAL_OFFER',
+    function (test) {
+      if (test.steeplechase) {
+        send_message({"type": "offer",
+          "offer": test.originalOffer,
+          "offer_constraints": test.pcLocal.constraints,
+          "offer_options": test.pcLocal.offerOptions});
+        test._local_offer = test.originalOffer;
+        test._offer_constraints = test.pcLocal.constraints;
+        test._offer_options = test.pcLocal.offerOptions;
+      }
+      test.next();
     }
-  },
-
-  function PC_LOCAL_SET_LOCAL_DESCRIPTION(test) {
-    return test.setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER)
-      .then(() => {
+  ],
+  [
+    'PC_LOCAL_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER, function () {
         is(test.pcLocal.signalingState, HAVE_LOCAL_OFFER,
            "signalingState after local setLocalDescription is 'have-local-offer'");
+        test.next();
       });
-  },
-
-  function PC_REMOTE_GET_OFFER(test) {
-    if (!test.steeplechase) {
-      test._local_offer = test.originalOffer;
-      test._offer_constraints = test.pcLocal.constraints;
-      test._offer_options = test.pcLocal.offerOptions;
-      return Promise.resolve();
     }
-    return test.getSignalingMessage("offer")
-      .then(message => {
-        ok("offer" in message, "Got an offer message");
-        test._local_offer = new mozRTCSessionDescription(message.offer);
-        test._offer_constraints = message.offer_constraints;
-        test._offer_options = message.offer_options;
-      });
-  },
-
-  function PC_REMOTE_SET_REMOTE_DESCRIPTION(test) {
-    return test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER)
-      .then(() => {
+  ],
+  [
+    'PC_REMOTE_GET_OFFER',
+    function (test) {
+      if (!test.steeplechase) {
+        test._local_offer = test.originalOffer;
+        test._offer_constraints = test.pcLocal.constraints;
+        test._offer_options = test.pcLocal.offerOptions;
+        test.next();
+      } else {
+        test.getSignalingMessage("offer", function (message) {
+          ok("offer" in message, "Got an offer message");
+          test._local_offer = new mozRTCSessionDescription(message.offer);
+          test._offer_constraints = message.offer_constraints;
+          test._offer_options = message.offer_options;
+          test.next();
+        });
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_SET_REMOTE_DESCRIPTION',
+    function (test) {
+      test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER, function () {
         is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
            "signalingState after remote setRemoteDescription is 'have-remote-offer'");
+        test.next();
       });
-  },
-
-  function PC_LOCAL_SANE_LOCAL_SDP(test) {
-    test.pcLocal.verifySdp(test._local_offer, "offer",
-                           test._offer_constraints, test._offer_options,
-                           true);
-  },
-
-  function PC_REMOTE_SANE_REMOTE_SDP(test) {
-    test.pcRemote.verifySdp(test._local_offer, "offer",
-                            test._offer_constraints, test._offer_options,
-                            false);
-  },
-
-  function PC_REMOTE_CREATE_ANSWER(test) {
-    return test.createAnswer(test.pcRemote)
-      .then(answer => {
+    }
+  ],
+  [
+    'PC_LOCAL_SANE_LOCAL_SDP',
+    function (test) {
+      test.pcLocal.verifySdp(test._local_offer, "offer",
+        test._offer_constraints, test._offer_options,
+        function(trickle) {
+          test.pcLocal.localRequiresTrickleIce = trickle;
+        });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SANE_REMOTE_SDP',
+    function (test) {
+      test.pcRemote.verifySdp(test._local_offer, "offer",
+        test._offer_constraints, test._offer_options,
+        function (trickle) {
+          test.pcRemote.remoteRequiresTrickleIce = trickle;
+        });
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_CREATE_ANSWER',
+    function (test) {
+      test.createAnswer(test.pcRemote, function (answer) {
         is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER,
            "Remote createAnswer does not change signaling state");
         if (test.steeplechase) {
           send_message({"type": "answer",
                         "answer": test.originalAnswer,
                         "answer_constraints": test.pcRemote.constraints});
           test._remote_answer = test.pcRemote._last_answer;
           test._answer_constraints = test.pcRemote.constraints;
         }
+        test.next();
       });
-  },
-
-  function PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP(test) {
-    var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g;
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_FOR_DUPLICATED_PORTS_IN_SDP',
+    function (test) {
+      var re = /a=candidate.* (UDP|TCP) [\d]+ ([\d\.]+) ([\d]+) typ host/g;
 
-    var _sdpCandidatesIntoArray = sdp => {
-      var regexArray = [];
-      var resultArray = [];
-      while ((regexArray = re.exec(sdp)) !== null) {
-        info("regexArray: " + regexArray);
-        if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) {
-          // As both sides can advertise TCP active connection on port 9 lets
-          // ignore them all together
-          info("Ignoring TCP candidate on port 9");
-          continue;
+      function _sdpCandidatesIntoArray(sdp) {
+        var regexArray = [];
+        var resultArray = [];
+        while ((regexArray = re.exec(sdp)) !== null) {
+          info("regexArray: " + regexArray);
+          if ((regexArray[1] === "TCP") && (regexArray[3] === "9")) {
+            // As both sides can advertise TCP active connection on port 9 lets
+            // ignore them all together
+            info("Ignoring TCP candidate on port 9");
+            continue;
+          }
+          const triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3];
+          info("triple: " + triple);
+          if (resultArray.indexOf(triple) !== -1) {
+            dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n");
+            ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!");
+          }
+          resultArray.push(triple);
         }
-        var triple = regexArray[1] + ":" + regexArray[2] + ":" + regexArray[3];
-        info("triple: " + triple);
-        if (resultArray.indexOf(triple) !== -1) {
-          dump("SDP: " + sdp.replace(/[\r]/g, '') + "\n");
-          ok(false, "This Transport:IP:Port " + triple + " appears twice in the SDP above!");
+        return resultArray;
+      }
+
+      const offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp);
+      info("Offer ICE host candidates: " + JSON.stringify(offerTriples));
+
+      const answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp);
+      info("Answer ICE host candidates: " + JSON.stringify(answerTriples));
+
+      for (var i=0; i< offerTriples.length; i++) {
+        if (answerTriples.indexOf(offerTriples[i]) !== -1) {
+          dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n");
+          dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n");
+          ok(false, "This IP:Port " + offerTriples[i] + " appears in SDP offer and answer!");
         }
-        resultArray.push(triple);
       }
-      return resultArray;
-    };
 
-    var offerTriples = _sdpCandidatesIntoArray(test._local_offer.sdp);
-    info("Offer ICE host candidates: " + JSON.stringify(offerTriples));
-
-    var answerTriples = _sdpCandidatesIntoArray(test.originalAnswer.sdp);
-    info("Answer ICE host candidates: " + JSON.stringify(answerTriples));
-
-    offerTriples.forEach(o => {
-      if (answerTriples.indexOf(o) !== -1) {
-        dump("SDP offer: " + test._local_offer.sdp.replace(/[\r]/g, '') + "\n");
-        dump("SDP answer: " + test.originalAnswer.sdp.replace(/[\r]/g, '') + "\n");
-        ok(false, "This IP:Port " + o + " appears in SDP offer and answer!");
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_SET_LOCAL_DESCRIPTION',
+    function (test) {
+      test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE,
+        function () {
+          is(test.pcRemote.signalingState, STABLE,
+            "signalingState after remote setLocalDescription is 'stable'");
+          test.next();
+        }
+      );
+    }
+  ],
+  [
+    'PC_LOCAL_GET_ANSWER',
+    function (test) {
+      if (!test.steeplechase) {
+        test._remote_answer = test.originalAnswer;
+        test._answer_constraints = test.pcRemote.constraints;
+        test.next();
+      } else {
+        test.getSignalingMessage("answer", function (message) {
+          ok("answer" in message, "Got an answer message");
+          test._remote_answer = new mozRTCSessionDescription(message.answer);
+          test._answer_constraints = message.answer_constraints;
+          test.next();
+        });
       }
-    });
-  },
-
-  function PC_REMOTE_SET_LOCAL_DESCRIPTION(test) {
-    return test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE)
-      .then(() => {
-        is(test.pcRemote.signalingState, STABLE,
-           "signalingState after remote setLocalDescription is 'stable'");
-      });
-  },
-
-  function PC_LOCAL_GET_ANSWER(test) {
-    if (!test.steeplechase) {
-      test._remote_answer = test.originalAnswer;
-      test._answer_constraints = test.pcRemote.constraints;
-      return Promise.resolve();
+    }
+  ],
+  [
+    'PC_LOCAL_SET_REMOTE_DESCRIPTION',
+    function (test) {
+      test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE,
+        function () {
+          is(test.pcLocal.signalingState, STABLE,
+            "signalingState after local setRemoteDescription is 'stable'");
+          test.next();
+        }
+      );
     }
+  ],
+  [
+    'PC_REMOTE_SANE_LOCAL_SDP',
+    function (test) {
+      test.pcRemote.verifySdp(test._remote_answer, "answer",
+        test._offer_constraints, test._offer_options,
+        function (trickle) {
+          test.pcRemote.localRequiresTrickleIce = trickle;
+        });
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_SANE_REMOTE_SDP',
+    function (test) {
+      test.pcLocal.verifySdp(test._remote_answer, "answer",
+        test._offer_constraints, test._offer_options,
+        function (trickle) {
+          test.pcLocal.remoteRequiresTrickleIce = trickle;
+        });
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_WAIT_FOR_ICE_CONNECTED',
+    function (test) {
+      var myTest = test;
+      var myPc = myTest.pcLocal;
 
-    return test.getSignalingMessage("answer").then(message => {
-      ok("answer" in message, "Got an answer message");
-      test._remote_answer = new mozRTCSessionDescription(message.answer);
-      test._answer_constraints = message.answer_constraints;
-    });
-  },
+      function onIceConnectedSuccess () {
+        info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog);
+        ok(true, "pc_local: ICE switched to 'connected' state");
+        myTest.next();
+      };
+      function onIceConnectedFailed () {
+        dumpSdp(myTest);
+        ok(false, "pc_local: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState);
+        myTest.next();
+      };
 
-  function PC_LOCAL_SET_REMOTE_DESCRIPTION(test) {
-    test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE)
-      .then(() => {
-        is(test.pcLocal.signalingState, STABLE,
-           "signalingState after local setRemoteDescription is 'stable'");
-      });
-  },
-  function PC_REMOTE_SANE_LOCAL_SDP(test) {
-    test.pcRemote.verifySdp(test._remote_answer, "answer",
-                            test._offer_constraints, test._offer_options,
-                            true);
-  },
-  function PC_LOCAL_SANE_REMOTE_SDP(test) {
-    test.pcLocal.verifySdp(test._remote_answer, "answer",
-                           test._offer_constraints, test._offer_options,
-                           false);
-  },
+      if (myPc.isIceConnected()) {
+        info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog);
+        ok(true, "pc_local: ICE is in connected state");
+        myTest.next();
+      } else if (myPc.isIceConnectionPending()) {
+        myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
+      } else {
+        dumpSdp(myTest);
+        ok(false, "pc_local: ICE is already in bad state: " + myPc.iceConnectionState);
+        myTest.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_VERIFY_ICE_GATHERING',
+    function (test) {
+      if (test.pcLocal.localRequiresTrickleIce) {
+        ok(test.pcLocal._local_ice_candidates.length > 0, "Received local trickle ICE candidates");
+      }
+      isnot(test.pcLocal._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'");
+      test.next();
+    }
+  ],
+  [
+    'PC_REMOTE_WAIT_FOR_ICE_CONNECTED',
+    function (test) {
+      var myTest = test;
+      var myPc = myTest.pcRemote;
+
+      function onIceConnectedSuccess () {
+        info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog);
+        ok(true, "pc_remote: ICE switched to 'connected' state");
+        myTest.next();
+      };
+      function onIceConnectedFailed () {
+        dumpSdp(myTest);
+        ok(false, "pc_remote: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState);
+        myTest.next();
+      };
 
-  function PC_LOCAL_WAIT_FOR_ICE_CONNECTED(test) {
-    return waitForIceConnected(test, test.pcLocal);
-  },
-
-  function PC_REMOTE_WAIT_FOR_ICE_CONNECTED(test) {
-    return waitForIceConnected(test, test.pcRemote);
-  },
-
-  function PC_LOCAL_VERIFY_ICE_GATHERING(test) {
-    return waitForAnIceCandidate(test.pcLocal);
-  },
-
-  function PC_REMOTE_VERIFY_ICE_GATHERING(test) {
-    return waitForAnIceCandidate(test.pcRemote);
-  },
-
-  function PC_LOCAL_CHECK_MEDIA_TRACKS(test) {
-    return test.pcLocal.checkMediaTracks(test._answer_constraints);
-  },
-
-  function PC_REMOTE_CHECK_MEDIA_TRACKS(test) {
-    return test.pcRemote.checkMediaTracks(test._offer_constraints);
-  },
-
-  function PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT(test) {
-    return test.pcLocal.checkMediaFlowPresent();
-  },
+      if (myPc.isIceConnected()) {
+        info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog);
+        ok(true, "pc_remote: ICE is in connected state");
+        myTest.next();
+      } else if (myPc.isIceConnectionPending()) {
+        myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
+      } else {
+        dumpSdp(myTest);
+        ok(false, "pc_remote: ICE is already in bad state: " + myPc.iceConnectionState);
+        myTest.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_VERIFY_ICE_GATHERING',
+    function (test) {
+      if (test.pcRemote.localRequiresTrickleIce) {
+        ok(test.pcRemote._local_ice_candidates.length > 0, "Received local trickle ICE candidates");
+      }
+      isnot(test.pcRemote._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'");
+      test.next();
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_MEDIA_TRACKS',
+    function (test) {
+      test.pcLocal.checkMediaTracks(test._answer_constraints, function () {
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_MEDIA_TRACKS',
+    function (test) {
+      test.pcRemote.checkMediaTracks(test._offer_constraints, function () {
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT',
+    function (test) {
+      test.pcLocal.checkMediaFlowPresent(function () {
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT',
+    function (test) {
+      test.pcRemote.checkMediaFlowPresent(function () {
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_STATS',
+    function (test) {
+      test.pcLocal.getStats(null, function(stats) {
+        test.pcLocal.checkStats(stats, test.steeplechase);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_STATS',
+    function (test) {
+      test.pcRemote.getStats(null, function(stats) {
+        test.pcRemote.checkStats(stats, test.steeplechase);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_ICE_CONNECTION_TYPE',
+    function (test) {
+      test.pcLocal.getStats(null, function(stats) {
+        test.pcLocal.checkStatsIceConnectionType(stats);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_ICE_CONNECTION_TYPE',
+    function (test) {
+      test.pcRemote.getStats(null, function(stats) {
+        test.pcRemote.checkStatsIceConnectionType(stats);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_ICE_CONNECTIONS',
+    function (test) {
+      test.pcLocal.getStats(null, function(stats) {
+        test.pcLocal.checkStatsIceConnections(stats,
+                                              test._offer_constraints,
+                                              test._offer_options,
+                                              test._remote_answer);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_ICE_CONNECTIONS',
+    function (test) {
+      test.pcRemote.getStats(null, function(stats) {
+        test.pcRemote.checkStatsIceConnections(stats,
+                                               test._offer_constraints,
+                                               test._offer_options,
+                                               test.originalAnswer);
+        test.next();
+      });
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat outbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat inbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_LOCAL_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcLocal;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcLocal.HasStat inbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_OUTBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getLocalStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat outbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"outboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"inboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_AUDIOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getAudioTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat inbound audio rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"audio" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"video" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ],
+  [
+    'PC_REMOTE_CHECK_GETSTATS_VIDEOTRACK_INBOUND',
+    function (test) {
+      var pc = test.pcRemote;
+      var stream = pc._pc.getRemoteStreams()[0];
+      var track = stream && stream.getVideoTracks()[0];
+      if (track) {
+        var msg = "pcRemote.HasStat inbound video rtp ";
+        pc.getStats(track, function(stats) {
+          ok(pc.hasStat(stats,
+                        { type:"inboundrtp", isRemote:false, mediaType:"video" }),
+             msg + "1");
+          ok(!pc.hasStat(stats, { type:"outboundrtp", isRemote:false }), msg + "2");
+          ok(!pc.hasStat(stats, { mediaType:"audio" }), msg + "3");
+          test.next();
+        });
+      } else {
+        test.next();
+      }
+    }
+  ]
+];
 
-  function PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT(test) {
-    return test.pcRemote.checkMediaFlowPresent();
-  },
-/* TODO: re-enable when Bug 1095218 lands
-  function PC_LOCAL_CHECK_MSID(test) {
-    test.pcLocal.checkMsids();
-  },
-  function PC_REMOTE_CHECK_MSID(test) {
-    test.pcRemote.checkMsids();
-  },
-*/
-  function PC_LOCAL_CHECK_STATS(test) {
-    return test.pcLocal.getStats(null).then(stats => {
-      test.pcLocal.checkStats(stats, test.steeplechase);
-    });
-  },
-
-  function PC_REMOTE_CHECK_STATS(test) {
-    test.pcRemote.getStats(null).then(stats => {
-      test.pcRemote.checkStats(stats, test.steeplechase);
-    });
-  },
-
-  function PC_LOCAL_CHECK_ICE_CONNECTION_TYPE(test) {
-    test.pcLocal.getStats(null).then(stats => {
-      test.pcLocal.checkStatsIceConnectionType(stats);
-    });
-  },
-
-  function PC_REMOTE_CHECK_ICE_CONNECTION_TYPE(test) {
-    test.pcRemote.getStats(null).then(stats => {
-      test.pcRemote.checkStatsIceConnectionType(stats);
-    });
-  },
-
-  function PC_LOCAL_CHECK_ICE_CONNECTIONS(test) {
-    test.pcLocal.getStats(null).then(stats => {
-      test.pcLocal.checkStatsIceConnections(stats,
-                                            test._offer_constraints,
-                                            test._offer_options,
-                                            test._remote_answer);
-    });
-  },
-
-  function PC_REMOTE_CHECK_ICE_CONNECTIONS(test) {
-    test.pcRemote.getStats(null).then(stats => {
-      test.pcRemote.checkStatsIceConnections(stats,
-                                             test._offer_constraints,
-                                             test._offer_options,
-                                             test.originalAnswer);
-    });
-  },
-
-  function PC_LOCAL_CHECK_STATS(test) {
-    return checkAllTrackStats(test.pcLocal);
-  },
-  function PC_REMOTE_CHECK_STATS(test) {
-    return checkAllTrackStats(test.pcRemote);
-  }
-];
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudio.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicAudio.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796895",
     title: "Basic data channel audio connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796891",
     title: "Basic data channel audio/video connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796891",
     title: "Basic data channel audio/video connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html
@@ -1,33 +1,44 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1016476",
     title: "Basic data channel audio/video connection without bundle"
   });
 
-var test;
-runNetworkTest(function () {
-  test = new PeerConnectionTest();
-  addInitialDataChannel(test.chain);
-  test.chain.insertAfter("PC_LOCAL_CREATE_OFFER", [
-    function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
-      // Just replace a=group:BUNDLE with something that will be ignored.
-      test.originalOffer.sdp = test.originalOffer.sdp.replace(
-        "a=group:BUNDLE",
-        "a=foo:");
-    }
-  ]);
-  test.setMediaConstraints([{audio: true}, {video: true}],
-                           [{audio: true}, {video: true}]);
-  test.run();
-});
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    addInitialDataChannel(test.chain);
+    test.chain.insertAfter("PC_LOCAL_CREATE_OFFER",
+      [[
+        'PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER',
+        function (test) {
+          // Just replace a=group:BUNDLE with something that will be ignored.
+          test.originalOffer.sdp = test.originalOffer.sdp.replace(
+            "a=group:BUNDLE",
+            "a=foo:");
+          test.next();
+        }
+      ]]
+      );
+    test.setMediaConstraints([{audio: true}, {video: true}],
+                             [{audio: true}, {video: true}]);
+    test.run();
+  });
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796894",
     title: "Basic datachannel only connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796889",
     title: "Basic data channel video connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
+++ b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="dataChannel.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796895",
     title: "Basic data channel audio connection"
   });
--- a/dom/media/tests/mochitest/test_dataChannel_noOffer.html
+++ b/dom/media/tests/mochitest/test_dataChannel_noOffer.html
@@ -1,11 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "856319",
     title: "Don't offer m=application unless createDataChannel is called first"
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -1,29 +1,46 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Audio Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Audio Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></audio>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Basic Audio Test", bug: "781534" });
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an audio LocalMediaStream on an audio HTMLMediaElement.
    */
   runTest(function () {
-    var testAudio = createMediaElement('audio', 'testAudio');
+    var testAudio = document.getElementById('testAudio');
     var constraints = {audio: true};
 
-    getUserMedia(constraints).then(aStream => {
+    getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testAudio, aStream);
-      return playback.playMedia(false);
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+      playback.playMedia(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -1,45 +1,58 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Screenshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983504">mozGetUserMedia Basic Screenshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    title: "getUserMedia Basic Screenshare Test",
-    bug: "983504"
-  });
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
     const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
     if (IsMacOSX10_6orOlder() || isWinXP) {
         ok(true, "Screensharing disabled for OSX10.6 and WinXP");
         SimpleTest.finish();
         return;
     }
-    var testVideo = createMediaElement('video', 'testVideo');
+    var testVideo = document.getElementById('testVideo');
     var constraints = {
       video: {
          mozMediaSource: "screen",
          mediaSource: "screen"
       },
       fake: false
     };
 
-    getUserMedia(constraints).then(aStream => {
+    getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideo, aStream);
-      return playback.playMediaWithStreamStop(false);
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -1,32 +1,46 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Video Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    title: "getUserMedia Basic Video Test",
-    bug: "781534"
-  });
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an video LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
-    var testVideo = createMediaElement('video', 'testVideo');
+    var testVideo = document.getElementById('testVideo');
     var constraints = {video: true};
 
-    getUserMedia(constraints).then(aStream => {
+    getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideo, aStream);
-      return playback.playMedia(false);
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+      playback.playMedia(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -1,32 +1,45 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781534
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Video & Audio Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781534">mozGetUserMedia Basic Video & Audio Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideoAudio"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    title: "getUserMedia Basic Video & Audio Test",
-    bug: "781534"
-  });
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for a video and audio LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
-    var testVideoAudio = createMediaElement('video', 'testVideoAudio');
+    var testVideoAudio = document.getElementById('testVideoAudio');
     var constraints = {video: true, audio: true};
 
-    getUserMedia(constraints).then(aStream => {
+    getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream);
-      return playback.playMedia(false);
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+      playback.playMedia(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
@@ -1,45 +1,58 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Windowshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038926">mozGetUserMedia Basic Windowshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    title: "getUserMedia Basic Windowshare Test",
-    bug: "1038926"
-  });
   /**
    * Run a test to verify that we can complete a start and stop media playback
    * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
    */
   runTest(function () {
     const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
     if (IsMacOSX10_6orOlder() || isWinXP) {
         ok(true, "Screensharing disabled for OSX10.6 and WinXP");
         SimpleTest.finish();
         return;
     }
-    var testVideo = createMediaElement('video', 'testVideo');
+    var testVideo = document.getElementById('testVideo');
     var constraints = {
       video: {
          mozMediaSource: "window",
          mediaSource: "window"
       },
       fake: false
     };
 
-    getUserMedia(constraints).then(aStream => {
+    getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideo, aStream);
-      return playback.playMediaWithStreamStop(false);
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
deleted file mode 100644
--- a/dom/media/tests/mochitest/test_getUserMedia_callbacks.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
-</head>
-<body>
-<pre id="test">
-<script type="application/javascript">
-  createHTML({
-    title: "navigator.mozGetUserMedia Callback Test",
-    bug: "1119593"
-  });
-  /**
-   * Check that the old fashioned callback-based function works.
-   */
-  runTest(function () {
-    var testAudio = createMediaElement('audio', 'testAudio');
-    var constraints = {audio: true};
-
-    SimpleTest.waitForExplicitFinish();
-    navigator.mozGetUserMedia(constraints, aStream => {
-      checkMediaStreamTracks(constraints, aStream);
-
-      var playback = new LocalMediaStreamPlayback(testAudio, aStream);
-      return playback.playMedia(false)
-        .then(() => SimpleTest.finish(), generateErrorCallback());
-    }, generateErrorCallback());
-  });
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -1,18 +1,29 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=882145
+-->
 <head>
-  <script src="mediaStreamPlayback.js"></script>
-  <script src="constraints.js"></script>
+  <meta charset="utf-8">
+  <title>Test mozGetUserMedia Constraints</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="constraints.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882145">Test mozGetUserMedia Constraints (desktop)</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
 <pre id="test">
 <script type="application/javascript">
-createHTML({ title: "Test getUserMedia constraints (desktop)", bug: "882145" });
 /**
   See constraints.js for testConstraints() and common_tests.
   TODO(jib): Merge desktop and mobile version of these tests again (Bug 997365)
 */
 var desktop_tests = [
   { message: "legacy facingMode ignored (desktop)",
     constraints: { video: { mandatory: { facingMode:'left' } }, fake: true },
     error: null },
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints_mobile.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints_mobile.html
@@ -1,18 +1,29 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=882145
+-->
 <head>
-  <script src="mediaStreamPlayback.js"></script>
-  <script src="constraints.js"></script>
+  <meta charset="utf-8">
+  <title>Test mozGetUserMedia Constraints</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="constraints.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=882145">Test mozGetUserMedia Constraints (mobile)</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
 <pre id="test">
 <script type="application/javascript">
-createHTML({ title: "Test getUserMedia constraints (mobile)", bug: "882145" });
 /**
   See constraints.js for testConstraints() and common_tests.
   TODO(jib): Merge desktop and mobile version of these tests again (Bug 997365)
 */
 var mobile_tests = [
   { message: "legacy facingMode overconstrains video (mobile)",
     constraints: { video: { mandatory: { facingMode:'left' } }, fake: true },
     error: "NotFoundError" },
--- a/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_exceptions.html
@@ -1,17 +1,28 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=795367
+-->
 <head>
-  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <meta charset="utf-8">
+  <title>Test mozGetUserMedia Exceptions</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795367">Test mozGetUserMedia Exceptions</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
 <pre id="test">
 <script type="application/javascript">
-createHTML({ title: "Test mozGetUserMedia Exceptions", bug: "795367" });
 /**
   These tests verify that the appropriate exception is thrown when incorrect
   values are provided to the immediate mozGetUserMedia call.
 */
 var exceptionTests = [
   // Each test here verifies that a caller is required to have all
   // three arguments in order to call mozGetUserMedia
   { params: undefined,
--- a/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
@@ -1,40 +1,56 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia gum within gum</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia gum within gum</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+  <audio id="testAudio"></audio>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({title: "getUserMedia within getUserMedia", bug: "822109" });
   /**
    * Run a test that we can complete a playback cycle for a video,
    * then upon completion, do a playback cycle with audio, such that
    * the audio gum call happens within the video gum call.
    */
   runTest(function () {
-    getUserMedia({video: true})
-      .then(videoStream => {
-        var testVideo = createMediaElement('video', 'testVideo');
-        var videoPlayback = new LocalMediaStreamPlayback(testVideo,
-                                                         videoStream);
+    getUserMedia({video: true}, function(videoStream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
+        videoStream);
+
+      videoStreamPlayback.playMedia(false, function() {
+        getUserMedia({audio: true}, function(audioStream) {
+          var testAudio = document.getElementById('testAudio');
+          var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
+            audioStream);
 
-        return videoPlayback.playMedia(false)
-          .then(() => getUserMedia({audio: true}))
-          .then(audioStream => {
-            var testAudio = createMediaElement('audio', 'testAudio');
-            var audioPlayback = new LocalMediaStreamPlayback(testAudio,
-                                                             audioStream);
+          audioStreamPlayback.playMedia(false, function() {
+            audioStream.stop();
+            videoStream.stop();
+            SimpleTest.finish();
+          }, generateErrorCallback());
 
-            return audioPlayback.playMedia(false)
-              .then(() => audioStream.stop());
-          })
-          .then(() => videoStream.stop());
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_peerIdentity.html
@@ -1,18 +1,29 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=942367
+-->
 <head>
-  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <meta charset="utf-8">
+  <title>Test mozGetUserMedia peerIdentity Constraint</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="blacksilence.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942367">Test mozGetUserMedia peerIdentity Constraint</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
 <pre id="test">
 <script type="application/javascript">
-createHTML({ title: "Test getUserMedia peerIdentity Constraint", bug: "942367" });
 function theTest() {
   function testPeerIdentityConstraint(withConstraint, done) {
     var config = { audio: true, video: true, fake: true };
     if (withConstraint) {
       config.peerIdentity = 'user@example.com';
     }
     info('getting media with constraints: ' + JSON.stringify(config));
     navigator.mediaDevices.getUserMedia(config).then(function(stream) {
--- a/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
@@ -1,26 +1,46 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Audio Twice</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Audio Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></audio>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({title: "getUserMedia Play Audio Twice", bug: "822109" });
   /**
    * Run a test that we can complete an audio playback cycle twice in a row.
    */
   runTest(function () {
-    getUserMedia({audio: true}).then(audioStream => {
-      var testAudio = createMediaElement('audio', 'testAudio');
-      var playback = new LocalMediaStreamPlayback(testAudio, audioStream);
+    getUserMedia({audio: true}, function(audioStream) {
+      var testAudio = document.getElementById('testAudio');
+      var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
+        audioStream);
+
+      audioStreamPlayback.playMedia(false, function() {
 
-      return playback.playMedia(false)
-        .then(() => playback.playMedia(true))
-        .then(() => audioStream.stop());
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+        audioStreamPlayback.playMedia(true, function() {
+          audioStream.stop();
+          SimpleTest.finish();
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
@@ -1,27 +1,45 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Video and Audio Twice</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video and Audio Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({title: "getUserMedia Play Video and Audio Twice", bug: "822109" });
   /**
    * Run a test that we can complete a video playback cycle twice in a row.
    */
   runTest(function () {
-    getUserMedia({video: true, audio: true}).then(stream => {
-      var testVideo = createMediaElement('video', 'testVideo');
-      var playback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true, audio: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+
+      streamPlayback.playMedia(false, function() {
 
-      return playback.playMedia(false)
-        .then(() => playback.playMedia(true))
-        .then(() => stream.stop());
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+        streamPlayback.playMedia(true, function() {
+          stream.stop();
+          SimpleTest.finish();
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
@@ -1,27 +1,46 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Play Video Twice</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Play Video Twice</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Play Video Twice", bug: "822109" });
   /**
    * Run a test that we can complete a video playback cycle twice in a row.
    */
   runTest(function () {
-    getUserMedia({video: true}).then(stream => {
-      var testVideo = createMediaElement('video', 'testVideo');
-      var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true}, function(videoStream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
+        videoStream);
+
+      videoStreamPlayback.playMedia(false, function() {
 
-      return streamPlayback.playMedia(false)
-        .then(() => streamPlayback.playMedia(true))
-        .then(() => stream.stop());
-    }).then(() => SimpleTest.finish(), generateErrorCallback());
+        videoStreamPlayback.playMedia(true, function() {
+          videoStream.stop();
+          SimpleTest.finish();
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
@@ -1,28 +1,39 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Audio Stream</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Stop Audio Stream", bug: "822109" });
   /**
    * Run a test to verify that we can start an audio stream in a media element,
    * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
-    getUserMedia({audio: true})
-      .then(stream => {
-        var testAudio = createMediaElement('audio', 'testAudio');
-        var streamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
+    getUserMedia({audio: true}, function(stream) {
+      var testAudio = document.getElementById('testAudio');
+      var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
 
-        return streamPlayback.playMediaWithStreamStop(false);
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+      audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
@@ -1,36 +1,51 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Audio Stream With Followup Audio</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Audio Stream With Followup Audio</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <audio id="testAudio"></audio>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Stop Audio Stream With Followup Audio", bug: "822109" });
   /**
    * Run a test to verify that I can complete an audio gum playback in a media
    * element, stop the stream, and then complete another audio gum playback
    * in a media element.
    */
   runTest(function () {
-    getUserMedia({audio: true})
-      .then(firstStream => {
-        var testAudio = createMediaElement('audio', 'testAudio');
-        var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream);
+    getUserMedia({audio: true}, function(firstStream) {
+      var testAudio = document.getElementById('testAudio');
+      var streamPlayback = new LocalMediaStreamPlayback(testAudio, firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({audio: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
 
-        return streamPlayback.playMediaWithStreamStop(false)
-          .then(() => getUserMedia({audio: true}))
-          .then(secondStream => {
-            streamPlayback.mediaStream = secondStream;
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, generateErrorCallback());
 
-            return streamPlayback.playMedia(false)
-              .then(() => secondStream.stop());
-          });
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
@@ -1,29 +1,40 @@
 <!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Audio Stream</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Audio Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Stop Video Audio Stream", bug: "822109" });
   /**
    * Run a test to verify that we can start a video+audio stream in a
    * media element, call stop() on the stream, and successfully get an
    * ended event fired.
    */
   runTest(function () {
-    getUserMedia({video: true, audio: true})
-      .then(stream => {
-        var testVideo = createMediaElement('video', 'testVideo');
-        var playback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true, audio: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
-        return playback.playMediaWithStreamStop(false);
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+      streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
@@ -1,39 +1,51 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video+Audio Stream With Followup Video+Audio</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({
-    title: "getUserMedia Stop Video+Audio Stream With Followup Video+Audio",
-    bug: "822109"
-  });
   /**
    * Run a test to verify that I can complete an video+audio gum playback in a
    * media element, stop the stream, and then complete another video+audio gum
    * playback in a media element.
    */
   runTest(function () {
-    getUserMedia({video: true, audio: true})
-      .then(stream => {
-        var testVideo = createMediaElement('video', 'testVideo');
-        var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true, audio: true}, function(firstStream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo, firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({video: true, audio: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
 
-        return streamPlayback.playMediaWithStreamStop(false)
-          .then(() => getUserMedia({video: true, audio: true}))
-          .then(secondStream => {
-            streamPlayback.mediaStream = secondStream;
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, generateErrorCallback());
 
-            return streamPlayback.playMedia(false)
-              .then(() => secondStream.stop());
-          });
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
@@ -1,29 +1,39 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Stream</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Stop Video Stream", bug: "822109" });
   /**
-   * Run a test to verify that we can start a video stream in a
-   * media element, call stop() on the stream, and successfully get an
-   * ended event fired.
+   * Run a test to verify that we can start a video stream in a media element,
+   * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
-    getUserMedia({video: true})
-      .then(stream => {
-        var testVideo = createMediaElement('video', 'testVideo');
-        var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true}, function(stream) {
+      var testVideo = document.getElementById('testVideo');
+      var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
-        return streamPlayback.playMediaWithStreamStop(false);
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+      videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
@@ -1,36 +1,52 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822109
+-->
 <head>
-  <script src="mediaStreamPlayback.js"></script>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Stop Video Stream With Followup Video</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
 </head>
 <body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=822109">mozGetUserMedia Stop Video Stream With Followup Video</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
 <pre id="test">
 <script type="application/javascript">
-  createHTML({ title: "getUserMedia Stop Video Stream With Followup Video", bug: "822109" });
   /**
-   * Run a test to verify that I can complete an video gum playback in a
-   * media element, stop the stream, and then complete another video gum
-   * playback in a media element.
+   * Run a test to verify that I can complete an audio gum playback in a media
+   * element, stop the stream, and then complete another audio gum playback
+   * in a media element.
    */
   runTest(function () {
-    getUserMedia({video: true})
-      .then(stream => {
-        var testVideo = createMediaElement('video', 'testVideo');
-        var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
+    getUserMedia({video: true}, function(firstStream) {
+      var testVideo = document.getElementById('testVideo');
+      var streamPlayback = new LocalMediaStreamPlayback(testVideo,
+        firstStream);
+
+      streamPlayback.playMediaWithStreamStop(false, function() {
+        getUserMedia({video: true}, function(secondStream) {
+          streamPlayback.mediaStream = secondStream;
 
-        return streamPlayback.playMediaWithStreamStop(false)
-          .then(() => getUserMedia({video: true}))
-          .then(secondStream => {
-            streamPlayback.mediaStream = secondStream;
+          streamPlayback.playMedia(false, function() {
+            secondStream.stop();
+            SimpleTest.finish();
+          }, generateErrorCallback());
 
-            return streamPlayback.playMedia(false)
-              .then(() => secondStream.stop());
-          });
-      })
-      .then(() => SimpleTest.finish(), generateErrorCallback());
+        }, generateErrorCallback());
+
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html
@@ -1,37 +1,45 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "addCandidate (answer) in 'have-local-offer'"
   });
 
   var test;
   runNetworkTest(function () {
     test = new PeerConnectionTest();
     test.setMediaConstraints([{audio: true}], [{audio: true}]);
     test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
 
-    test.chain.append([
-      function PC_LOCAL_ADD_CANDIDATE(test) {
-        var candidate = new mozRTCIceCandidate(
-          {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host",
-           sdpMLineIndex: 1});
-        return test.pcLocal._pc.addIceCandidate(candidate).then(
-          generateErrorCallback("addIceCandidate should have failed."),
-          err => {
+    test.chain.append([[
+      "PC_LOCAL_ADD_CANDIDATE",
+      function (test) {
+        test.pcLocal.addIceCandidateAndFail(
+          new mozRTCIceCandidate(
+            {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host",
+             sdpMLineIndex: 1}),
+          function(err) {
             is(err.name, "InvalidStateError", "Error is InvalidStateError");
-          });
-        }
-    ]);
+            test.next();
+          } );
+      }
+    ]]);
+
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html
+++ b/dom/media/tests/mochitest/test_peerConnection_addSecondAudioStream.html
@@ -1,59 +1,102 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1091242",
     title: "Renegotiation: add second audio stream"
   });
 
   var test;
   runNetworkTest(function (options) {
     test = new PeerConnectionTest(options);
     test.chain.append([
-      function PC_LOCAL_SETUP_NEGOTIATION_CALLBACK(test) {
+    [
+      'PC_LOCAL_SETUP_NEGOTIATION_CALLBACK',
+      function (test) {
         test.pcLocal.onNegotiationneededFired = false;
-        test.pcLocal._pc.onnegotiationneeded = anEvent => {
+        test.pcLocal._pc.onnegotiationneeded = function (anEvent) {
           info("pcLocal.onnegotiationneeded fired");
           test.pcLocal.onNegotiationneededFired = true;
         };
-      },
-      function PC_LOCAL_ADD_SECOND_STREAM(test) {
-        return test.pcLocal.getAllUserMedia([{audio: true}]);
-      },
-      function PC_LOCAL_CREATE_NEW_OFFER(test) {
+        test.next();
+      }
+    ],
+    [
+      'PC_LOCAL_ADD_SECOND_STREAM',
+      function (test) {
+        test.pcLocal.getAllUserMedia([{audio: true}], function () {
+          test.next();
+        });
+      }
+    ],
+    [
+      'PC_LOCAL_CREATE_NEW_OFFER',
+      function (test) {
         ok(test.pcLocal.onNegotiationneededFired, "onnegotiationneeded");
-        return test.createOffer(test.pcLocal).then(offer => {
+        test.createOffer(test.pcLocal, function (offer) {
           test._new_offer = offer;
+          test.next();
+        });
+      }
+    ],
+    [
+      'PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION',
+      function (test) {
+        test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER, function () {
+          test.next();
         });
-      },
-      function PC_LOCAL_SET_NEW_LOCAL_DESCRIPTION(test) {
-        return test.setLocalDescription(test.pcLocal, test._new_offer, HAVE_LOCAL_OFFER);
-      },
-      function PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION(test) {
-        return test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER);
-      },
-      function PC_REMOTE_CREATE_NEW_ANSWER(test) {
-        return test.createAnswer(test.pcRemote).then(answer => {
+      }
+    ],
+    [
+      'PC_REMOTE_SET_NEW_REMOTE_DESCRIPTION',
+      function (test) {
+        test.setRemoteDescription(test.pcRemote, test._new_offer, HAVE_REMOTE_OFFER, function () {
+          test.next();
+        });
+      }
+    ],
+    [
+      'PC_REMOTE_CREATE_NEW_ANSWER',
+      function (test) {
+        test.createAnswer(test.pcRemote, function (answer) {
           test._new_answer = answer;
+          test.next();
         });
-      },
-      function PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION(test) {
-        return test.setLocalDescription(test.pcRemote, test._new_answer, STABLE);
-      },
-      function PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION(test) {
-        return test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE);
+      }
+    ],
+    [
+      'PC_REMOTE_SET_NEW_LOCAL_DESCRIPTION',
+      function (test) {
+        test.setLocalDescription(test.pcRemote, test._new_answer, STABLE, function () {
+          test.next();
+        });
       }
-      // TODO(bug 1093835): figure out how to verify if media flows through the new stream
+    ],
+    [
+      'PC_LOCAL_SET_NEW_REMOTE_DESCRIPTION',
+      function (test) {
+        test.setRemoteDescription(test.pcLocal, test._new_answer, STABLE, function () {
+          test.next();
+        });
+      }
+    ]
+    // TODO(bug 1093835): figure out how to verify if media flows through the new stream
     ]);
     test.setMediaConstraints([{audio: true}], [{audio: true}]);
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796892",
     title: "Basic audio-only peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideo.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796890",
     title: "Basic audio/video (separate) peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796890",
     title: "Basic audio/video (combined) peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoCombined_long.html
@@ -1,37 +1,44 @@
 <!DOCTYPE HTML>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="long.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1014328",
     title: "Basic audio/video (combined) peer connection, long running",
     visible: true
   });
 
   var test;
-  runNetworkTest(function (options) {
+  runTest(function (options) {
     options = options || {};
     options.commands = commandsPeerConnection.slice(0);
     options.commands.push(generateIntervalCommand(verifyConnectionStatus,
                                                   1000 * 10,
                                                   1000 * 3600 * 3));
 
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{audio: true, video: true, fake: false}],
                              [{audio: true, video: true, fake: false}]);
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
+
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioVideoNoBundle.html
@@ -1,34 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1016476",
     title: "Basic audio/video peer connection with no Bundle"
   });
 
-  runNetworkTest(options => {
-    var test = new PeerConnectionTest(options);
-    test.chain.insertAfter(
-      'PC_LOCAL_CREATE_OFFER',
-      [
-        function PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER(test) {
-          test.originalOffer.sdp = test.originalOffer.sdp.replace(
-              /a=group:BUNDLE .*\r\n/g,
-            ""
-          );
-          info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
-        }
-      ]);
+  SimpleTest.requestFlakyTimeout("WebRTC is full of inherent timeouts");
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    test.chain.insertAfter('PC_LOCAL_CREATE_OFFER',
+    [['PC_LOCAL_REMOVE_BUNDLE_FROM_OFFER',
+      function (test) {
+        test.originalOffer.sdp = test.originalOffer.sdp.replace(
+          /a=group:BUNDLE .*\r\n/g,
+          ""
+        );
+        info("Updated no bundle offer: " + JSON.stringify(test.originalOffer));
+        test.next();
+      }
+    ]]);
     test.setMediaConstraints([{audio: true}, {video: true}],
                              [{audio: true}, {video: true}]);
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudio_long.html
@@ -1,18 +1,24 @@
 <!DOCTYPE HTML>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="long.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796892",
     title: "Basic audio-only peer connection",
     visible: true
--- a/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicH264Video.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1040346",
     title: "Basic H.264 GMP video-only peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1039666",
     title: "Basic screenshare-only peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796888",
     title: "Basic video-only peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicVideo_long.html
@@ -1,18 +1,24 @@
 <!DOCTYPE HTML>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="long.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796888",
     title: "Basic video-only peer connection",
     visible: true
--- a/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1038926",
     title: "Basic windowshare-only peer connection"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_bug1013809.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug1013809.html
@@ -1,12 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1013809",
     title: "Audio-only peer connection with swapped setLocal and setRemote steps"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_bug1042791.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug1042791.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1040346",
     title: "Basic H.264 GMP video-only peer connection"
   });
@@ -14,23 +20,25 @@
   var test;
   runNetworkTest(function (options) {
     options = options || { };
     options.h264 = true;
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{video: true}], [{video: true}]);
     test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
 
-    test.chain.append([
-      function PC_LOCAL_VERIFY_H264_OFFER(test) {
+    test.chain.append([[
+      "PC_LOCAL_VERIFY_H264_OFFER",
+      function (test) {
         ok(!test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=0x42e0"),
-           "H264 offer does not contain profile-level-id=0x42e0");
+          "H264 offer does not contain profile-level-id=0x42e0");
         ok(test.pcLocal._latest_offer.sdp.toLowerCase().contains("profile-level-id=42e0"),
-           "H264 offer contains profile-level-id=42e0");
+          "H264 offer contains profile-level-id=42e0");
+        test.next();
       }
-    ]);
+    ]]);
 
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug822674.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug822674.html
@@ -1,11 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "822674",
     title: "mozRTCPeerConnection isn't a true javascript object as it should be"
--- a/dom/media/tests/mochitest/test_peerConnection_bug825703.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug825703.html
@@ -1,80 +1,83 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "825703",
     title: "RTCConfiguration valid/invalid permutations"
   });
 
-var makePC = (config, expected_error) => {
-  var exception;
-  try {
-    new mozRTCPeerConnection(config).close();
-  } catch (e) {
-    exception = e;
-  }
-  is((exception? exception.name : "success"), expected_error || "success",
-     "mozRTCPeerConnection(" + JSON.stringify(config) + ")");
-};
-
-// This is a test of the iceServers parsing code + readable errors
-runNetworkTest(() => {
-  var exception = null;
-
-  try {
-    new mozRTCPeerConnection().close();
-  } catch (e) {
-    exception = e;
-  }
-  ok(!exception, "mozRTCPeerConnection() succeeds");
-  exception = null;
-
-  makePC();
-
-  makePC(1, "TypeError");
-
-  makePC({});
-
-  makePC({ iceServers: [] });
-
-  makePC({ iceServers: [{ urls:"" }] }, "SyntaxError");
-
-  makePC({ iceServers: [
-    { urls:"stun:127.0.0.1" },
-    { urls:"stun:localhost", foo:"" },
-    { urls: ["stun:127.0.0.1", "stun:localhost"] },
-    { urls:"stuns:localhost", foo:"" },
-    { urls:"turn:[::1]:3478", username:"p", credential:"p" },
-    { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" },
-    { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" },
-    { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" },
-    { url:"stun:localhost", foo:"" },
-    { url:"turn:localhost", username:"p", credential:"p" }
-  ]});
-
-  makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError");
-
-  makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError");
-
-  makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError");
-
-  makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError");
-
-  try {
-    new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close();
-  } catch (e) {
-    ok(e.message.indexOf("http") > 0,
-       "mozRTCPeerConnection() constructor has readable exceptions");
+  makePC = (config, expected_error) => {
+    var exception;
+    try {
+      new mozRTCPeerConnection(config).close();
+    } catch (e) {
+      exception = e;
+    }
+    is((exception? exception.name : "success"), expected_error || "success",
+       "mozRTCPeerConnection(" + JSON.stringify(config) + ")");
   }
 
-  networkTestFinished();
-});
+  // This is a test of the iceServers parsing code + readable errors
+
+  runNetworkTest(function () {
+    var exception = null;
+
+    try {
+      new mozRTCPeerConnection().close();
+    } catch (e) {
+      exception = e;
+    }
+    ok(!exception, "mozRTCPeerConnection() succeeds");
+    exception = null;
+
+    makePC();
+
+    makePC(1, "TypeError");
+
+    makePC({});
+
+    makePC({ iceServers: [] });
+
+    makePC({ iceServers: [{ urls:"" }] }, "SyntaxError");
+
+    makePC({ iceServers: [
+      { urls:"stun:127.0.0.1" },
+      { urls:"stun:localhost", foo:"" },
+      { urls: ["stun:127.0.0.1", "stun:localhost"] },
+      { urls:"stuns:localhost", foo:"" },
+      { urls:"turn:[::1]:3478", username:"p", credential:"p" },
+      { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" },
+      { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" },
+      { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" },
+      { url:"stun:localhost", foo:"" },
+      { url:"turn:localhost", username:"p", credential:"p" }
+    ]});
+
+    makePC({ iceServers: [{ urls: ["stun:127.0.0.1", ""] }] }, "SyntaxError");
+
+    makePC({ iceServers: [{ urls:"turns:localhost:3478", username:"p" }] }, "InvalidAccessError");
+
+    makePC({ iceServers: [{ url:"turns:localhost:3478", credential:"p" }] }, "InvalidAccessError");
+
+    makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError");
+    try {
+      new mozRTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close();
+    } catch (e) {
+      ok(e.message.indexOf("http") > 0,
+         "mozRTCPeerConnection() constructor has readable exceptions");
+    }
+
+    networkTestFinished();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug827843.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug827843.html
@@ -1,70 +1,72 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "827843",
     title: "Ensure that localDescription and remoteDescription are null after close"
   });
 
-var steps = [
-  function CHECK_SDP_ON_CLOSED_PC(test) {
-    var description;
-    var exception = null;
+  var steps = [
+    [
+      "CHECK_SDP_ON_CLOSED_PC",
+      function (test) {
+        var description;
+        var exception = null;
 
-    // handle the event which the close() triggers
-    var localClosed = new Promise(resolve => {
-      test.pcLocal.onsignalingstatechange = e => {
-        is(e.target.signalingState, "closed",
-           "Received expected onsignalingstatechange event on 'closed'");
-        resolve();
-      }
-    });
+        // handle the event which the close() triggers
+        test.pcLocal.onsignalingstatechange = function (e) {
+          is(e.target.signalingState, "closed",
+             "Received expected onsignalingstatechange event on 'closed'");
+        }
+
+        test.pcLocal.close();
 
-    test.pcLocal.close();
+        try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
+        ok(exception, "Attempt to access localDescription of pcLocal after close throws exception");
+        exception = null;
 
-    try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
-    ok(exception, "Attempt to access localDescription of pcLocal after close throws exception");
-    exception = null;
-
-    try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; }
-    ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
-    exception = null;
+        try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; }
+        ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
+        exception = null;
 
-    // handle the event which the close() triggers
-    var remoteClosed = new Promise(resolve => {
-      test.pcRemote.onsignalingstatechange = e => {
-        is(e.target.signalingState, "closed",
-           "Received expected onsignalingstatechange event on 'closed'");
-        resolve();
-      }
-    });
+        // handle the event which the close() triggers
+        test.pcRemote.onsignalingstatechange = function (e) {
+          is(e.target.signalingState, "closed",
+             "Received expected onsignalingstatechange event on 'closed'");
+        }
 
-    test.pcRemote.close();
+        test.pcRemote.close();
 
-    try  { description = test.pcRemote.localDescription; } catch (e) { exception = e; }
-    ok(exception, "Attempt to access localDescription of pcRemote after close throws exception");
-    exception = null;
+        try  { description = test.pcRemote.localDescription; } catch (e) { exception = e; }
+        ok(exception, "Attempt to access localDescription of pcRemote after close throws exception");
+        exception = null;
 
-    try  { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; }
-    ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception");
+        try  { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; }
+        ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception");
 
-    return Promise.all([localClosed, remoteClosed]);
-  }
-];
+        test.next();
+      }
+    ]
+  ];
 
-var test;
-runNetworkTest(() => {
-  test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.append(steps);
-  test.run();
-});
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.append(steps);
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug834153.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug834153.html
@@ -1,11 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "834153",
     title: "Queue CreateAnswer in PeerConnection.js"
deleted file mode 100644
--- a/dom/media/tests/mochitest/test_peerConnection_callbacks.html
+++ /dev/null
@@ -1,92 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <script type="application/javascript" src="pc.js"></script>
-</head>
-<body>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-  createHTML({
-    title: "PeerConnection using callback functions",
-    bug: "1119593",
-    visible: true
-  });
-
-// This still aggressively uses promises, but it is testing that the callback functions
-// are properly in place.
-
-// wrapper that turns a callback-based function call into a promise
-function pcall(o, f, beforeArg) {
-  return new Promise((resolve, reject) => {
-    var args = [resolve, reject];
-    if (typeof beforeArg !== 'undefined') {
-      args.unshift(beforeArg);
-    }
-    info('Calling ' + f.name);
-    f.apply(o, args);
-  });
-}
-
-var pc1 = new mozRTCPeerConnection();
-var pc2 = new mozRTCPeerConnection();
-
-var pc2_haveRemoteOffer = new Promise(resolve => {
-  pc2.onsignalingstatechange =
-    e => (e.target.signalingState == "have-remote-offer") && resolve();
-});
-var pc1_stable = new Promise(resolve => {
-  pc1.onsignalingstatechange =
-    e => (e.target.signalingState == "stable") && resolve();
-});
-
-pc1.onicecandidate = e => {
-  pc2_haveRemoteOffer
-    .then(() => !e.candidate || pcall(pc2, pc2.addIceCandidate, e.candidate))
-    .catch(generateErrorCallback());
-};
-pc2.onicecandidate = e => {
-  pc1_stable
-    .then(() => !e.candidate || pcall(pc1, pc1.addIceCandidate, e.candidate))
-    .catch(generateErrorCallback());
-};
-
-var v1, v2;
-var delivered = new Promise(resolve => {
-  pc2.onaddstream = e => {
-    v2.mozSrcObject = e.stream;
-    resolve(e.stream);
-  };
-});
-
-runNetworkTest(function() {
-  v1 = createMediaElement('video', 'v1');
-  v2 = createMediaElement('video', 'v2');
-  var canPlayThrough = new Promise(resolve => v2.canplaythrough = resolve);
-  is(v2.currentTime, 0, "v2.currentTime is zero at outset");
-
-  // not testing legacy gUM here
-  navigator.mediaDevices.getUserMedia({ fake: true, video: true, audio: true })
-    .then(stream => pc1.addStream(v1.mozSrcObject = stream))
-    .then(() => pcall(pc1, pc1.createOffer))
-    .then(offer => pcall(pc1, pc1.setLocalDescription, offer))
-    .then(() => pcall(pc2, pc2.setRemoteDescription, pc1.localDescription))
-    .then(() => pcall(pc2, pc2.createAnswer))
-    .then(answer => pcall(pc2, pc2.setLocalDescription, answer))
-    .then(() => pcall(pc1, pc1.setRemoteDescription, pc2.localDescription))
-    .then(() => delivered)
-  //    .then(() => canPlayThrough)    // why doesn't this fire?
-    .then(() => waitUntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0))
-    .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")"))
-    .then(() => ok(true, "Connected."))
-    .then(() => pcall(pc1, pc1.getStats, null))
-    .then(stats => ok(Object.keys(stats).length > 0, "pc1 has stats"))
-    .then(() => pcall(pc2, pc2.getStats, null))
-    .then(stats => ok(Object.keys(stats).length > 0, "pc2 has stats"))
-    .then(() => { v1.pause(); v2.pause(); })
-    .catch(reason => ok(false, "unexpected failure: " + reason))
-      .then(networkTestFinished);
-});
-</script>
-</pre>
-</body>
-</html>
--- a/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_capturedVideo.html
@@ -1,45 +1,55 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <video id="v1" src="../../test/vp9cake.webm" height="120" width="160" autoplay muted></video>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1081409",
     title: "Captured video-only over peer connection",
     visible: true
   });
 
-var metadataLoaded = new Promise(resolve => {
-  if (v1.readyState < v1.HAVE_METADATA) {
-    v1.onloadedmetadata = resolve;
-  } else {
+  var metadataLoaded = new Promise(resolve => {
+    if (v1.readyState < v1.HAVE_METADATA) {
+      v1.onloadedmetadata = e => resolve();
+      return;
+    }
     resolve();
-  }
-});
+  });
 
-runNetworkTest(function() {
-  var test = new PeerConnectionTest();
-  test.setOfferOptions({ offerToReceiveVideo: false,
-                         offerToReceiveAudio: false });
-  test.chain.insertAfter("PC_LOCAL_GUM", [
-    function PC_LOCAL_CAPTUREVIDEO(test) {
-      return metadataLoaded
-        .then(() => {
-          var stream = v1.mozCaptureStreamUntilEnded();
-          is(stream.getTracks().length, 2, "Captured stream has 2 tracks");
-          stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream));
-          test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests
-        });
+  runNetworkTest(function() {
+    var test = new PeerConnectionTest();
+    test.setOfferOptions({ offerToReceiveVideo: false,
+                           offerToReceiveAudio: false });
+    test.chain.insertAfter("PC_LOCAL_GUM", [["PC_LOCAL_CAPTUREVIDEO", function (test) {
+      metadataLoaded
+      .then(function() {
+        var stream = v1.mozCaptureStreamUntilEnded();
+        is(stream.getTracks().length, 2, "Captured stream has 2 tracks");
+        stream.getTracks().forEach(tr => test.pcLocal._pc.addTrack(tr, stream));
+        test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests
+        test.next();
+      })
+      .catch(function(reason) {
+        ok(false, "unexpected failure: " + reason);
+        SimpleTest.finish();
+      });
     }
-  ]);
-  test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
-  test.run();
-});
+    ]]);
+    test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_close.html
+++ b/dom/media/tests/mochitest/test_peerConnection_close.html
@@ -1,11 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "991877",
     title: "Basic RTCPeerConnection.close() tests"
--- a/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
@@ -1,11 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "834270",
     title: "Align PeerConnection error handling with WebRTC specification"
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleAnswer.html
@@ -1,13 +1,19 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="nonTrickleIce.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1060102",
     title: "Basic audio only SDP answer without trickle ICE"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleOffer.html
@@ -1,13 +1,19 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="nonTrickleIce.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1060102",
     title: "Basic audio only SDP offer without trickle ICE"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_noTrickleOfferAnswer.html
@@ -1,13 +1,19 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="nonTrickleIce.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1060102",
     title: "Basic audio only SDP offer and answer without trickle ICE"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html
+++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveAudio.html
@@ -1,12 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "850275",
     title: "Simple offer media constraint test with audio"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
+++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideo.html
@@ -1,12 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "850275",
     title: "Simple offer media constraint test with video"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html
+++ b/dom/media/tests/mochitest/test_peerConnection_offerRequiresReceiveVideoAudio.html
@@ -1,12 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "850275",
     title: "Simple offer media constraint test with video/audio"
   });
--- a/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
+++ b/dom/media/tests/mochitest/test_peerConnection_promiseSendOnly.html
@@ -1,57 +1,62 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
+<video id="v1" controls="controls" height="120" width="160" autoplay></video>
+<video id="v2" controls="controls" height="120" width="160" autoplay></video><br>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1091898",
     title: "PeerConnection with promises (sendonly)",
     visible: true
   });
 
+  var waituntil = func => new Promise(resolve => {
+    var inter = setInterval(() => func() && resolve(clearInterval(inter)), 200);
+  });
+
   var pc1 = new mozRTCPeerConnection();
   var pc2 = new mozRTCPeerConnection();
 
   var pc2_haveRemoteOffer = new Promise(resolve => pc2.onsignalingstatechange =
     e => (e.target.signalingState == "have-remote-offer") && resolve());
   var pc1_stable = new Promise(resolve => pc1.onsignalingstatechange =
     e => (e.target.signalingState == "stable") && resolve());
 
   pc1.onicecandidate = e => pc2_haveRemoteOffer.then(() => !e.candidate ||
     pc2.addIceCandidate(e.candidate)).catch(generateErrorCallback());
   pc2.onicecandidate = e => pc1_stable.then(() => !e.candidate ||
     pc1.addIceCandidate(e.candidate)).catch(generateErrorCallback());
 
-  var v1, v2;
   var delivered = new Promise(resolve =>
     pc2.onaddstream = e => resolve(v2.mozSrcObject = e.stream));
+  var canPlayThrough = new Promise(resolve => v2.canplaythrough = e => resolve());
 
   runNetworkTest(function() {
-    v1 = createMediaElement('video', 'v1');
-    v2 = createMediaElement('video', 'v2');
-    var canPlayThrough = new Promise(resolve => v2.canplaythrough = e => resolve());
-
     is(v2.currentTime, 0, "v2.currentTime is zero at outset");
 
     navigator.mediaDevices.getUserMedia({ fake: true, video: true, audio: true })
     .then(stream => pc1.addStream(v1.mozSrcObject = stream))
     .then(() => pc1.createOffer())
     .then(offer => pc1.setLocalDescription(offer))
     .then(() => pc2.setRemoteDescription(pc1.localDescription))
     .then(() => pc2.createAnswer())
     .then(answer => pc2.setLocalDescription(answer))
     .then(() => pc1.setRemoteDescription(pc2.localDescription))
     .then(() => delivered)
 //    .then(() => canPlayThrough)    // why doesn't this fire?
-    .then(() => waitUntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0))
+    .then(() => waituntil(() => v2.currentTime > 0 && v2.mozSrcObject.currentTime > 0))
     .then(() => ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")"))
     .then(() => ok(true, "Connected."))
     .catch(reason => ok(false, "unexpected failure: " + reason))
     .then(networkTestFinished);
   });
 </script>
 </pre>
 </body>
--- a/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html
+++ b/dom/media/tests/mochitest/test_peerConnection_replaceTrack.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
   createHTML({
     bug: "1032839",
     title: "Replace video track",
     visible: true
@@ -20,33 +26,37 @@
 
   var test;
   runNetworkTest(function () {
     test = new PeerConnectionTest();
     test.setMediaConstraints([{video: true}], [{video: true}]);
     test.chain.removeAfter("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
     var flowtest = test.chain.remove("PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT");
     test.chain.append(flowtest);
-    test.chain.append([
-      function PC_LOCAL_REPLACE_VIDEOTRACK(test) {
+    test.chain.append([["PC_LOCAL_REPLACE_VIDEOTRACK",
+      function (test) {
         var stream = test.pcLocal._pc.getLocalStreams()[0];
         var track = stream.getVideoTracks()[0];
         var sender = test.pcLocal._pc.getSenders().find(isSenderOfTrack, track);
         ok(sender, "track has a sender");
         var newtrack;
-        return navigator.mediaDevices.getUserMedia({video:true, fake: true})
-          .then(newStream => {
-            newtrack = newStream.getVideoTracks()[0];
-            return sender.replaceTrack(newtrack);
-          })
-          .then(() => {
-            is(sender.track, newtrack, "sender.track has been replaced");
-          });
+        navigator.mediaDevices.getUserMedia({video:true, fake: true})
+        .then(function(newStream) {
+          newtrack = newStream.getVideoTracks()[0];
+          return sender.replaceTrack(newtrack);
+        })
+        .then(function() {
+          is(sender.track, newtrack, "sender.track has been replaced");
+        })
+        .catch(function(reason) {
+          ok(false, "unexpected error = " + reason.message);
+        })
+        .then(test.next.bind(test));
       }
-    ]);
+    ]]);
     test.chain.append(flowtest);
 
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html
@@ -1,34 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "setLocalDescription (answer) in 'have-local-offer'"
   });
 
-runNetworkTest(function () {
-  var test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
 
-  test.chain.append([
-    function PC_LOCAL_SET_LOCAL_ANSWER(test) {
-      test.pcLocal._latest_offer.type = "answer";
-      return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
-        .then(err => {
-          is(err.name, "InvalidStateError", "Error is InvalidStateError");
-        });
-    }
-  ]);
+    test.chain.append([[
+      "PC_LOCAL_SET_LOCAL_ANSWER",
+      function (test) {
+        test.pcLocal._latest_offer.type="answer";
+        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
+          function(err) {
+            is(err.name, "InvalidStateError", "Error is InvalidStateError");
+            test.next();
+          } );
+      }
+    ]]);
 
-  test.run();
-});
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html
@@ -1,34 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "setLocalDescription (answer) in 'stable'"
   });
 
-runNetworkTest(function () {
-  var test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
 
-  test.chain.append([
-    function PC_LOCAL_SET_LOCAL_ANSWER(test) {
-      test.pcLocal._latest_offer.type = "answer";
-      return test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
-        .then(err => {
-          is(err.name, "InvalidStateError", "Error is InvalidStateError");
-        });
-    }
-  ]);
+    test.chain.append([[
+      "PC_LOCAL_SET_LOCAL_ANSWER",
+      function (test) {
+        test.pcLocal._latest_offer.type="answer";
+        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
+          function(err) {
+            is(err.name, "InvalidStateError", "Error is InvalidStateError");
+            test.next();
+          } );
+      }
+    ]]);
 
-  test.run();
-});
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html
@@ -1,33 +1,42 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "setLocalDescription (offer) in 'have-remote-offer'"
   });
 
-runNetworkTest(function () {
-  var test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
 
-  test.chain.append([
-    function PC_REMOTE_SET_LOCAL_OFFER(test) {
-      test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer)
-        .then(err => {
-          is(err.name, "InvalidStateError", "Error is InvalidStateError");
-        });
-    }
-  ]);
+    test.chain.append([[
+      "PC_REMOTE_SET_LOCAL_OFFER",
+      function (test) {
+        test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._latest_offer,
+          function(err) {
+            is(err.name, "InvalidStateError", "Error is InvalidStateError");
+            test.next();
+          } );
+      }
+    ]]);
 
-  test.run();
-});
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html
+++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html
@@ -1,34 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "setRemoteDescription (answer) in 'have-remote-offer'"
   });
 
-runNetworkTest(function () {
-  var test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
 
-  test.chain.append([
-    function PC_REMOTE_SET_REMOTE_ANSWER(test) {
-      test.pcLocal._latest_offer.type = "answer";
-      test.pcRemote._pc.setRemoteDescription(test.pcLocal._latest_offer)
-        .then(generateErrorCallback('setRemoteDescription should fail'),
-              err =>
-              is(err.name, "InvalidStateError", "Error is InvalidStateError"));
-    }
-  ]);
+    test.chain.append([[
+      "PC_REMOTE_SET_REMOTE_ANSWER",
+      function (test) {
+        test.pcLocal._latest_offer.type="answer";
+        test.pcRemote.setRemoteDescriptionAndFail(test.pcLocal._latest_offer,
+          function(err) {
+            is(err.name, "InvalidStateError", "Error is InvalidStateError");
+            test.next();
+          } );
+      }
+    ]]);
 
-  test.run();
-});
+    test.run();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html
+++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html
@@ -1,34 +1,43 @@
 <!DOCTYPE HTML>
 <html>
 <head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
   <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "784519",
     title: "setRemoteDescription (answer) in 'stable'"
   });
 
-runNetworkTest(function () {
-  var test = new PeerConnectionTest();
-  test.setMediaConstraints([{audio: true}], [{audio: true}]);
-  test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
+  var test;
+  runNetworkTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
 
-  test.chain.append([
-    function PC_LOCAL_SET_REMOTE_ANSWER(test) {
-      test.pcLocal.