Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 01 Dec 2016 15:31:47 -0800
changeset 325034 b49684127ce464141b0a989cd621cb4794b6a85f
parent 324947 9b8bf5feb0b52aa4b03aa5fa3d4f0727b2974663 (current diff)
parent 325033 083ffc01652a13e80d12288074fd5dbc39621e93 (diff)
child 325035 6f39c69810f258b4108f8ee88048c5b690a503a2
child 325055 4dc3204e5c26b377ab17fe9dd2845408361fecc1
child 325085 ca40b6ff416d2aa61a7a89dad46432daab3f1856
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
Merge inbound to central, a=merge
dom/base/BlobSet.cpp
dom/base/BlobSet.h
dom/base/File.cpp
dom/base/File.h
dom/base/FileList.cpp
dom/base/FileList.h
dom/base/FileReader.cpp
dom/base/FileReader.h
dom/base/MultipartBlobImpl.cpp
dom/base/MultipartBlobImpl.h
dom/base/MutableBlobStorage.cpp
dom/base/MutableBlobStorage.h
dom/base/MutableBlobStreamListener.cpp
dom/base/MutableBlobStreamListener.h
dom/base/nsHostObjectProtocolHandler.cpp
dom/base/nsHostObjectProtocolHandler.h
dom/base/nsHostObjectURI.cpp
dom/base/nsHostObjectURI.h
dom/base/nsIDOMBlob.idl
dom/base/nsIDOMFileList.idl
dom/base/test/create_file_objects.js
dom/base/test/file_blobURL_expiring.html
dom/base/test/file_mozfiledataurl_audio.ogg
dom/base/test/file_mozfiledataurl_doc.html
dom/base/test/file_mozfiledataurl_inner.html
dom/base/test/file_mozfiledataurl_text.txt
dom/base/test/file_nonascii_blob_url.html
dom/base/test/fileapi_chromeScript.js
dom/base/test/fileutils.js
dom/base/test/test_blobURL_expiring.html
dom/base/test/test_blob_fragment_and_query.html
dom/base/test/test_blobconstructor.html
dom/base/test/test_file_from_blob.html
dom/base/test/test_file_negative_date.html
dom/base/test/test_fileapi.html
dom/base/test/test_fileapi_slice.html
dom/base/test/test_ipc_messagemanager_blob.html
dom/base/test/test_mozfiledataurl.html
dom/base/test/test_nonascii_blob_url.html
dom/html/HTMLMediaElement.cpp
dom/ipc/Blob.cpp
dom/ipc/BlobChild.h
dom/ipc/BlobParent.h
dom/ipc/BlobTypes.ipdlh
dom/ipc/PBlob.ipdl
dom/ipc/PBlobStream.ipdl
dom/ipc/nsIRemoteBlob.h
gfx/2d/PathCG.cpp
gfx/2d/PathCG.h
modules/libpref/init/all.js
parser/html/nsHtml5StreamParser.cpp
testing/web-platform/meta/FileAPI/file/File-constructor.html.ini
testing/web-platform/meta/FileAPI/historical.html.ini
--- a/addon-sdk/source/lib/sdk/content/page-mod.js
+++ b/addon-sdk/source/lib/sdk/content/page-mod.js
@@ -47,17 +47,17 @@ const ChildPageMod = Class({
   ],
   setup: function PageMod(model) {
     merge(this, model);
 
     // Set listeners on {PageMod} itself, not the underlying worker,
     // like `onMessage`, as it'll get piped.
     setListeners(this, model);
 
-    function deserializeRules(rules) {
+    function* deserializeRules(rules) {
       for (let rule of rules) {
         yield rule.type == "string" ? rule.value
                                     : new RegExp(rule.pattern, rule.flags);
       }
     }
 
     let include = [...deserializeRules(this.include)];
     this.include = Rules();
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -113,17 +113,17 @@ const PageMod = Class({
 
     // Set listeners on {PageMod} itself, not the underlying worker,
     // like `onMessage`, as it'll get piped.
     setListeners(this, options);
 
     pagemods.set(model.id, this);
     workers.set(this, []);
 
-    function serializeRules(rules) {
+    function* serializeRules(rules) {
       for (let rule of rules) {
         yield isRegExp(rule) ? { type: "regexp", pattern: rule.source, flags: rule.flags }
                              : { type: "string", value: rule };
       }
     }
 
     model.childOptions = omit(model, ["include", "exclude", "contentScriptOptions"]);
     model.childOptions.include = [...serializeRules(model.include)];
--- a/addon-sdk/source/test/test-addon-bootstrap.js
+++ b/addon-sdk/source/test/test-addon-bootstrap.js
@@ -16,17 +16,17 @@ const BOOTSTRAP_REASONS = {
   ADDON_ENABLE    : 3,
   ADDON_DISABLE   : 4,
   ADDON_INSTALL   : 5,
   ADDON_UNINSTALL : 6,
   ADDON_UPGRADE   : 7,
   ADDON_DOWNGRADE : 8
 };
 
-exports["test install/startup/shutdown/uninstall all return a promise"] = function(assert) {
+exports["test install/startup/shutdown/uninstall all return a promise"] = function*(assert) {
   let uri = require.resolve("./fixtures/addon/bootstrap.js");
   let id = "test-min-boot@jetpack";
   let bootstrapScope = create({
     id: id,
     uri: uri
   });
 
   // As we don't want our caller to control the JS version used for the
--- a/addon-sdk/source/test/test-context-menu@2.js
+++ b/addon-sdk/source/test/test-context-menu@2.js
@@ -411,17 +411,17 @@ exports["test iframe context"] = withTab
     iframe: new Item({
       label: "iframe",
       context: [new Contexts.Frame()]
     }),
     h2: new Item({
       label: "element",
       context: [new Contexts.Selector("*")]
     })
-  }, function(_) {
+  }, function*(_) {
     assert.deepEqual((yield captureContextMenu("iframe")),
                      menugroup(menuseparator(),
                                menuitem({label: "page"}),
                                menuitem({label: "iframe"}),
                                menuitem({label: "element"})),
                      "matching items are present");
 
     assert.deepEqual((yield captureContextMenu("h1")),
--- a/addon-sdk/source/test/test-event-core.js
+++ b/addon-sdk/source/test/test-event-core.js
@@ -258,17 +258,17 @@ exports['test listen to all events'] = f
   assert.deepEqual(actual[1], ['foo', 'hello'],
     'wildcard listener called');
 
   emit(target, 'bar', 'goodbye');
   assert.deepEqual(actual[2], ['bar', 'goodbye'],
     'wildcard listener called for unbound event name');
 };
 
-exports['test once'] = function(assert, done) {
+exports['test once'] = function*(assert, done) {
   let target = {};
   let called = false;
   let { resolve, promise } = defer();
 
   once(target, 'foo', function(value) {
     assert.ok(!called, "listener called only once");
     assert.equal(value, "bar", "correct argument was passed");
   });
--- a/addon-sdk/source/test/test-event-dom.js
+++ b/addon-sdk/source/test/test-event-dom.js
@@ -10,17 +10,17 @@ const { Cc, Ci } = require('chrome');
 const els = Cc["@mozilla.org/eventlistenerservice;1"].
             getService(Ci.nsIEventListenerService);
 
 function countListeners(target, type) {
   let listeners = els.getListenerInfoFor(target, {});
   return listeners.filter(listener => listener.type == type).length;
 }
 
-exports['test window close clears listeners'] = function(assert) {
+exports['test window close clears listeners'] = function*(assert) {
   let window = yield openWindow();
   let loader = Loader(module);
 
   // Any element will do here
   let gBrowser = window.gBrowser;
 
   // Other parts of the app may be listening for this
   let windowListeners = countListeners(window, "DOMWindowClose");
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -145,16 +145,17 @@
 @RESPATH@/components/cookie.xpt
 @RESPATH@/components/directory.xpt
 @RESPATH@/components/diskspacewatcher.xpt
 @RESPATH@/components/docshell.xpt
 @RESPATH@/components/dom.xpt
 @RESPATH@/components/dom_activities.xpt
 @RESPATH@/components/dom_audiochannel.xpt
 @RESPATH@/components/dom_base.xpt
+@RESPATH@/components/dom_file.xpt
 @RESPATH@/components/dom_system.xpt
 @RESPATH@/components/dom_workers.xpt
 #ifdef MOZ_WIDGET_GONK
 @RESPATH@/components/dom_wifi.xpt
 @RESPATH@/components/dom_system_gonk.xpt
 #endif
 @RESPATH@/components/dom_canvas.xpt
 @RESPATH@/components/dom_core.xpt
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -150,16 +150,23 @@ this.DownloadsViewUI.DownloadElementShel
     // The progress bar is only displayed for in-progress downloads.
     if (this.download.hasProgress) {
       this.element.setAttribute("progressmode", "normal");
       this.element.setAttribute("progress", this.download.progress);
     } else {
       this.element.setAttribute("progressmode", "undetermined");
     }
 
+    if (this.download.stopped && this.download.canceled &&
+        this.download.hasPartialData) {
+      this.element.setAttribute("progresspaused", "true");
+    } else {
+      this.element.removeAttribute("progresspaused");
+    }
+
     // Dispatch the ValueChange event for accessibility, if possible.
     if (this._progressElement) {
       let event = this.element.ownerDocument.createEvent("Events");
       event.initEvent("ValueChange", true, true);
       this._progressElement.dispatchEvent(event);
     }
 
     let status = this.statusTextAndTip;
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -40,17 +40,17 @@
           <xul:description class="downloadTarget"
                            crop="center"
                            style="min-width: &downloadsSummary.minWidth2;"
                            xbl:inherits="value=displayName,tooltiptext=displayName"/>
           <xul:progressmeter anonid="progressmeter"
                              class="downloadProgress"
                              min="0"
                              max="100"
-                             xbl:inherits="mode=progressmode,value=progress"/>
+                             xbl:inherits="mode=progressmode,value=progress,paused=progresspaused"/>
           <xul:description class="downloadDetails"
                            crop="end"
                            xbl:inherits="value=status,tooltiptext=statusTip"/>
         </xul:vbox>
       </xul:hbox>
       <xul:toolbarseparator />
       <xul:stack class="downloadButtonArea">
         <xul:button class="downloadButton downloadCancel downloadIconCancel"
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -39,19 +39,25 @@ support-files =
   test2.html
   test2.js
   test2.js^headers^
   test_firstParty.html
   test_firstParty_cookie.html
   test_firstParty_html_redirect.html
   test_firstParty_http_redirect.html
   test_firstParty_http_redirect.html^headers^
+  test_firstParty_http_redirect_to_same_domain.html
+  test_firstParty_http_redirect_to_same_domain.html^headers^
   test_firstParty_iframe_http_redirect.html
   test_firstParty_postMessage.html
+  test_form.html
   window.html
+  window2.html
+  window3.html
+  window_redirect.html
   worker_blobify.js
   worker_deblobify.js
 
 [browser_broadcastChannel.js]
 [browser_cache.js]
 [browser_cookieIsolation.js]
 [browser_favicon_firstParty.js]
 [browser_favicon_userContextId.js]
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
@@ -167,8 +167,113 @@ add_task(function* openWindow_test() {
     Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
                  attrs.firstPartyDomain, "iframe should have firstPartyDomain");
   });
 
   gBrowser.removeTab(tab);
   yield BrowserTestUtils.closeWindow(win);
 });
 
+/**
+ * When the web page calls window.open, the top-level docshell in the new
+ * created window will have firstPartyDomain set.
+ */
+add_task(function* window_open_redirect_test() {
+  Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.link.open_newwindow");
+  });
+
+  let tab = gBrowser.addTab(BASE_URL + "window_redirect.html");
+  let win = yield BrowserTestUtils.waitForNewWindow();
+  yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
+
+  yield ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
+                 "window.open() should have firstPartyDomain attribute");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+  });
+
+  gBrowser.removeTab(tab);
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * When the web page calls window.open, the top-level docshell in the new
+ * created window will inherit the firstPartyDomain attribute.
+ * However the top-level document will override the firstPartyDomain if the
+ * document is from another domain.
+ */
+add_task(function* window_open_iframe_test() {
+  Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.link.open_newwindow");
+  });
+
+  let tab = gBrowser.addTab(BASE_URL + "window2.html");
+  let win = yield BrowserTestUtils.waitForNewWindow();
+  yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser, true);
+
+  yield ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
+                 "window.open() should have firstPartyDomain attribute");
+
+    // The document is http://example.com/browser/browser/components/originattributes/test/browser/test_firstParty.html
+    // so the firstPartyDomain will be overriden to 'example.com'.
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 "example.com", "The document should have firstPartyDomain");
+
+    let iframe = content.document.getElementById("iframe1");
+    Assert.equal(iframe.frameLoader.docShell.getOriginAttributes().firstPartyDomain,
+                 "example.com", "iframe's docshell should have firstPartyDomain");
+    Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
+                 "example.com", "iframe should have firstPartyDomain");
+  });
+
+  gBrowser.removeTab(tab);
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * Test for the loadInfo->TriggeringPrincipal is the document itself.
+ */
+add_task(function* form_test() {
+  let tab = gBrowser.addTab(BASE_URL + "test_form.html");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+  yield ContentTask.spawn(tab.linkedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+
+    let submit = content.document.getElementById("submit");
+    submit.click();
+  });
+
+  gBrowser.removeTab(tab);
+});
+
+/**
+ * Another test for loadInfo->TriggeringPrincipal in the window.open case.
+ */
+add_task(function* window_open_form_test() {
+  Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.link.open_newwindow");
+  });
+
+  let tab = gBrowser.addTab(BASE_URL + "window3.html");
+  let win = yield BrowserTestUtils.waitForNewWindow();
+  yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser, true);
+
+  yield ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
+                 "window.open() should have firstPartyDomain attribute");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 "example.com", "The document should have firstPartyDomain");
+
+    let submit = content.document.getElementById("submit");
+    submit.click();
+  });
+
+  gBrowser.removeTab(tab);
+  yield BrowserTestUtils.closeWindow(win);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_http_redirect_to_same_domain.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>Test for Bug 1260931</title>
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_http_redirect_to_same_domain.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Found
+Location: http://mochi.test:8888/browser/browser/components/originattributes/test/browser/dummy.html
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_form.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1260931</title>
+</head>
+<body>
+<form action="test_firstParty_http_redirect_to_same_domain.html" method="POST">
+   First name: <input type="text" name="fname"><br>
+   Last name: <input type="text" name="lname"><br>
+   <input type="submit" id="submit" value="Submit">
+</form>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/window2.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <meta charset="utf8">
+    <title>Page creating a popup</title>
+  </head>
+  <body>
+    <script type="text/javascript">
+       var w = window.open("http://example.com/browser/browser/components/originattributes/test/browser/test_firstParty.html", "test");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/window3.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <meta charset="utf8">
+    <title>Page creating a popup</title>
+  </head>
+  <body>
+    <script type="text/javascript">
+       var w = window.open("http://example.com/browser/browser/components/originattributes/test/browser/test_form.html", "test");
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/window_redirect.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+  <head>
+    <meta charset="utf8">
+    <title>Page creating a popup</title>
+  </head>
+  <body>
+    <script type="text/javascript">
+       var w = window.open('test_firstParty_http_redirect_to_same_domain.html', "test");
+    </script>
+  </body>
+</html>
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -185,16 +185,17 @@
 @RESPATH@/components/content_webrtc.xpt
 #endif
 @RESPATH@/components/content_xslt.xpt
 @RESPATH@/components/cookie.xpt
 @RESPATH@/components/directory.xpt
 @RESPATH@/components/docshell.xpt
 @RESPATH@/components/dom.xpt
 @RESPATH@/components/dom_base.xpt
+@RESPATH@/components/dom_file.xpt
 @RESPATH@/components/dom_system.xpt
 @RESPATH@/components/dom_canvas.xpt
 @RESPATH@/components/dom_core.xpt
 @RESPATH@/components/dom_css.xpt
 @RESPATH@/components/dom_events.xpt
 @RESPATH@/components/dom_geolocation.xpt
 @RESPATH@/components/dom_media.xpt
 @RESPATH@/components/dom_network.xpt
--- a/browser/themes/osx/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/osx/downloads/allDownloadsViewOverlay.css
@@ -4,8 +4,16 @@
 
 %include ../../shared/downloads/allDownloadsViewOverlay.inc.css
 
 /*** List items ***/
 
 :root {
   --downloads-item-height: 6em;
 }
+
+.downloadProgress > .progress-bar {
+  background-color: #3c9af8;
+}
+
+.downloadProgress[paused="true"] > .progress-bar {
+  background-color: #a6a6a6;
+}
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -30,14 +30,22 @@
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
 @item@[verdict="Malware"] {
   color: #aa1b08;
 }
 
+.downloadProgress > .progress-bar {
+  background-color: #3c9af8;
+}
+
+.downloadProgress[paused="true"] > .progress-bar {
+  background-color: #a6a6a6;
+}
+
 /*** Highlighted list items ***/
 
 @keyfocus@ @itemFocused@ {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
--- a/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
+++ b/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
@@ -66,23 +66,21 @@
 }
 
 @item@ > toolbarseparator {
   display: none;
 }
 
 .downloadTarget {
   margin-bottom: 3px;
-  cursor: inherit;
 }
 
 .downloadDetails {
   opacity: 0.7;
   font-size: 95%;
-  cursor: inherit;
 }
 
 .downloadButton {
   -moz-appearance: none;
   -moz-box-align: center;
   background: transparent;
   min-width: 0;
   min-height: 0;
@@ -141,8 +139,11 @@
 %else
   list-style-image: url("chrome://browser/skin/panel-icons.svg#folder");
 %endif
 }
 
 .downloadIconRetry > .button-box > .button-icon {
   list-style-image: url("chrome://browser/skin/panel-icons.svg#retry");
 }
+
+/*** Progressmeter ***/
+%include progressmeter.inc.css
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -146,17 +146,16 @@ toolbarseparator.downloadsDropmarkerSpli
 /* Override default icon size which is too small for this dropdown */
 .downloadsDropmarker > .button-box > .button-menu-dropmarker {
   width: 16px;
   height: 16px;
 }
 
 #downloadsSummary {
   padding: 0 12px;
-  cursor: pointer;
   -moz-user-focus: normal;
 }
 
 #downloadsSummary > .downloadTypeIcon {
   list-style-image: url("chrome://browser/skin/downloads/download-summary.svg");
 }
 
 #downloadsSummaryDescription {
@@ -230,28 +229,26 @@ richlistitem[type="download"] > .downloa
 #downloadsSummaryDetails,
 .downloadContainer {
   font-size: calc(100% * var(--downloads-item-font-size-factor));
 }
 
 #downloadsSummaryDescription,
 .downloadTarget {
   margin-bottom: var(--downloads-item-target-margin-bottom);
-  cursor: inherit;
 }
 
 .downloadTarget {
   font-size: calc(100% / var(--downloads-item-font-size-factor));
 }
 
 #downloadsSummaryDetails,
 .downloadDetails {
   margin-top: var(--downloads-item-details-margin-top);
   opacity: var(--downloads-item-details-opacity);
-  cursor: inherit;
 }
 
 @item@[verdict] > toolbarseparator {
   visibility: hidden;
 }
 
 .downloadButton {
   -moz-appearance: none;
@@ -384,8 +381,11 @@ richlistitem[type="download"] > .downloa
 }
 
 #downloadsPanel-blockedSubview-title,
 #downloadsPanel-blockedSubview-details1,
 #downloadsPanel-blockedSubview-details2 {
   -moz-margin-start: 64px;
   -moz-margin-end: 16px;
 }
+
+/*** Progressmeter ***/
+%include progressmeter.inc.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/downloads/progressmeter.inc.css
@@ -0,0 +1,72 @@
+/*** Common-styled progressmeter ***/
+.downloadProgress {
+  height: 8px;
+  border-radius: 1px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  margin-inline-start: 6px;
+  margin-inline-end: 12px;
+
+  /* for overriding rules in progressmeter.css */
+  -moz-appearance: none;
+  border-style: none;
+  background-color: transparent;
+  min-width: initial;
+  min-height: initial;
+}
+
+.downloadProgress[mode="undetermined"] {
+  /* for overriding rules on global.css in Linux. */
+  -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter");
+}
+
+.downloadProgress > .progress-bar {
+  background-color: Highlight;
+
+  /* for overriding rules in progressmeter.css */
+  -moz-appearance: none;
+}
+
+.downloadProgress[paused="true"] > .progress-bar {
+  background-color: GrayText;
+}
+
+.downloadProgress[mode="undetermined"] > .progress-bar {
+  /* Make a white reflecting animation.
+     Create a gradient with 2 identical pattern, and enlarge the size to 200%.
+     This allows us to animate background-position with percentage. */
+  background-image: linear-gradient(90deg, transparent 0%,
+                                           rgba(255,255,255,0.5) 25%,
+                                           transparent 50%,
+                                           rgba(255,255,255,0.5) 75%,
+                                           transparent 100%);
+  background-blend-mode: lighten;
+  background-size: 200% 100%;
+  animation: downloadProgressSlideX 1.5s linear infinite;
+}
+
+.downloadProgress > .progress-remainder {
+  border: solid ButtonShadow;
+  border-block-start-width: 1px;
+  border-block-end-width: 1px;
+  border-inline-start-width: 0;
+  border-inline-end-width: 1px;
+  background-color: ButtonFace;
+}
+
+.downloadProgress[value="0"] > .progress-remainder {
+  border-width: 1px;
+}
+
+.downloadProgress > .progress-remainder[mode="undetermined"] {
+  border: none;
+}
+
+@keyframes downloadProgressSlideX {
+  0% {
+    background-position: 0 0;
+  }
+  100% {
+    background-position: -100% 0;
+  }
+}
--- a/browser/themes/windows/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/windows/downloads/allDownloadsViewOverlay.css
@@ -5,16 +5,22 @@
 %include ../../shared/downloads/allDownloadsViewOverlay.inc.css
 
 /*** List items ***/
 
 :root {
   --downloads-item-height: 6em;
 }
 
+@media (-moz-windows-default-theme) {
+  .downloadProgress > .progress-bar {
+    background-color: #3c9af8;
+  }
+}
+
 /*** Highlighted list items ***/
 
 @media not all and (-moz-os-version: windows-xp) {
   @media (-moz-windows-default-theme) {
     /*
     -moz-appearance: menuitem is almost right, but the hover effect is not
     transparent and is lighter than desired.
 
--- a/browser/themes/windows/downloads/downloads.css
+++ b/browser/themes/windows/downloads/downloads.css
@@ -1,55 +1,16 @@
 /* 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/. */
 
 %include ../../shared/downloads/downloads.inc.css
 
 /*** Panel and outer controls ***/
 
-#downloadsSummary {
-  background-color: hsla(210,4%,10%,.04);
-  box-shadow: 0 1px 0 hsla(210,4%,10%,.08) inset;
-  transition-duration: 150ms;
-  transition-property: background-color;
-}
-
-#downloadsSummary:hover {
-  background-color: hsla(210,4%,10%,.05);
-}
-
-#downloadsSummary:hover:active {
-  background-color: hsla(210,4%,10%,.1);
-  box-shadow: 0 2px 0 0 hsla(210,4%,10%,.1) inset;
-}
-
-@media (-moz-os-version: windows-xp),
-       (-moz-os-version: windows-vista),
-       (-moz-os-version: windows-win7) {
-  @media (-moz-windows-default-theme) {
-    #downloadsSummary,
-    #downloadsSummary:hover,
-    #downloadsSummary:hover:active {
-      background-color: #f1f5fb;
-      box-shadow: 0px 1px 2px rgb(204,214,234) inset;
-      transition-duration: 0s;
-      cursor: pointer;
-    }
-
-    @media (-moz-os-version: windows-xp) {
-      #downloadsSummary,
-      #downloadsSummary:hover,
-      #downloadsSummary:hover:active {
-        background-color: hsla(216,45%,88%,.98);
-      }
-    }
-  }
-}
-
 @keyfocus@ #downloadsSummary:focus,
 @keyfocus@ .downloadsPanelFooterButton:focus {
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -1px;
 }
 
 @keyfocus@ #downloadsSummary:focus {
   outline-offset: -5px;
@@ -72,16 +33,26 @@
 @keyfocus@ .downloadButton:focus > .button-box {
   border: 1px dotted ThreeDDarkShadow;
 }
 
 @media (-moz-windows-default-theme) {
   @item@[verdict="Malware"] {
     color: #aa1b08;
   }
+
+  /* Use unified color for the progressbar on default theme */
+  .downloadProgress > .progress-bar {
+    background-color: #3c9af8;
+  }
+
+  .downloadProgress[paused="true"] > .progress-bar {
+    background-color: #a6a6a6;
+  }
+
 }
 
 /*** Highlighted list items ***/
 
 @keyfocus@ @itemFocused@ {
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -1px;
 }
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -748,26 +748,37 @@ nsScriptSecurityManager::CheckLoadURIWit
                              &denySameSchemeLinks);
     if (NS_FAILED(rv)) return rv;
 
     while (currentURI && currentOtherURI) {
         nsAutoCString scheme, otherScheme;
         currentURI->GetScheme(scheme);
         currentOtherURI->GetScheme(otherScheme);
 
+        bool schemesMatch = scheme.Equals(otherScheme, stringComparator);
+        bool isSamePage;
+        // about: URIs are special snowflakes.
+        if (scheme.EqualsLiteral("about")) {
+            nsAutoCString module, otherModule;
+            isSamePage = schemesMatch &&
+                NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, module)) &&
+                NS_SUCCEEDED(NS_GetAboutModuleName(currentOtherURI, otherModule)) &&
+                module.Equals(otherModule);
+        } else {
+            bool equalExceptRef = false;
+            rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
+            isSamePage = NS_SUCCEEDED(rv) && equalExceptRef;
+        }
+
         // If schemes are not equal, or they're equal but the target URI
         // is different from the source URI and doesn't always allow linking
         // from the same scheme, check if the URI flags of the current target
         // URI allow the current source URI to link to it.
         // The policy is specified by the protocol flags on both URIs.
-        bool equalExceptRef = false;
-        if (!scheme.Equals(otherScheme, stringComparator) ||
-            (denySameSchemeLinks &&
-             (!NS_SUCCEEDED(currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef)) ||
-              !equalExceptRef))) {
+        if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
             return CheckLoadURIFlags(currentURI, currentOtherURI,
                                      sourceBaseURI, targetBaseURI, aFlags);
         }
         // Otherwise... check if we can nest another level:
         nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
         nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
 
         // If schemes match and neither URI is nested further, we're OK.
--- a/caps/tests/mochitest/browser_checkloaduri.js
+++ b/caps/tests/mochitest/browser_checkloaduri.js
@@ -47,16 +47,28 @@ const URLs = new Map([
     ["view-source:http://www.example2.com", true, true, true],
     ["view-source:https://www.example2.com", true, true, true],
     ["view-source:feed:http://www.example2.com", false, false, true],
     ["feed:view-source:http://www.example2.com", false, false, false],
     ["data:text/html,Hi", true, false, true],
     ["view-source:data:text/html,Hi", true, false, true],
     ["javascript:alert('hi')", true, false, true],
   ]],
+  ["about:foo", [
+    ["about:foo?", true, true, true],
+    ["about:foo?bar", true, true, true],
+    ["about:foo#", true, true, true],
+    ["about:foo#bar", true, true, true],
+    ["about:foo?#", true, true, true],
+    ["about:foo?bar#baz", true, true, true],
+    ["about:bar", false, false, true],
+    ["about:bar?foo#baz", false, false, true],
+    ["about:bar?foo", false, false, true],
+    ["http://www.example.com/", true, true, true],
+  ]],
 ]);
 
 function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) {
   let threw = false;
   let targetURI;
   try {
     targetURI = makeURI(target);
   } catch (ex) {
--- a/devtools/client/canvasdebugger/test/browser_profiling-webgl.js
+++ b/devtools/client/canvasdebugger/test/browser_profiling-webgl.js
@@ -72,20 +72,16 @@ function* ifTestingSupported() {
   is(animationOverview.primitive.points, 4, "The count of points is correct.");
   is(animationOverview.primitive.lines, 8, "The count of lines is correct.");
   
   yield removeTab(target.tab);
   finish();
 }
 
 function testFunctionCallTimestamp(functionCalls, currentTime) {
-
   info("Check the timestamps of function calls");
 
   for ( let i = 0; i < functionCalls.length-1; i += 2 ) {
     ok( functionCalls[i].timestamp > 0, "The timestamp of the called function is larger than 0." );
     ok( functionCalls[i].timestamp < currentTime, "The timestamp has been minus the frame start time." );
     ok( functionCalls[i+1].timestamp > functionCalls[i].timestamp, "The timestamp of the called function is correct." );
   }
-
-  yield removeTab(target.tab);
-  finish();
 }
--- a/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-new-script.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_breakpoints-new-script.js
@@ -68,25 +68,25 @@ function test() {
       });
 
       EventUtils.sendMouseEvent({ type: "mousedown" },
                                 gDebugger.document.getElementById("resume"),
                                 gDebugger);
       return deferred.promise;
     }
 
-    Task.spawn(function(){
+    (async function(){
       let onCaretUpdated = waitForCaretAndScopes(gPanel, 16);
       callInTab(gTab, "runDebuggerStatement");
-      yield onCaretUpdated;
+      await onCaretUpdated;
 
       is(gDebugger.gThreadClient.state, "paused",
          "The debugger statement was reached.");
       ok(isCaretPos(gPanel, 16),
          "The source editor caret position is incorrect (1).");
 
-      yield actions.addBreakpoint({ actor: getSourceActor(gSources, TAB_URL), line: 20 });
-      yield testResume();
-      yield testBreakpointHit();
+      await actions.addBreakpoint({ actor: getSourceActor(gSources, TAB_URL), line: 20 });
+      await testResume();
+      await testBreakpointHit();
       resumeDebuggerThenCloseAndFinish(gPanel);
-    });
+    })();
   });
 }
--- a/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_host-layout.js
@@ -11,106 +11,100 @@
 "use strict";
 
 var gDefaultHostType = Services.prefs.getCharPref("devtools.toolbox.host");
 
 function test() {
   // test is too slow on some platforms due to the number of test cases
   requestLongerTimeout(3);
 
-  Task.spawn(function*() {
-    yield testHosts(["bottom", "side", "window:big"], ["horizontal", "vertical", "horizontal"]);
-    yield testHosts(["side", "bottom", "side"], ["vertical", "horizontal", "vertical"]);
-    yield testHosts(["bottom", "side", "bottom"], ["horizontal", "vertical", "horizontal"]);
-    yield testHosts(["side", "window:big", "side"], ["vertical", "horizontal", "vertical"]);
-    yield testHosts(["window:big", "side", "window:big"], ["horizontal", "vertical", "horizontal"]);
-    yield testHosts(["window:small", "bottom", "side"], ["vertical", "horizontal", "vertical"]);
-    yield testHosts(["window:small", "window:big", "window:small"], ["vertical", "horizontal", "vertical"]);
+  (async function() {
+    await testHosts(["bottom", "side", "window:big"], ["horizontal", "vertical", "horizontal"]);
+    await testHosts(["side", "bottom", "side"], ["vertical", "horizontal", "vertical"]);
+    await testHosts(["bottom", "side", "bottom"], ["horizontal", "vertical", "horizontal"]);
+    await testHosts(["side", "window:big", "side"], ["vertical", "horizontal", "vertical"]);
+    await testHosts(["window:big", "side", "window:big"], ["horizontal", "vertical", "horizontal"]);
+    await testHosts(["window:small", "bottom", "side"], ["vertical", "horizontal", "vertical"]);
+    await testHosts(["window:small", "window:big", "window:small"], ["vertical", "horizontal", "vertical"]);
     finish();
-  });
+  })();
 }
 
-function testHosts(aHostTypes, aLayoutTypes) {
+async function testHosts(aHostTypes, aLayoutTypes) {
   let [firstHost, secondHost, thirdHost] = aHostTypes;
   let [firstLayout, secondLayout, thirdLayout] = aLayoutTypes;
 
   Services.prefs.setCharPref("devtools.toolbox.host", getHost(firstHost));
 
-  return Task.spawn(function*() {
-    let [tab, debuggee, panel] = yield initDebugger();
-    if (getHost(firstHost) === "window") {
-      yield resizeToolboxWindow(panel, firstHost);
-    }
+  let [tab, debuggee, panel] = await initDebugger();
+  if (getHost(firstHost) === "window") {
+    await resizeToolboxWindow(panel, firstHost);
+  }
 
-    yield testHost(panel, getHost(firstHost), firstLayout);
-    yield switchAndTestHost(tab, panel, secondHost, secondLayout);
-    yield switchAndTestHost(tab, panel, thirdHost, thirdLayout);
-    yield teardown(panel);
-  });
+  await testHost(panel, getHost(firstHost), firstLayout);
+  await switchAndTestHost(tab, panel, secondHost, secondLayout);
+  await switchAndTestHost(tab, panel, thirdHost, thirdLayout);
+  await teardown(panel);
 }
 
-function switchAndTestHost(aTab, aPanel, aHostType, aLayoutType) {
+async function switchAndTestHost(aTab, aPanel, aHostType, aLayoutType) {
   let gToolbox = aPanel._toolbox;
   let gDebugger = aPanel.panelWin;
 
-  return Task.spawn(function*() {
-    let layoutChanged = waitEventOnce(gDebugger, gDebugger.EVENTS.LAYOUT_CHANGED);
-    let hostChanged = gToolbox.switchHost(getHost(aHostType));
+  let layoutChanged = waitEventOnce(gDebugger, gDebugger.EVENTS.LAYOUT_CHANGED);
+  let hostChanged = gToolbox.switchHost(getHost(aHostType));
 
-    yield hostChanged;
-    info("The toolbox's host has changed.");
+  await hostChanged;
+  info("The toolbox's host has changed.");
 
-    if (getHost(aHostType) === "window") {
-      yield resizeToolboxWindow(aPanel, aHostType);
-    }
+  if (getHost(aHostType) === "window") {
+    await resizeToolboxWindow(aPanel, aHostType);
+  }
 
-    yield layoutChanged;
-    info("The debugger's layout has changed.");
+  await layoutChanged;
+  info("The debugger's layout has changed.");
 
-    yield testHost(aPanel, getHost(aHostType), aLayoutType);
-  });
+  await testHost(aPanel, getHost(aHostType), aLayoutType);
 }
 
 function waitEventOnce(aTarget, aEvent) {
-  let deferred = promise.defer();
-  aTarget.once(aEvent, deferred.resolve);
-  return deferred.promise;
+  return new Promise(resolve => aTarget.once(aEvent, resolve));
 }
 
 function getHost(host) {
   if (host.indexOf("window") == 0) {
     return "window";
   }
   return host;
 }
 
-function resizeToolboxWindow(panel, host) {
+async function resizeToolboxWindow(panel, host) {
   let sizeOption = host.split(":")[1];
   let win = panel._toolbox.win.parent;
 
   // should be the same value as BREAKPOINT_SMALL_WINDOW_WIDTH in debugger-view.js
   let breakpoint = 850;
 
   if (sizeOption == "big" && win.outerWidth <= breakpoint) {
-    yield resizeAndWaitForLayoutChange(panel, breakpoint + 300);
+    await resizeAndWaitForLayoutChange(panel, breakpoint + 300);
   } else if (sizeOption == "small" && win.outerWidth >= breakpoint) {
-    yield resizeAndWaitForLayoutChange(panel, breakpoint - 300);
+    await resizeAndWaitForLayoutChange(panel, breakpoint - 300);
   } else {
     info("Window resize unnecessary for host " + host);
   }
 }
 
-function resizeAndWaitForLayoutChange(panel, width) {
-    info("Updating toolbox window width to " + width);
+async function resizeAndWaitForLayoutChange(panel, width) {
+  info("Updating toolbox window width to " + width);
 
-    let win = panel._toolbox.win.parent;
-    let gDebugger = panel.panelWin;
+  let win = panel._toolbox.win.parent;
+  let gDebugger = panel.panelWin;
 
-    win.resizeTo(width, window.screen.availHeight);
-    yield waitEventOnce(gDebugger, gDebugger.EVENTS.LAYOUT_CHANGED);
+  win.resizeTo(width, window.screen.availHeight);
+  await waitEventOnce(gDebugger, gDebugger.EVENTS.LAYOUT_CHANGED);
 }
 
 function testHost(aPanel, aHostType, aLayoutType) {
   let gDebugger = aPanel.panelWin;
   let gView = gDebugger.DebuggerView;
 
   is(gView._hostType, aHostType,
     "The default host type should've been set on the panel window (1).");
--- a/devtools/client/shared/components/tabs/tabbar.css
+++ b/devtools/client/shared/components/tabs/tabbar.css
@@ -21,17 +21,17 @@
 }
 
 .tabs .tabs-menu-item.is-active {
   height: 23px;
 }
 
 /* Firebug theme is using slightly different height. */
 .theme-firebug .tabs .tabs-navigation {
-  height: 24px;
+  height: 28px;
 }
 
 .tabs .tabs-menu-item a {
   cursor: default;
 }
 
 /* The tab takes entire horizontal space and individual tabs
   should stretch accordingly. Use flexbox for the behavior.
--- a/devtools/client/storage/test/storage-listings.html
+++ b/devtools/client/storage/test/storage-listings.html
@@ -23,94 +23,94 @@ document.cookie = "c3=foobar-2; expires=
   new Date(cookieExpiresTime2).toGMTString() + "; path=/";
 // ... and some local storage items ..
 localStorage.setItem("ls1", "foobar");
 localStorage.setItem("ls2", "foobar-2");
 // ... and finally some session storage items too
 sessionStorage.setItem("ss1", "foobar-3");
 dump("added cookies and storage from main page\n");
 
-let idbGenerator = function*() {
+let idbGenerator = async function() {
   let request = indexedDB.open("idb1", 1);
   request.onerror = function() {
     throw new Error("error opening db connection");
   };
-  let db = yield new Promise(done => {
+  let db = await new Promise(done => {
     request.onupgradeneeded = event => {
       let db = event.target.result;
       let store1 = db.createObjectStore("obj1", { keyPath: "id" });
       store1.createIndex("name", "name", { unique: false });
       store1.createIndex("email", "email", { unique: true });
       let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
       store1.transaction.oncomplete = () => {
         done(db);
       };
     };
   });
 
   // Prevents AbortError
-  yield new Promise(done => {
+  await new Promise(done => {
     request.onsuccess = done;
   });
 
   let transaction = db.transaction(["obj1", "obj2"], "readwrite");
   let store1 = transaction.objectStore("obj1");
   let store2 = transaction.objectStore("obj2");
   store1.add({id: 1, name: "foo", email: "foo@bar.com"});
   store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
   store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
   store2.add({
     id2: 1,
     name: "foo",
     email: "foo@bar.com",
     extra: "baz"
   });
   // Prevents AbortError during close()
-  yield new Promise(success => {
+  await new Promise(success => {
     transaction.oncomplete = success;
   });
 
   db.close();
 
   request = indexedDB.open("idb2", 1);
-  let db2 = yield new Promise(done => {
+  let db2 = await new Promise(done => {
     request.onupgradeneeded = event => {
       let db2 = event.target.result;
       let store3 = db2.createObjectStore("obj3", { keyPath: "id3" });
       store3.createIndex("name2", "name2", { unique: true });
       store3.transaction.oncomplete = () => {
         done(db2);
       }
     };
   });
   // Prevents AbortError during close()
-  yield new Promise(done => {
+  await new Promise(done => {
     request.onsuccess = done;
   });
   db2.close();
 
   dump("added indexedDB from main page\n");
 };
 
 function deleteDB(dbName) {
   return new Promise(resolve => {
     dump("removing database " + dbName + " from " + document.location + "\n");
     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
   });
 }
 
-function fetchPut(cache, url) {
-  let response = yield fetch(url);
-  yield cache.put(url, response);
+async function fetchPut(cache, url) {
+  let response = await fetch(url);
+  await cache.put(url, response);
 }
 
-let cacheGenerator = function*() {
-  let cache = yield caches.open("plop");
-  yield fetchPut(cache, "404_cached_file.js");
-  yield fetchPut(cache, "browser_storage_basic.js");
+let cacheGenerator = async function() {
+  let cache = await caches.open("plop");
+  await fetchPut(cache, "404_cached_file.js");
+  await fetchPut(cache, "browser_storage_basic.js");
 };
 
 window.setup = function*() {
   yield idbGenerator();
   yield cacheGenerator();
 };
 
 window.clear = function*() {
--- a/devtools/client/themes/firebug-theme.css
+++ b/devtools/client/themes/firebug-theme.css
@@ -76,17 +76,17 @@
   background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2));
 }
 
 /* Add a negative bottom margin to overlap bottom border
   of the parent element (see also the next comment for 'tabs') */
 .theme-firebug .devtools-tab,
 .theme-firebug .devtools-sidebar-tabs tab {
   margin: 3px 0 -1px 0;
-  padding: 2px 0 0 0;
+  padding: 2px 4px 0 4px;
   border: 1px solid transparent !important;
   border-radius: 4px 4px 0 0;
   font-weight: bold;
   color: var(--theme-body-color);
   -moz-box-flex: initial;
   min-width: 0;
 }
 
--- a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
+++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
@@ -15,221 +15,221 @@ const inspector = require("devtools/shar
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker, gDoc;
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   let url = document.getElementById("inspectorContent").href;
 
   let def = promise.defer();
   attachURL(url, function(err, client, tab, doc) {
     def.resolve({client, tab, doc});
   });
   let {client, tab, doc} = yield def.promise;
   gDoc = doc;
 
   let {InspectorFront} = require("devtools/shared/fronts/inspector");
   let inspector = InspectorFront(client, tab);
   gWalker = yield inspector.getWalker();
 
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.parents(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.parents(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.parents() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.children(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.children(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.children() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.siblings(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.siblings(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.siblings() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.nextSibling(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.nextSibling(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.nextSibling() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.previousSibling(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.previousSibling(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.previousSibling() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.addPseudoClassLock(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.addPseudoClassLock(nodeFront, ":hover");
   yield newRoot;
 
   ok(true, "The call to walker.addPseudoClassLock() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removePseudoClassLock(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.removePseudoClassLock(nodeFront, ":hover");
   yield newRoot;
 
   ok(true, "The call to walker.removePseudoClassLock() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.clearPseudoClassLocks(nodeFront) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.clearPseudoClassLocks(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.clearPseudoClassLocks() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.innerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.innerHTML(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.innerHTML() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.setInnerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.setInnerHTML(nodeFront, "<span>innerHTML changed</span>");
   yield newRoot;
 
   ok(true, "The call to walker.setInnerHTML() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.outerHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.outerHTML(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.outerHTML() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.setOuterHTML(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.setOuterHTML(nodeFront, "<h1><span>innerHTML changed</span></h1>");
   yield newRoot;
 
   ok(true, "The call to walker.setOuterHTML() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertAdjacentHTML(nodeFront) before the load completes shouldn't " +
     "fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertAdjacentHTML(nodeFront, "afterEnd",
     "<span>new adjacent HTML</span>");
   yield newRoot;
 
   ok(true, "The call to walker.insertAdjacentHTML() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removeNode(nodeFront) before the load completes should throw");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   let hasThrown = false;
   try {
@@ -238,17 +238,17 @@ addAsyncTest(function() {
     hasThrown = true;
   }
   yield newRoot;
 
   ok(hasThrown, "The call to walker.removeNode() threw");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.removeNodes([nodeFront]) before the load completes should throw");
 
   let nodeFront1 = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let nodeFront2 = yield gWalker.querySelector(gWalker.rootNode, "#longstring");
   let nodeFront3 = yield gWalker.querySelector(gWalker.rootNode, "#shortstring");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
@@ -259,106 +259,106 @@ addAsyncTest(function() {
     hasThrown = true;
   }
   yield newRoot;
 
   ok(hasThrown, "The call to walker.removeNodes() threw");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertBefore(nodeFront, parent, null) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertBefore(nodeFront, newParentFront);
   yield newRoot;
 
   ok(true, "The call to walker.insertBefore() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.insertBefore(nodeFront, parent, sibling) before the load completes " +
     "shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist");
   let siblingFront = yield gWalker.querySelector(gWalker.rootNode, "#b");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.insertBefore(nodeFront, newParentFront, siblingFront);
   yield newRoot;
 
   ok(true, "The call to walker.insertBefore() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.editTagName(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.editTagName(nodeFront, "h2");
   yield newRoot;
 
   ok(true, "The call to walker.editTagName() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.hideNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.hideNode(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.hideNode() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.unhideNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.unhideNode(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.unhideNode() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.releaseNode(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.releaseNode(nodeFront);
   yield newRoot;
 
   ok(true, "The call to walker.releaseNode() didn't fail");
   runNextTest();
 });
 
-addAsyncTest(function() {
+addAsyncTest(function*() {
   info("Getting a nodeFront, reloading the page, and calling " +
     "walker.querySelector(nodeFront) before the load completes shouldn't fail");
 
   let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body");
   let newRoot = waitForMutation(gWalker, isNewRoot);
   gDoc.defaultView.location.reload();
   yield gWalker.querySelector(nodeFront, "h1");
   yield newRoot;
--- a/devtools/shared/qrcode/tests/mochitest/test_decode.html
+++ b/devtools/shared/qrcode/tests/mochitest/test_decode.html
@@ -8,57 +8,53 @@ Test decoding a simple message
   <title>Test decoding a simple message</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const { utils: Cu } = Components;
   const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  const { Task } = require("devtools/shared/task");
-  const promise = require("promise");
-  const defer = require("devtools/shared/defer");
 
   const QR = require("devtools/shared/qrcode/index");
 
   SimpleTest.waitForExplicitFinish();
 
   const testImage =
     "data:image/gif;base64,R0lGODdhOgA6AIAAAAAAAP///ywAAAAAOgA6AAAC" +
     "/4yPqcvtD6OctNqLs968+w+G4gKU5nkaKKquLuW+QVy2tAkDTj3rfQts8CRDko" +
     "+HPPoYRUgy9YsyldDm44mLWhHYZM6W7WaDqyCRGkZDySxpRGw2sqvLt1q5w/fo" +
     "XyE6vnUQOJUHBlinMGh046V1F5PDqNcoqcgBOWKBKbK2N+aY+Ih49VkmqMcl2l" +
     "dkhZUK1umE6jZXJ2ZJaujZaRqH4bpb2uZrJxvIt4Ebe9qoYYrJOsw8apz2bCut" +
     "m9kqDcw52uuImyr5Oh1KXH1jrn2anuunywtODU/o2c6teceW39ZcLFg/fNMo1b" +
     "t3jVw2dwTPwJq1KYG3gAklCgu37yGxeScYKyiCc+7DR34hPVQiuQ7UhJMagyEb" +
     "lymmzJk0a9q8iTOnzp0NCgAAOw==";
 
-  Task.spawn(function() {
-    let result = yield QR.decodeFromURI(testImage);
+  (async function() {
+    let result = await QR.decodeFromURI(testImage);
     is(result, "HELLO", "Decoded data URI result matches");
-    let canvas = yield drawToCanvas(testImage);
+    let canvas = await drawToCanvas(testImage);
     result = QR.decodeFromCanvas(canvas);
     is(result, "HELLO", "Decoded canvas result matches");
-  }).then(SimpleTest.finish, ok.bind(null, false));
+  })().then(SimpleTest.finish, ok.bind(null, false));
 
   function drawToCanvas(src) {
-    let deferred = defer();
-    let canvas = document.createElement("canvas");
-    let context = canvas.getContext("2d");
-    let image = new Image();
+    return new Promise(resolve => {
+      let canvas = document.createElement("canvas");
+      let context = canvas.getContext("2d");
+      let image = new Image();
 
-    image.onload = () => {
-      canvas.width = image.width;
-      canvas.height = image.height;
-      context.drawImage(image, 0, 0);
-      deferred.resolve(canvas);
-    };
-    image.src = src;
-
-    return deferred.promise;
+      image.onload = () => {
+        canvas.width = image.width;
+        canvas.height = image.height;
+        context.drawImage(image, 0, 0);
+        resolve(canvas);
+      };
+      image.src = src;
+    });
   }
 };
   </script>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3002,39 +3002,16 @@ nsDocShell::TopSessionStorageManager()
   if (!mSessionStorageManager) {
     mSessionStorageManager =
       do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
   }
 
   return mSessionStorageManager;
 }
 
-NS_IMETHODIMP
-nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
-                                          const nsAString& aDocumentURI,
-                                          bool aCreate,
-                                          nsIDOMStorage** aStorage)
-{
-  nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
-  if (!manager) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
-
-  AssertOriginAttributesMatchPrivateBrowsing();
-  if (aCreate) {
-    return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
-                                  aDocumentURI, aStorage);
-  }
-
-  return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
-                             aStorage);
-}
-
 nsresult
 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, nsIDOMStorage* aStorage)
 {
   RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
   if (!storage) {
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -9582,17 +9559,17 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI,
 }
 
 class InternalLoadEvent : public Runnable
 {
 public:
   InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
                     nsIURI* aOriginalURI, bool aLoadReplace,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
-                    nsIPrincipal* aTriggeringPrincipal, 
+                    nsIPrincipal* aTriggeringPrincipal,
                     nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
                     const char* aTypeHint, nsIInputStream* aPostData,
                     nsIInputStream* aHeadersData, uint32_t aLoadType,
                     nsISHEntry* aSHEntry, bool aFirstParty,
                     const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
                     nsIURI* aBaseURI)
     : mSrcdoc(aSrcdoc)
     , mDocShell(aDocShell)
@@ -9917,17 +9894,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   // or by C++ setting document.location) don't get a funky principal.  If
   // callers want something interesting to happen with the about:blank
   // principal in this case, they should pass aPrincipalToInherit in.
   //
   {
     bool inherits;
     // One more twist: Don't inherit the principal for external loads.
     if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
-        (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&   
+        (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
          NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
                                                                  &inherits)) &&
          inherits) {
       principalToInherit = GetInheritedPrincipal(true);
     }
   }
 
   // Don't allow loads that would inherit our security context
@@ -10324,17 +10301,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
       /* This is a anchor traversal with in the same page.
        * call OnNewURI() so that, this traversal will be
        * recorded in session and global history.
        */
       nsCOMPtr<nsIPrincipal> triggeringPrincipal, principalToInherit;
       if (mOSHE) {
         mOSHE->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
-        mOSHE->GetPrincipalToInherit(getter_AddRefs(principalToInherit)); 
+        mOSHE->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
       }
       // Pass true for aCloneSHChildren, since we're not
       // changing documents here, so all of our subframes are
       // still relevant to the new session history entry.
       //
       // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
       // flag on firing onLocationChange(...).
       // Anyway, aCloneSHChildren param is simply reflecting
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -47,17 +47,17 @@ interface nsICommandManager;
 interface nsICommandParams;
 native TabChildRef(already_AddRefed<nsITabChild>);
 
 [scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
-   * in the object implementing	this interface.  If it can't be loaded here
+   * in the object implementing this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
    * @param uri        - The URI to load.
    * @param loadInfo   - This is the extended load info for this load.  This
    *                     most often will be null, but if you need to do
    *                     additional setup for this load you can get a loadInfo
    *                     object by calling createLoadInfo.  Once you have this
@@ -525,31 +525,16 @@ interface nsIDocShell : nsIDocShellTreeI
    *
    * @param numEntries - The number of entries removed
    */
   void historyPurged(in long numEntries);
 
   /*
    * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
    *
-   * Retrieves the WebApps session storage object for the supplied principal.
-   *
-   * @param principal returns a storage for this principal
-   * @param documentURI new storage will be created with reference to this
-   *                    document.documentURI that will appear in storage event
-   * @param create If true and a session storage object doesn't
-   *               already exist, a new one will be created.
-   */
-  nsIDOMStorage getSessionStorageForPrincipal(in nsIPrincipal principal,
-                                              in DOMString documentURI,
-                                              in boolean create);
-
-  /*
-   * @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
-   *
    * Add a WebApps session storage object to the docshell.
    *
    * @param principal the principal the storage object is associated with
    * @param storage the storage object to add
    */
   void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
 
   /**
--- a/docshell/test/browser/browser_bug349769.js
+++ b/docshell/test/browser/browser_bug349769.js
@@ -1,14 +1,14 @@
 add_task(function* test() {
   const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
   const uris = [undefined, "about:blank"];
 
-  function checkContentProcess(uri) {
-    yield ContentTask.spawn(newBrowser, uri, function* (uri) {
+  function checkContentProcess(newBrowser, uri) {
+    return ContentTask.spawn(newBrowser, uri, function* (uri) {
       var prin = content.document.nodePrincipal;
       Assert.notEqual(prin, null, "Loaded principal must not be null when adding " + uri);
       Assert.notEqual(prin, undefined, "Loaded principal must not be undefined when loading " + uri);
 
       const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
                        .getService(Ci.nsIScriptSecurityManager);
       Assert.equal(secMan.isSystemPrincipal(prin), false,
          "Loaded principal must not be system when loading " + uri);
@@ -23,25 +23,25 @@ add_task(function* test() {
       isnot(prin, null, "Forced principal must not be null when loading " + uri);
       isnot(prin, undefined,
             "Forced principal must not be undefined when loading " + uri);
       is(secMan.isSystemPrincipal(prin), false,
          "Forced principal must not be system when loading " + uri);
 
       // Belt-and-suspenders e10s check: make sure that the same checks hold
       // true in the content process.
-      checkContentProcess(uri);
+      yield checkContentProcess(newBrowser, uri);
 
       yield BrowserTestUtils.browserLoaded(newBrowser);
 
       prin = newBrowser.contentPrincipal;
       isnot(prin, null, "Loaded principal must not be null when adding " + uri);
       isnot(prin, undefined, "Loaded principal must not be undefined when loading " + uri);
       is(secMan.isSystemPrincipal(prin), false,
          "Loaded principal must not be system when loading " + uri);
 
       // Belt-and-suspenders e10s check: make sure that the same checks hold
       // true in the content process.
-      checkContentProcess(uri);
+      yield checkContentProcess(newBrowser, uri);
     });
   }
 });
 
--- a/docshell/test/chrome/bug112564_window.xul
+++ b/docshell/test/chrome/bug112564_window.xul
@@ -71,24 +71,20 @@
       }
 
       if (gExpected.length == 0) {
         setTimeout(nextTest, 0);
       }
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err if err instanceof StopIteration) {
-        finish();
-      }
+      gTestsIterator.next();
     }
 
-    function testsIterator() {
+    function* testsIterator() {
       // Load a secure page with a no-cache header, followed by a simple page.
       // no-cache should not interfere with the bfcache in the way no-store
       // does.
       var test1DocURI = "https://example.com:443/tests/docshell/test/chrome/112564_nocache.html";
 
       gExpected = [{type: "pagehide", persisted: true},
                    {type: "load", title: "test1"},
                    {type: "pageshow", title: "test1", persisted: false}];
@@ -105,13 +101,15 @@
       yield undefined;
 
       // Now go back in history. First page has been cached.
       // Check persisted property to confirm
       gExpected = [{type: "pagehide", title: "test2", persisted: true},
                    {type: "pageshow", title: "test1", persisted: true}];
       gBrowser.goBack();
       yield undefined;
+
+      finish();
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug215405_window.xul
+++ b/docshell/test/chrome/bug215405_window.xul
@@ -48,24 +48,20 @@
       nextTest();
     }
 
     function eventListener(event) {
       setTimeout(nextTest, 0);
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err if err instanceof StopIteration) {
-        finish();
-      }
+      gTestsIterator.next();
     }
 
-    function testsIterator() {
+    function* testsIterator() {
       // No-store tests
       var testName = "[nostore]";
 
       // Load a page with a no-store header
       gBrowser.loadURI(nostoreURI);
       yield undefined;
 
 
@@ -151,17 +147,14 @@
       // and form contents are restored
       is(gBrowser.contentWindow.scrollX, scrollX, testName +
          " horizontal axis scroll position not correctly restored");
       is(gBrowser.contentWindow.scrollY, scrollY, testName +
          " vertical axis scroll position not correctly restored");
       var formValue = gBrowser.contentDocument.getElementById("inp").value;
       is(formValue, text, testName + " form value not correctly restored");
       
-      // nextTest has to be called from here, as no events are fired in this
-      // step
-      setTimeout(nextTest, 0);
-      yield undefined;
+      finish();
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug293235_window.xul
+++ b/docshell/test/chrome/bug293235_window.xul
@@ -37,17 +37,17 @@
     function $(id) { return TestWindow.getDocument().getElementById(id); }
 
     ////
     // Generator function for test steps for bug 293235:
     // A visited link should have the :visited style applied
     // to it when displayed on a page which was fetched from
     // the bfcache.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Register our observer to know when the link lookup is complete.
       let testURI = NetUtil.newURI(getHttpUrl("bug293235_p2.html"));
       let os = Cc["@mozilla.org/observer-service;1"].
                getService(Ci.nsIObserverService);
       const URI_VISITED_RESOLUTION_TOPIC = "visited-status-resolution";
       let observer = {
         notified: false,
@@ -145,18 +145,16 @@
         onNavComplete: nextTest
       });
       yield undefined;
 
       // Now we can test the link color.
       ok(snapshotsEqual(refVisited, snapshotWindow(TestWindow.getWindow())),
          "visited link should be purple");
 
-      // Tell the framework the test is finished.  Include the final 'yield'
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
 
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug294258_window.xul
+++ b/docshell/test/chrome/bug294258_window.xul
@@ -23,17 +23,17 @@
     }
 
     function $(id) { return TestWindow.getDocument().getElementById(id); }
 
     ////
     // Generator function for test steps for bug 294258:  
     // Form values should be preserved on reload.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Load a page containing a form.
       doPageNavigation( {
           uri: getHttpUrl("bug294258_testcase.html"),
           onNavComplete: nextTest
         } );
       yield undefined;
       
@@ -59,18 +59,16 @@
       // Verify that none of the form data has changed.
       is($("text").value, "text value", "Text value changed");
       is($("checkbox").checked, true, "Checkbox value changed");
       is($("file").value, filePath, "File value changed");
       is($("textarea").value, "textarea value", "Textarea value changed");
       is($("radio1").checked, true, "Radio value changed");
       is($("select").selectedIndex, 2, "Select value changed");
 
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug298622_window.xul
+++ b/docshell/test/chrome/bug298622_window.xul
@@ -24,17 +24,17 @@
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 298622:  
     // Find should work correctly on a page loaded from the
     // bfcache.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Make sure bfcache is on.
       enableBFCache(true);
       
       // Load a test page which contains some text to be found.
       doPageNavigation({
         uri: "data:text/html,<html><head><title>test1</title></head>" +
              "<body>find this!</body></html>",
@@ -126,20 +126,18 @@
           "find this");
       }, nextTest, 20);
       yield undefined;
 
       is(TestWindow.getWindow().getSelection().toString().toLowerCase(),
          "find this",
          "find failed on page loaded from bfcache");
       
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <commandset>
     <command id="cmd_find"
      oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   </commandset>
--- a/docshell/test/chrome/bug301397_window.xul
+++ b/docshell/test/chrome/bug301397_window.xul
@@ -38,17 +38,17 @@
     }
     
     ////
     // Generator function for test steps for bug 301397:  
     // The correct page should be displayed in an iframe when
     // navigating back and forwards, when the parent page
     // occupies multiple spots in the session history.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Make sure the bfcache is enabled.
       enableBFCache(8);
 
       // Load a dummy page.
       doPageNavigation({
         uri: getHttpUrl("generic.html"),
         onNavComplete: nextTest
@@ -233,18 +233,16 @@
                             persisted: true,
                             title: "iframe parent" } ],
         onNavComplete: nextTest
       });
       yield undefined;
       
       verifyIframeInnerHtml("You made it");
       
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug303267_window.xul
+++ b/docshell/test/chrome/bug303267_window.xul
@@ -22,17 +22,17 @@
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 303267:  When a page is 
     // displayed from the bfcache, the script globals should
     // remain intact from the page's initial load.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Load an initial test page which should be saved in the bfcache.
       var navData = {
         uri: getHttpUrl("bug303267.html"),
         eventsToListenFor: ["pageshow"],
         expectedEvents: [ {type: "pageshow", title: "bug303267.html"} ],
         onNavComplete: nextTest
       };
@@ -77,20 +77,18 @@
       
       // After going back, if showpagecount() could access a global variable
       // and change the test div's innerHTML, then we pass.  Otherwise, it
       // threw an exception and the following test will fail.
       var newHTML = getInnerHTMLById("div1");
       isnot(originalHTML, 
         newHTML, "HTML not updated on pageshow; javascript broken?");
 
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
 
     ////
     // Return the innerHTML of a particular element in the content document.
     //
     function getInnerHTMLById(id) {
       return TestWindow.getDocument().getElementById(id).innerHTML;
     }
--- a/docshell/test/chrome/bug321671_window.xul
+++ b/docshell/test/chrome/bug321671_window.xul
@@ -29,17 +29,17 @@
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 321671:  Scroll position 
     // should be retained when moving backwards and forwards through pages 
     // when bfcache is enabled.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Variable to hold the scroll positions of the test pages.
       var scrollPositions = [];
       
       // Make sure bfcache is on.
       enableBFCache(true);
       
       // Load enough test pages that so the first one is evicted from the
@@ -116,18 +116,16 @@
           onNavComplete: nextTest
         } );
         yield undefined;
         
         is(TestWindow.getWindow().scrollY, scrollPositions[i],
           "Scroll position not restored while going forward!");
       }
       
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug360511_window.xul
+++ b/docshell/test/chrome/bug360511_window.xul
@@ -22,17 +22,17 @@
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 360511:  
     // Fragment uri's in session history should be restored correctly
     // upon back navigation.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Case 1: load a page containing a fragment link; the page should be 
       // stored in the bfcache.
       // Case 2: load a page containing a fragment link; the page should NOT 
       // be stored in the bfcache.
       for (var i = 1; i < 3; i++)
       {
         var url = "bug360511_case" + i + ".html";
@@ -117,18 +117,16 @@
       
         // Verify the current url is the original url without fragment
         is(TestWindow.getBrowser().currentURI.spec, originalUrl, 
           "current url is not the original url");
         is(TestWindow.getDocument().location.href, originalDocLocation,
           "document.location is not the original url");
       }
                       
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug364461_window.xul
+++ b/docshell/test/chrome/bug364461_window.xul
@@ -67,24 +67,20 @@
       }
 
       if (gExpected.length == 0) {
         setTimeout(nextTest, 0);
       }
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err if err instanceof StopIteration) {
-        finish();
-      }
+      gTestsIterator.next();
     }
 
-    function testsIterator() {
+    function* testsIterator() {
 
       // Tests 1 + 2:
       //  Back/forward between two simple documents. Bfcache will be used.
 
       var test1Doc = "data:text/html,<html><head><title>test1</title></head>" +
                      "<body>test1</body></html>";
 
       gExpected = [{type: "pagehide", persisted: true},
@@ -261,17 +257,14 @@
                    {type: "load"},
                    {type: "pageshow", persisted: false}];
       clickIframeLink();
       yield undefined;
       
       is(gBrowser.currentURI.spec, "data:text/plain,aaa",
          "Navigation is blocked when clicking link");
 
-      // nextTest has to be called from here, as no events are fired in this
-      // step
-      setTimeout(nextTest, 0);
-      yield undefined;
+      finish();
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug396519_window.xul
+++ b/docshell/test/chrome/bug396519_window.xul
@@ -87,24 +87,20 @@
       else {
         is(history.count, gExpected.length, "Wrong history length in test "+gTestCount);
       }
 
       setTimeout(nextTest, 0);
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err if err instanceof StopIteration) {
-        finish();
-      }
+      gTestsIterator.next();
     }
 
-    function testsIterator() {
+    function* testsIterator() {
 
       // Tests 1 + 2:
       //  Back/forward between two simple documents. Bfcache will be used.
 
       var test1Doc = "data:text/html,<html><head><title>test1</title></head>" +
                      "<body>test1</body></html>";
 
       gTestCount++;
@@ -157,13 +153,15 @@
       gExpected = [false, true, true, true, false, false];
       gBrowser.goBack();
       yield undefined;
 
       gTestCount++;
       gExpected = [false, false, true, true, false, false];
       gBrowser.gotoIndex(5);
       yield undefined;
+
+      finish();
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug396649_window.xul
+++ b/docshell/test/chrome/bug396649_window.xul
@@ -35,17 +35,17 @@
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 396649:  Content
     // viewers should be evicted from bfcache when going back if more
     // than MAX_BFCACHE_PAGES from the current index.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Make sure bfcache is on.
       enableBFCache(true);
       
       // Load enough pages so that the first loaded is eviced from
       // the bfcache, since it is greater the MAX_BFCACHE_PAGES from
       // the current position in the session history. Verify all
       // of the pages are initially stored in the bfcache when
@@ -105,18 +105,16 @@
                             { type: "pageshow", 
                               title: "bug396649 page" + i,
                               persisted: i < MAX_BFCACHE_PAGES + 1 } ],
           onNavComplete: nextTest
         } );
         yield undefined;
       }
       
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug449780_window.xul
+++ b/docshell/test/chrome/bug449780_window.xul
@@ -32,17 +32,17 @@
     /** Test for Bug 449780 **/
     var doc1 = "data:text/html,<html><body>This is a test</body></html>";
     var doc2 = "data:text/html,<html><body>This is a second test</body></html>";
 
     function getDOM(id) {
       return $(id).contentDocument.documentElement.innerHTML;
     }
 
-    var tester = (function() {
+    var tester = (function*() {
       var origDOM = getDOM("f1");
       $("f1").contentDocument.body.textContent = "Modified";
       var modifiedDOM = getDOM("f1");
       isnot(origDOM, modifiedDOM, "DOM should be different");
       $("f1").contentWindow.location.href = doc2;
       yield undefined;
 
       $("f1").goBack();
@@ -57,17 +57,16 @@
       $("f1").swapDocShells($("f2"));
       $("f2").addEventListener("pageshow", testDriver, false);
       $("f2").goBack();
       yield undefined;
 
       is(getDOM("f2"), origDOM, "Should have not have been bfcached");
       window.close();
       SimpleTest.finish();
-      yield undefined;
     })();
 
     function testDriver() {
       setTimeout(function() { tester.next() }, 0);
     }
 
     function doTheTest() {
       $("f1").addEventListener("pageshow", testDriver, false);
--- a/docshell/test/chrome/bug582176_window.xul
+++ b/docshell/test/chrome/bug582176_window.xul
@@ -21,17 +21,17 @@
     function nextTestAsync() {
       SimpleTest.executeSoon(tests.next.bind(tests));
     }
 
     ////
     // Generator function for test steps for bug 582176:
     // Description goes here.
     //
-    function testIterator()
+    function* testIterator()
     {
       var browser = document.getElementById('content');
       browser.addEventListener("pageshow", nextTestAsync, true);
 
       enableBFCache(true);
 
       var notificationCount = 0;
       var observer = {
@@ -72,17 +72,15 @@
       browser.loadURI("http://mochi.test:8888/tests/docshell/test/chrome/582176_xml.xml");
       yield undefined;
       is(browser.contentDocument.body.textContent, "xslt result",
          "Transform performed successfully");
       is(notificationCount, 3, "Should notify only once on XSLT navigation");
 
       os.removeObserver(observer, "content-document-global-created")
 
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug662200_window.xul
+++ b/docshell/test/chrome/bug662200_window.xul
@@ -23,17 +23,17 @@
     function nextTest() {
       tests.next();
     }
 
     ////
     // Generator function for test steps for bug 662200:  
     // Description goes here.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Load the first test page
       var navData = {
         uri: getHttpUrl("662200a.html"),
         eventsToListenFor: ["pageshow"],
         expectedEvents: [ {type: "pageshow", title: "A"} ],
         onNavComplete: nextTest
       };
@@ -112,18 +112,16 @@
       shistory.reload(0);
       yield undefined;
 
       // After this sequence of events, we should be able to go back and forward
       is(TestWindow.getBrowser().canGoBack, true, "Should be able to go back!");
       is(TestWindow.getBrowser().canGoForward, true, "Should be able to go forward!");
       is(shistory.requestedIndex, -1, "Requested index should be cleared!");
 
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
     
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug690056_window.xul
+++ b/docshell/test/chrome/bug690056_window.xul
@@ -13,17 +13,17 @@
   <script type="application/javascript"><![CDATA[
     var tests = testIterator();
 
     function nextTest() {
       tests.next();
     }
 
     // Makes sure that we fire the visibilitychange events
-    function testIterator() {
+    function* testIterator() {
       // Enable bfcache
       enableBFCache(8);
 
       // Load something for a start
       doPageNavigation({
         uri: 'data:text/html,<title>initial load</title>',
         onNavComplete: nextTest
       });
@@ -160,17 +160,15 @@
                           { type: "pageshow",
                             title: "new load",
                             persisted: true },
                         ],
         onNavComplete: nextTest
       });
       yield undefined;
 
-      // Tell the framework the test is finished.  Include the final 'yield' 
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;      
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug89419_window.xul
+++ b/docshell/test/chrome/bug89419_window.xul
@@ -30,17 +30,17 @@
     }
 
     ////
     // Generator function for test steps for bug 89419:
     // A visited link should have the :visited style applied
     // to it when displayed on a page which was fetched from
     // the bfcache.
     //
-    function testIterator()
+    function* testIterator()
     {
       // Load a test page containing an image referring to the sjs that returns
       // a different redirect every time it's loaded.
       doPageNavigation({
         uri: getHttpUrl("89419.html"),
         onNavComplete: nextTest,
         preventBFCache: true
       });
@@ -65,18 +65,16 @@
         onNavComplete: nextTest
       });
       yield undefined;
 
       var third = snapshotWindow(TestWindow.getWindow());
       ok(!snapshotsEqual(third, second), "going back should not be the same as about:blank");
       ok(snapshotsEqual(first, third), "going back should be the same as the initial load");
 
-      // Tell the framework the test is finished.  Include the final 'yield'
-      // statement to prevent a StopIteration exception from being thrown.
+      // Tell the framework the test is finished.
       finish();
-      yield undefined;
     }
 
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/bug92598_window.xul
+++ b/docshell/test/chrome/bug92598_window.xul
@@ -71,24 +71,20 @@
       }
 
       if (gExpected.length == 0) {
         setTimeout(nextTest, 0);
       }
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err if err instanceof StopIteration) {
-        finish();
-      }
+      gTestsIterator.next();
     }
 
-    function testsIterator() {
+    function* testsIterator() {
       // Load a page with a no-cache header, followed by a simple page
       // On pagehide, first page should report it is not being persisted
       var test1DocURI = "http://mochi.test:8888/tests/docshell/test/chrome/92598_nostore.html";
 
       gExpected = [{type: "pagehide", persisted: true},
                    {type: "load", title: "test1"},
                    {type: "pageshow", title: "test1", persisted: false}];
       gBrowser.loadURI(test1DocURI);
@@ -106,13 +102,15 @@
 
       // Now go back in history. First page should not have been cached.
       // Check persisted property to confirm
       gExpected = [{type: "pagehide", title: "test2", persisted: true},
                    {type: "load", title: "test1"},
                    {type: "pageshow", title: "test1", persisted: false}];
       gBrowser.goBack();
       yield undefined;
+
+      finish();
     }
   ]]></script>
 
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
 </window>
--- a/docshell/test/chrome/test_bug453650.xul
+++ b/docshell/test/chrome/test_bug453650.xul
@@ -18,33 +18,35 @@ https://bugzilla.mozilla.org/show_bug.cg
   SimpleTest.waitForExplicitFinish();
 
   var Ci = Components.interfaces;
   var Cr = Components.results;
 
   var iter = runTests();
   nextTest();
 
-  function runTests() {
+  function* runTests() {
     var iframe = document.createElement("iframe");
     iframe.style.width = "300px";
     iframe.style.height = "300px";
     iframe.setAttribute("src", "data:text/html,<h1 id='h'>hello</h1>");
 
     document.documentElement.appendChild(iframe);
     yield whenLoaded(iframe);
     info("iframe loaded");
 
     var h1 = iframe.contentDocument.getElementById("h");
     h1.style.width = "400px";
     yield waitForInterruptibleReflow(iframe.docShell);
 
     h1.style.width = "300px";
     waitForReflow(iframe.docShell);
     yield is(300, h1.offsetWidth, "h1 has correct width");
+
+    SimpleTest.finish();
   }
 
   function waitForInterruptibleReflow(docShell) {
     waitForReflow(docShell, true);
   }
 
   function waitForReflow(docShell, interruptible = false) {
     function done() {
@@ -92,21 +94,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   function whenLoaded(iframe) {
     iframe.addEventListener("load", function onLoad() {
       iframe.removeEventListener("load", onLoad);
       SimpleTest.executeSoon(nextTest);
     });
   }
 
   function nextTest() {
-    try {
-      iter.next();
-    } catch (e if e instanceof StopIteration) {
-      SimpleTest.finish();
-    }
+    iter.next();
   }
 
   ]]>
   </script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=453650"
--- a/docshell/test/chrome/test_bug608669.xul
+++ b/docshell/test/chrome/test_bug608669.xul
@@ -45,17 +45,17 @@ SimpleTest.waitForExplicitFinish();
 addLoadEvent(nextTest);
 
 gen = doTest();
 
 function nextTest() {
   gen.next();
 }
 
-function doTest() {
+function* doTest() {
   var container = document.getElementById('container');
 
   setCachePref(true);
 
   var notificationCount = 0;
   var observer = {
     observe: function(aSubject, aTopic, aData) {
       is(aTopic, "chrome-document-global-created",
@@ -100,17 +100,16 @@ function doTest() {
   yield undefined;
   is(notificationCount, 3, "after window.open load");
   popup.close();
 
   setCachePref(false);
   os.removeObserver(observer, "chrome-document-global-created");
   os.removeObserver(observer, "content-document-global-created");
   SimpleTest.finish();
-  yield undefined;
 }
 
 
 
   ]]></script>
   <vbox id="container" flex="1">
     <description>Below will an iframe be added</description>
   </vbox>
--- a/docshell/test/test_bfcache_plus_hash.html
+++ b/docshell/test/test_bfcache_plus_hash.html
@@ -42,19 +42,16 @@ var expectedLoadNum = -1;
 function childLoad(n) {
   if (n == expectedLoadNum) {
     debug('Got load ' + n);
     expectedLoadNum = -1;
 
     // Spin the event loop before calling gGen.next() so the generator runs
     // outside the onload handler.  This prevents us from encountering all
     // sorts of docshell quirks.
-    //
-    // (I don't know why I need to wrap gGen.next() in a function, but it
-    // throws an error otherwise.)
     setTimeout(function() { gGen.next() }, 0);
   }
   else {
     debug('Got unexpected load ' + n);
     ok(false, 'Got unexpected load ' + n);
   }
 }
 
@@ -79,17 +76,17 @@ function waitForLoad(n) {
   expectedLoadNum = n;
 }
 
 function waitForShow(n) {
   debug('Waiting for show ' + n);
   expectedPageshowNum = n;
 }
 
-function test() {
+function* test() {
   var popup = window.open('data:text/html,' +
                           '<html><body onload="opener.childLoad(1)" ' +
                                       'onpageshow="opener.childPageshow(1)">' +
                                 'Popup 1' +
                                 '</body></html>');
   waitForLoad(1);
   yield undefined;
 
@@ -101,19 +98,16 @@ function test() {
 
   // Now go back 2.  The first page should be retrieved from bfcache.
   popup.history.go(-2);
   waitForShow(1);
   yield undefined;
 
   popup.close();
   SimpleTest.finish();
-  
-  // Yield once more so we don't throw a StopIteration exception.
-  yield undefined;
 }
 
 var gGen = test();
 gGen.next();
 
 </script>
 </pre>
 </body>
--- a/docshell/test/test_bug385434.html
+++ b/docshell/test/test_bug385434.html
@@ -192,17 +192,16 @@ function run_test() {
    *     not "complete" (bug 504837).
    */
   frameCw.document.location = "file_bug385434_3.html";
   yield undefined;
   eventExpected("Hashchange should fire even if the document " +
                 "hasn't finished loading.");
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 var gGen = run_test();
 gGen.next();
 
 </script>
 </pre>
 </body>
--- a/docshell/test/test_bug475636.html
+++ b/docshell/test/test_bug475636.html
@@ -22,29 +22,29 @@ Test that refresh to data: URIs don't in
 
 SimpleTest.waitForExplicitFinish();
 
 gen = runTests();
 
 window.private = 42;
 
 window.addEventListener("message", function(e) {
-  gen.send(e.data);
+  gen.next(e.data);
 }, false);
 
 var url = "file_bug475636.sjs?";
 
-function runTests() {
+function* runTests() {
   var loader = document.getElementById('loader');
   for (var testNum = 1; ; ++testNum) {
     loader.src = url + testNum;
     let res = (yield);
     if (res == "done") {
       SimpleTest.finish();
-      yield undefined;
+      return;
     }
     is(res, "pass");
   }
 }
 
 
 </script>
 </pre>
--- a/docshell/test/test_bug509055.html
+++ b/docshell/test/test_bug509055.html
@@ -35,17 +35,17 @@ function onChildHashchange(e) {
     gGen.next();
 }
 
 function onChildLoad(e) {
   if(gGen)
     gGen.next();
 }
 
-function runTest() {
+function* runTest() {
   var popup = window.open("file_bug509055.html", "popup 0",
                            "height=200,width=200,location=yes," +
                            "menubar=yes,status=yes,toolbar=yes,dependent=yes");
   popup.hashchangeCallback = onChildHashchange;
   popup.onload = onChildLoad;
   dump('Waiting for initial load.\n');
   yield undefined;
 
@@ -77,18 +77,16 @@ function runTest() {
 
   // Get the title of the inner popup's current SHEntry 
   var sheTitle = sh.getEntryAtIndex(sh.index, false).title;
   is(sheTitle, "Changed", "SHEntry's title should change when we change.");
 
   popup.close();
 
   SimpleTest.executeSoon(SimpleTest.finish);
-  dump('Final yield.\n');
-  yield undefined;
 }
 
 window.addEventListener('load', function() {
   gGen = runTest();
   gGen.next();
 }, false);
 
 </script>
--- a/docshell/test/test_bug640387_1.html
+++ b/docshell/test/test_bug640387_1.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=640387">Mozilla Bug 640387</a>
 
 <script type='application/javascript;version=1.7'>
 SimpleTest.waitForExplicitFinish();
 
-function test() {
+function* test() {
   /* Spin the event loop so we get out of the onload handler. */
   SimpleTest.executeSoon(function() { gGen.next() });
   yield undefined;
 
   popup.history.pushState('', '', '#hash1');
   popup.history.pushState('', '', '#hash2');
 
   // Now the history looks like:
@@ -66,17 +66,16 @@ function test() {
   gCallbackOnPopstate = false;
 
   // Spin the event loop so hashchange has a chance to fire, if it's going to.
   SimpleTest.executeSoon(function() { gGen.next() });
   yield undefined;
 
   popup.close();
   SimpleTest.finish();
-  yield undefined;
 }
 
 gGen = null;
 function childLoad() {
   gGen = test();
   gGen.next();
 }
 
--- a/docshell/test/test_bug640387_2.html
+++ b/docshell/test/test_bug640387_2.html
@@ -40,17 +40,17 @@ function childHashchange() {
     ok(false, 'Got unexpected hashchange.');
   }
   if (callbackOnHashchange) {
     callbackOnHashchange = false;
     gGen.next();
   }
 }
 
-function run_test() {
+function* run_test() {
   var iframe = $('iframe').contentWindow;
 
   ok(true, 'Got first load');
 
   // Spin the event loop so we exit the onload handler.
   SimpleTest.executeSoon(function() { gGen.next() });
   yield undefined;
 
@@ -71,17 +71,16 @@ function run_test() {
 
   ok(true, 'Got final load.');
 
   // Spin the event loop to give hashchange a chance to fire, if it's going to.
   SimpleTest.executeSoon(function() { gGen.next() });
   yield undefined;
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 callbackOnLoad = true;
 gGen = run_test();
 
 </script>
 
 <iframe id='iframe' src='file_bug640387.html'></iframe>
--- a/docshell/test/test_bug669671.html
+++ b/docshell/test/test_bug669671.html
@@ -60,17 +60,17 @@ function checkPopupLoadCount()
   // because we didn't do a load we should have, let's not cause a cascade of
   // failures by incrementing _loadCount.
   var origCount = _loadCount;
   if (popup.document.body.innerHTML >= _loadCount + '')
     _loadCount++;
   return origCount;
 }
 
-function test()
+function* test()
 {
   // Step 0 - Make sure the count is reset to 0 in case of reload
   popup.location = 'file_bug669671.sjs?countreset';
   yield;
   is(popup.document.body.innerHTML, '0',
      'Load count should be reset to 0');
 
   // Step 1 - The popup's body counts how many times we've requested the
@@ -127,17 +127,16 @@ function test()
   checkPopupLoadCount();
   SpecialPowers.wrap(popup).back();
   yield undefined;
   is(popup.document.body.innerHTML, initialCount + '',
      'Load count (should be cached)');
   popup.close();
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 // This will call into onChildLoad once it loads.
 var popup = window.open('file_bug669671.sjs?pushed');
 
 var gGen = test();
 
 </script>
--- a/dom/archivereader/test/helpers.js
+++ b/dom/archivereader/test/helpers.js
@@ -1,31 +1,27 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-var testGenerator;
-
 function runTest()
 {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({'set': [ ["dom.archivereader.enabled", true] ]}, function() {
     SpecialPowers.createFiles(filesToCreate(),
                               function (files) {
-                                testGenerator = testSteps(files);
-                                return testGenerator.next();
+                                testSteps(files);
                               },
                               function (msg) {
                                 ok(false, "File creation error: " + msg);
                                 finishTest();
                               });
   });
 }
 
 function finishTest()
 {
   SpecialPowers.popPrefEnv(function() {
-    testGenerator.close();
     SimpleTest.finish();
   });
 }
--- a/dom/archivereader/test/test_basic.html
+++ b/dom/archivereader/test/test_basic.html
@@ -207,17 +207,16 @@
       is(this.result[1].type, "text/plain", "ArchiveReader.getFile('test/a.txt') the type MUST be 'text/plain'");
 
       markTestDone();
     }
     handle5.onerror = function() {
       ok(false, "ArchiveReader.getFiles() should not return an 'error'");
       markTestDone();
     }
-    yield undefined;
   }
 
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 </head>
 
 <body onload="runTest();">
 <p id="display">
--- a/dom/archivereader/test/test_nonUnicode.html
+++ b/dom/archivereader/test/test_nonUnicode.html
@@ -46,17 +46,16 @@
       ok(e instanceof RangeError, "Expected a RangeError");
     }
     finishTest();
   }
 
   function testSteps(files)
   {
     test1(files[0]);
-    yield undefined;
   }
 
 
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
--- a/dom/archivereader/test/test_zip_in_zip.html
+++ b/dom/archivereader/test/test_zip_in_zip.html
@@ -20,18 +20,16 @@
   }
 
   function testSteps(files)
   {
     var binaryFile = files[0];
 
     // The input is 4 nested zip archives:
     doLoop(binaryFile, 4);
-
-    yield undefined;
   }
 
   function doLoop(blob, loop)
   {
     var r = new ArchiveReader(blob);
     isnot(r, null, "ArchiveReader cannot be null");
 
     // GetFilename
--- a/dom/base/Dispatcher.cpp
+++ b/dom/base/Dispatcher.cpp
@@ -1,49 +1,55 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozilla/dom/Dispatcher.h"
 #include "mozilla/Move.h"
+#include "nsINamed.h"
 
 using namespace mozilla;
 
 nsresult
 DispatcherTrait::Dispatch(const char* aName,
                           TaskCategory aCategory,
                           already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(aRunnable));
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  if (aName) {
+    if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
+      named->SetName(aName);
+    }
+  }
+  if (NS_IsMainThread()) {
+    return NS_DispatchToCurrentThread(runnable.forget());
+  } else {
+    return NS_DispatchToMainThread(runnable.forget());
+  }
 }
 
 already_AddRefed<nsIEventTarget>
-DispatcherTrait::CreateEventTarget(const char* aName,
-                                   TaskCategory aCategory)
+DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
 {
   nsCOMPtr<nsIEventTarget> main = do_GetMainThread();
   return main.forget();
 }
 
 namespace {
 
 class DispatcherEventTarget final : public nsIEventTarget
 {
   RefPtr<dom::Dispatcher> mDispatcher;
-  const char* mName;
   TaskCategory mCategory;
 
 public:
-  DispatcherEventTarget(dom::Dispatcher* aDispatcher,
-                        const char* aName,
-                        TaskCategory aCategory)
+  DispatcherEventTarget(dom::Dispatcher* aDispatcher, TaskCategory aCategory)
    : mDispatcher(aDispatcher)
-   , mName(aName)
    , mCategory(aCategory)
   {}
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
 
   dom::Dispatcher* Dispatcher() const { return mDispatcher; }
 
@@ -62,31 +68,31 @@ DispatcherEventTarget::DispatchFromScrip
 }
 
 NS_IMETHODIMP
 DispatcherEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
 {
   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
     return NS_ERROR_UNEXPECTED;
   }
-  return mDispatcher->Dispatch(mName, mCategory, Move(aRunnable));
+  return mDispatcher->Dispatch(nullptr, mCategory, Move(aRunnable));
 }
 
 NS_IMETHODIMP
 DispatcherEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 DispatcherEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
 {
   *aIsOnCurrentThread = NS_IsMainThread();
   return NS_OK;
 }
 
 already_AddRefed<nsIEventTarget>
-Dispatcher::CreateEventTarget(const char* aName, TaskCategory aCategory)
+Dispatcher::CreateEventTargetFor(TaskCategory aCategory)
 {
   RefPtr<DispatcherEventTarget> target =
-    new DispatcherEventTarget(this, aName, aCategory);
+    new DispatcherEventTarget(this, aCategory);
   return target.forget();
 }
--- a/dom/base/Dispatcher.h
+++ b/dom/base/Dispatcher.h
@@ -29,16 +29,18 @@ enum class TaskCategory {
   // requestIdleCallback
   IdleCallback,
 
   // Vsync notifications
   RefreshDriver,
 
   // Most DOM events (postMessage, media, plugins)
   Other,
+
+  Count
 };
 
 // This trait should be attached to classes like nsIGlobalObject and nsIDocument
 // that have a DocGroup or TabGroup attached to them. The methods here should
 // delegate to the DocGroup or TabGroup. We can't use the Dispatcher class
 // directly because it inherits from nsISupports.
 class DispatcherTrait {
 public:
@@ -47,29 +49,33 @@ public:
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable);
 
   // This method may or may not be safe off of the main thread. For nsIDocument
   // it is safe. For nsIGlobalWindow it is not safe. The nsIEventTarget can
   // always be used off the main thread.
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, TaskCategory aCategory);
+  EventTargetFor(TaskCategory aCategory) const;
 };
 
 // Base class for DocGroup and TabGroup.
 class Dispatcher : public nsISupports {
 public:
   // This method is always safe to call off the main thread.
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) = 0;
 
   // This method is always safe to call off the main thread. The nsIEventTarget
   // can always be used off the main thread.
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, TaskCategory aCategory);
+  EventTargetFor(TaskCategory aCategory) const = 0;
+
+protected:
+  virtual already_AddRefed<nsIEventTarget>
+  CreateEventTargetFor(TaskCategory aCategory);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Dispatcher_h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -55,13 +55,19 @@ DocGroup::~DocGroup()
 
 NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
 
 nsresult
 DocGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(aRunnable));
+  return mTabGroup->Dispatch(aName, aCategory, Move(aRunnable));
+}
+
+already_AddRefed<nsIEventTarget>
+DocGroup::EventTargetFor(TaskCategory aCategory) const
+{
+  return mTabGroup->EventTargetFor(aCategory);
 }
 
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -64,16 +64,19 @@ public:
   {
     return mDocuments.end();
   }
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
+  virtual already_AddRefed<nsIEventTarget>
+  EventTargetFor(TaskCategory aCategory) const override;
+
 private:
   DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
   ~DocGroup();
 
   nsCString mKey;
   RefPtr<TabGroup> mTabGroup;
   nsTArray<nsIDocument*> mDocuments;
 };
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/OffscreenCanvas.h"
 #include "mozilla/dom/OffscreenCanvasBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
@@ -33,17 +34,16 @@
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "MultipartBlobImpl.h"
-#include "nsIRemoteBlob.h"
 #include "nsQueryObject.h"
 
 #ifdef MOZ_WEBRTC
 #include "mozilla/dom/RTCCertificate.h"
 #include "mozilla/dom/RTCCertificateBinding.h"
 #endif
 
 using namespace mozilla::ipc;
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -16,17 +16,23 @@
 #include "nsIURI.h"
 
 namespace mozilla {
 namespace dom {
 
 static StaticRefPtr<TabGroup> sChromeTabGroup;
 
 TabGroup::TabGroup(bool aIsChrome)
+ : mLastWindowLeft(false)
 {
+  for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
+    TaskCategory category = static_cast<TaskCategory>(i);
+    mEventTargets[i] = CreateEventTargetFor(category);
+  }
+
   // Do not throttle runnables from chrome windows.  In theory we should
   // not have abuse issues from these windows and many browser chrome
   // tests have races that fail if we do throttle chrome runnables.
   if (aIsChrome) {
     MOZ_ASSERT(!sChromeTabGroup);
     return;
   }
 
@@ -94,16 +100,28 @@ TabGroup::Join(nsPIDOMWindowOuter* aWind
   return tabGroup.forget();
 }
 
 void
 TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
 {
   MOZ_ASSERT(mWindows.Contains(aWindow));
   mWindows.RemoveElement(aWindow);
+
+  if (mWindows.IsEmpty()) {
+    mLastWindowLeft = true;
+
+    // There is a RefPtr cycle TabGroup -> DispatcherEventTarget -> TabGroup. To
+    // avoid leaks, we need to break the chain somewhere. We shouldn't be using
+    // the ThrottledEventQueue for this TabGroup when no windows belong to it,
+    // so it's safe to null out the queue here.
+    for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
+      mEventTargets[i] = nullptr;
+    }
+  }
 }
 
 nsresult
 TabGroup::FindItemWithName(const nsAString& aName,
                            nsIDocShellTreeItem* aRequestor,
                            nsIDocShellTreeItem* aOriginalRequestor,
                            nsIDocShellTreeItem** aFoundItem)
 {
@@ -167,13 +185,31 @@ TabGroup::HashEntry::HashEntry(const nsA
   : nsCStringHashKey(aKey), mDocGroup(nullptr)
 {}
 
 nsresult
 TabGroup::Dispatch(const char* aName,
                    TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return NS_DispatchToMainThread(Move(aRunnable));
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  if (aName) {
+    if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
+      named->SetName(aName);
+    }
+  }
+  if (NS_IsMainThread()) {
+    return NS_DispatchToCurrentThread(runnable.forget());
+  } else {
+    return NS_DispatchToMainThread(runnable.forget());
+  }
+}
+
+already_AddRefed<nsIEventTarget>
+TabGroup::EventTargetFor(TaskCategory aCategory) const
+{
+  MOZ_RELEASE_ASSERT(!mLastWindowLeft);
+  nsCOMPtr<nsIEventTarget> target = mEventTargets[size_t(aCategory)];
+  return target.forget();
 }
 
 }
 }
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -105,19 +105,24 @@ public:
   // the main thread.  This may return nullptr during browser shutdown.
   ThrottledEventQueue*
   GetThrottledEventQueue() const;
 
   virtual nsresult Dispatch(const char* aName,
                             TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
+  virtual already_AddRefed<nsIEventTarget>
+  EventTargetFor(TaskCategory aCategory) const override;
+
 private:
   ~TabGroup();
   DocGroupMap mDocGroups;
+  bool mLastWindowLeft;
   nsTArray<nsPIDOMWindowOuter*> mWindows;
   RefPtr<ThrottledEventQueue> mThrottledEventQueue;
+  nsCOMPtr<nsIEventTarget> mEventTargets[size_t(TaskCategory::Count)];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // defined(TabGroup_h)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -6,21 +6,19 @@
 
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'mozIDOMWindow.idl',
     'nsIContentPolicy.idl',
     'nsIContentPolicyBase.idl',
     'nsIDocumentEncoder.idl',
-    'nsIDOMBlob.idl',
     'nsIDOMDataChannel.idl',
     'nsIDOMDOMCursor.idl',
     'nsIDOMDOMRequest.idl',
-    'nsIDOMFileList.idl',
     'nsIDOMFormData.idl',
     'nsIDOMParser.idl',
     'nsIDOMSerializer.idl',
     'nsIDroppedLinkHandler.idl',
     'nsIFrameLoader.idl',
     'nsIImageLoadingContent.idl',
     'nsIMessageManager.idl',
     'nsIObjectLoadingContent.idl',
@@ -73,18 +71,16 @@ EXPORTS += [
     'nsDOMJSUtils.h',
     'nsDOMNavigationTiming.h',
     'nsDOMString.h',
     'nsFocusManager.h',
     'nsFrameMessageManager.h',
     'nsGenericDOMDataNode.h',
     'nsGkAtomList.h',
     'nsGkAtoms.h',
-    'nsHostObjectProtocolHandler.h',
-    'nsHostObjectURI.h',
     'nsIAnimationObserver.h',
     'nsIAttribute.h',
     'nsIContent.h',
     'nsIContentInlines.h',
     'nsIContentIterator.h',
     'nsIContentSerializer.h',
     'nsIDocument.h',
     'nsIDocumentInlines.h',
@@ -146,17 +142,16 @@ EXPORTS.mozilla += [
     'UseCounter.h',
 ]
 
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
     'AnonymousContent.h',
     'Attr.h',
     'BarProps.h',
-    'BlobSet.h',
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
@@ -175,32 +170,27 @@ EXPORTS.mozilla.dom += [
     'DOMQuad.h',
     'DOMRect.h',
     'DOMRequest.h',
     'DOMStringList.h',
     'DOMTokenListSupportedTokens.h',
     'Element.h',
     'ElementInlines.h',
     'EventSource.h',
-    'File.h',
-    'FileList.h',
-    'FileReader.h',
     'FormData.h',
     'FragmentOrElement.h',
     'FromParser.h',
     'GroupedSHistory.h',
     'IdleDeadline.h',
     'IdleRequest.h',
     'ImageEncoder.h',
     'ImageTracker.h',
     'ImportManager.h',
     'Link.h',
     'Location.h',
-    'MutableBlobStorage.h',
-    'MutableBlobStreamListener.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'PartialSHistory.h',
     'Pose.h',
     'ProcessGlobal.h',
@@ -221,17 +211,16 @@ EXPORTS.mozilla.dom += [
     'WebSocket.h',
     'WindowOrientationObserver.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnonymousContent.cpp',
     'Attr.cpp',
     'BarProps.cpp',
-    'BlobSet.cpp',
     'BodyUtil.cpp',
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
@@ -248,32 +237,26 @@ UNIFIED_SOURCES += [
     'DOMParser.cpp',
     'DOMPoint.cpp',
     'DOMQuad.cpp',
     'DOMRect.cpp',
     'DOMRequest.cpp',
     'DOMStringList.cpp',
     'Element.cpp',
     'EventSource.cpp',
-    'File.cpp',
-    'FileList.cpp',
-    'FileReader.cpp',
     'FormData.cpp',
     'FragmentOrElement.cpp',
     'GroupedSHistory.cpp',
     'IdleDeadline.cpp',
     'IdleRequest.cpp',
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
     'ImportManager.cpp',
     'Link.cpp',
     'Location.cpp',
-    'MultipartBlobImpl.cpp',
-    'MutableBlobStorage.cpp',
-    'MutableBlobStreamListener.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
@@ -297,18 +280,16 @@ UNIFIED_SOURCES += [
     'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsFrameLoader.cpp',
     'nsGenConImageContent.cpp',
     'nsGenericDOMDataNode.cpp',
     'nsGkAtoms.cpp',
     'nsGlobalWindowCommands.cpp',
     'nsHistory.cpp',
-    'nsHostObjectProtocolHandler.cpp',
-    'nsHostObjectURI.cpp',
     'nsHTMLContentSerializer.cpp',
     'nsIGlobalObject.cpp',
     'nsINode.cpp',
     'nsInProcessTabChildGlobal.cpp',
     'nsJSEnvironment.cpp',
     'nsJSTimeoutHandler.cpp',
     'nsJSUtils.cpp',
     'nsLineBreaker.cpp',
@@ -427,16 +408,17 @@ LOCAL_INCLUDES += [
     '../battery',
     '../events',
     '../media',
     '../network',
     '../time',
     '/caps',
     '/docshell/base',
     '/dom/base',
+    '/dom/file',
     '/dom/geolocation',
     '/dom/html',
     '/dom/ipc',
     '/dom/storage',
     '/dom/svg',
     '/dom/u2f',
     '/dom/workers',
     '/dom/xbl',
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -10,16 +10,19 @@
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "prtime.h"
 #include "nsIURI.h"
 #include "nsPrintfCString.h"
 #include "mozilla/dom/PerformanceNavigation.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
+
+using namespace mozilla;
 
 nsDOMNavigationTiming::nsDOMNavigationTiming()
 {
   Clear();
 }
 
 nsDOMNavigationTiming::~nsDOMNavigationTiming()
 {
@@ -47,35 +50,36 @@ nsDOMNavigationTiming::Clear()
   mDOMInteractiveSet = false;
   mDOMContentLoadedEventStartSet = false;
   mDOMContentLoadedEventEndSet = false;
   mDOMCompleteSet = false;
   mDocShellHasBeenActiveSinceNavigationStart = false;
 }
 
 DOMTimeMilliSec
-nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const
+nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const
 {
   if (aStamp.IsNull()) {
     return 0;
   }
-  mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
+
+  TimeDuration duration = aStamp - mNavigationStartTimeStamp;
   return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
 }
 
 DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart()
 {
-  return TimeStampToDOM(mozilla::TimeStamp::Now());
+  return TimeStampToDOM(TimeStamp::Now());
 }
 
 void
 nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState)
 {
   mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
-  mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
+  mNavigationStartTimeStamp = TimeStamp::Now();
   mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive);
 }
 
 void
 nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType)
 {
   mNavigationType = aNavigationType;
   // At the unload event time we don't really know the loading uri.
@@ -122,17 +126,17 @@ nsDOMNavigationTiming::NotifyLoadEventEn
 {
   if (!mLoadEventEndSet) {
     mLoadEventEnd = DurationFromStart();
     mLoadEventEndSet = true;
   }
 }
 
 void
-nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue)
+nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, TimeStamp aValue)
 {
   if (!mDOMLoadingSet) {
     mLoadedURI = aURI;
     mDOMLoading = TimeStampToDOM(aValue);
     mDOMLoadingSet = true;
   }
 }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2863,17 +2863,17 @@ nsDocument::SetPrincipal(nsIPrincipal *a
   // same docgroup as another document, so this should not be a problem.
   if (aNewPrincipal) {
     GetDocGroup();
   }
 #endif
 }
 
 mozilla::dom::DocGroup*
-nsIDocument::GetDocGroup()
+nsIDocument::GetDocGroup() const
 {
 #ifdef DEBUG
   // Sanity check that we have an up-to-date and accurate docgroup
   if (mDocGroup) {
     nsAutoCString docGroupKey;
     mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
     MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
     // XXX: Check that the TabGroup is correct as well!
@@ -2891,22 +2891,22 @@ nsIDocument::Dispatch(const char* aName,
   // Note that this method may be called off the main thread.
   if (mDocGroup) {
     return mDocGroup->Dispatch(aName, aCategory, Move(aRunnable));
   }
   return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
 }
 
 already_AddRefed<nsIEventTarget>
-nsIDocument::CreateEventTarget(const char* aName, TaskCategory aCategory)
+nsIDocument::EventTargetFor(TaskCategory aCategory) const
 {
   if (mDocGroup) {
-    return mDocGroup->CreateEventTarget(aName, aCategory);
-  }
-  return DispatcherTrait::CreateEventTarget(aName, aCategory);
+    return mDocGroup->EventTargetFor(aCategory);
+  }
+  return DispatcherTrait::EventTargetFor(aCategory);
 }
 
 NS_IMETHODIMP
 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
 {
   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
 
   return NS_OK;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3027,17 +3027,21 @@ nsGlobalWindow::PreloadLocalStorage()
   nsresult rv;
 
   nsCOMPtr<nsIDOMStorageManager> storageManager =
     do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  storageManager->PrecacheStorage(principal);
+  // private browsing windows do not persist local storage to disk so we should
+  // only try to precache storage when we're not a private browsing window.
+  if (principal->GetPrivateBrowsingId() == 0) {
+    storageManager->PrecacheStorage(principal);
+  }
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
   MOZ_ASSERT(IsOuterWindow());
 
   if (!mDoc) {
@@ -10785,16 +10789,18 @@ nsGlobalWindow::GetSessionStorage(ErrorR
     nsresult rv;
 
     nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
     if (NS_FAILED(rv)) {
       aError.Throw(rv);
       return nullptr;
     }
 
+    MOZ_DIAGNOSTIC_ASSERT((principal->GetPrivateBrowsingId() > 0) == IsPrivateBrowsing());
+
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mSessionStorage = static_cast<DOMStorage*>(storage.get());
@@ -10849,16 +10855,18 @@ nsGlobalWindow::GetLocalStorage(ErrorRes
     nsString documentURI;
     if (mDoc) {
       aError = mDoc->GetDocumentURI(documentURI);
       if (NS_WARN_IF(aError.Failed())) {
         return nullptr;
       }
     }
 
+    MOZ_DIAGNOSTIC_ASSERT((principal->GetPrivateBrowsingId() > 0) == IsPrivateBrowsing());
+
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = static_cast<DOMStorage*>(storage.get());
@@ -14993,17 +15001,17 @@ nsPIDOMWindow<T>::TabGroup()
   if (IsInnerWindow()) {
     return globalWindow->TabGroupInner();
   }
   return globalWindow->TabGroupOuter();
 }
 
 template<typename T>
 mozilla::dom::DocGroup*
-nsPIDOMWindow<T>::GetDocGroup()
+nsPIDOMWindow<T>::GetDocGroup() const
 {
   nsIDocument* doc = GetExtantDoc();
   if (doc) {
     return doc->GetDocGroup();
   }
   return nullptr;
 }
 
@@ -15015,24 +15023,23 @@ nsGlobalWindow::Dispatch(const char* aNa
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
     return GetDocGroup()->Dispatch(aName, aCategory, Move(aRunnable));
   }
   return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
 }
 
 already_AddRefed<nsIEventTarget>
-nsGlobalWindow::CreateEventTarget(const char* aName,
-                                  TaskCategory aCategory)
+nsGlobalWindow::EventTargetFor(TaskCategory aCategory) const
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
-    return GetDocGroup()->CreateEventTarget(aName, aCategory);
-  }
-  return DispatcherTrait::CreateEventTarget(aName, aCategory);
+    return GetDocGroup()->EventTargetFor(aCategory);
+  }
+  return DispatcherTrait::EventTargetFor(aCategory);
 }
 
 nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
   nsGlobalWindow* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   MOZ_ASSERT(aWindow);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1726,17 +1726,17 @@ private:
 
 public:
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::dom::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName, mozilla::dom::TaskCategory aCategory) override;
+  EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
 
 protected:
   // These members are only used on outer window objects. Make sure
   // you never set any of these on an inner object!
   bool                          mFullScreen : 1;
   bool                          mFullscreenMode : 1;
   bool                          mIsClosed : 1;
   bool                          mInClose : 1;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2851,35 +2851,34 @@ public:
   bool InlineScriptAllowedByCSP();
 
   void ReportHasScrollLinkedEffect();
   bool HasScrollLinkedEffect() const
   {
     return mHasScrollLinkedEffect;
   }
 
-  mozilla::dom::DocGroup* GetDocGroup();
+  mozilla::dom::DocGroup* GetDocGroup() const;
 
   virtual void AddIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
   virtual void RemoveIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
   
   virtual void UpdateIntersectionObservations() = 0;
   virtual void ScheduleIntersectionObserverNotification() = 0;
   virtual void NotifyIntersectionObservers() = 0;
 
   // Dispatch a runnable related to the document.
   virtual nsresult Dispatch(const char* aName,
                             mozilla::dom::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual already_AddRefed<nsIEventTarget>
-  CreateEventTarget(const char* aName,
-                    mozilla::dom::TaskCategory aCategory) override;
+  EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
 
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -190,18 +190,16 @@ public:
   virtual bool IsRunningTimeout() = 0;
 
 protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
 public:
-  inline bool IsLoadingOrRunningTimeout() const;
-
   // Check whether a document is currently loading
   inline bool IsLoading() const;
   inline bool IsHandlingResizeEvent() const;
 
   // Set the window up with an about:blank document with the current subject
   // principal.
   // Outer windows only.
   virtual void SetInitialPrincipalToSubject() = 0;
@@ -575,17 +573,17 @@ public:
   virtual nsresult Focus() = 0;
   virtual nsresult Close() = 0;
 
   virtual nsresult MoveBy(int32_t aXDif, int32_t aYDif) = 0;
   virtual nsresult UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason) = 0;
 
   mozilla::dom::TabGroup* TabGroup();
 
-  mozilla::dom::DocGroup* GetDocGroup();
+  mozilla::dom::DocGroup* GetDocGroup() const;
 
   virtual mozilla::ThrottledEventQueue* GetThrottledEventQueue() = 0;
 
 protected:
   // The nsPIDOMWindow constructor. The aOuterWindow argument should
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
--- a/dom/base/nsPIDOMWindowInlines.h
+++ b/dom/base/nsPIDOMWindowInlines.h
@@ -31,26 +31,16 @@ const nsPIDOMWindowOuter*
 nsPIDOMWindow<T>::AsOuter() const
 {
   MOZ_ASSERT(IsOuterWindow());
   return reinterpret_cast<const nsPIDOMWindowOuter*>(this);
 }
 
 template <class T>
 bool
-nsPIDOMWindow<T>::IsLoadingOrRunningTimeout() const
-{
-  if (IsOuterWindow()) {
-    return AsOuter()->GetCurrentInnerWindow()->IsLoadingOrRunningTimeout();
-  }
-  return !mIsDocumentLoaded || mRunningTimeout;
-}
-
-template <class T>
-bool
 nsPIDOMWindow<T>::IsLoading() const
 {
   if (IsOuterWindow()) {
     auto* win = AsOuter()->GetCurrentInnerWindow();
 
     if (!win) {
       NS_ERROR("No current inner window available!");
 
--- a/dom/base/test/browser_use_counters.js
+++ b/dom/base/test/browser_use_counters.js
@@ -78,17 +78,17 @@ add_task(function* () {
   yield check_use_counter_iframe("file_use_counter_svg_list_style_image.html",
                                  "PROPERTY_FILL");
 });
 
 add_task(function* () {
   let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
   Telemetry.canRecordExtended = gOldParentCanRecord;
 
-  yield ContentTask.spawn(gBrowser.selectedBrowser, { oldCanRecord: gOldContentCanRecord }, function (arg) {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, { oldCanRecord: gOldContentCanRecord }, function* (arg) {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
     yield new Promise(resolve => {
       let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
       telemetry.canRecordExtended = arg.oldCanRecord;
       resolve();
     });
   });
 });
@@ -122,34 +122,34 @@ function grabHistogramsFromContent(use_c
     telemetry.getHistogramById("CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
     telemetry.getHistogramById("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum,
   ];
   return BrowserTestUtils.waitForCondition(() => {
     return page_before != telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum;
   }).then(gather, gather);
 }
 
-var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix, check_documents=true) {
+var check_use_counter_iframe = async function(file, use_counter_middlefix, check_documents=true) {
   info("checking " + file + " with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab( "about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
   let [histogram_page_before, histogram_document_before,
        histogram_docs_before, histogram_toplevel_docs_before] =
-      yield grabHistogramsFromContent(use_counter_middlefix);
+      await grabHistogramsFromContent(use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
-  yield waitForPageLoad(gBrowser.selectedBrowser);
+  await waitForPageLoad(gBrowser.selectedBrowser);
 
   // Inject our desired file into the iframe of the newly-loaded page.
-  yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
+  await ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
     let deferred = PromiseUtils.defer();
 
     let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
 
     let iframe = content.document.getElementById('content');
     iframe.src = opts.file;
     let listener = (event) => {
@@ -169,51 +169,51 @@ var check_use_counter_iframe = Task.asyn
   });
   
   // Tear down the page.
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
-  yield waitForDestroyedDocuments();
+  await waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
   let [histogram_page_after, histogram_document_after,
        histogram_docs_after, histogram_toplevel_docs_after] =
-      yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
+      await grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
 
   is(histogram_page_after, histogram_page_before + 1,
      "page counts for " + use_counter_middlefix + " after are correct");
   ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
      "top level document counts are correct");
   if (check_documents) {
     is(histogram_document_after, histogram_document_before + 1,
        "document counts for " + use_counter_middlefix + " after are correct");
   }
-});
+};
 
-var check_use_counter_img = Task.async(function* (file, use_counter_middlefix) {
+var check_use_counter_img = async function(file, use_counter_middlefix) {
   info("checking " + file + " as image with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab("about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
   let [histogram_page_before, histogram_document_before,
        histogram_docs_before, histogram_toplevel_docs_before] =
-      yield grabHistogramsFromContent(use_counter_middlefix);
+      await grabHistogramsFromContent(use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
-  yield waitForPageLoad(gBrowser.selectedBrowser);
+  await waitForPageLoad(gBrowser.selectedBrowser);
 
   // Inject our desired file into the img of the newly-loaded page.
-  yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
+  await ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function*(opts) {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
     let deferred = PromiseUtils.defer();
 
     let img = content.document.getElementById('display');
     img.src = opts.file;
     let listener = (event) => {
       img.removeEventListener("load", listener, true);
 
@@ -234,49 +234,49 @@ var check_use_counter_img = Task.async(f
   });
   
   // Tear down the page.
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
-  yield waitForDestroyedDocuments();
+  await waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
   let [histogram_page_after, histogram_document_after,
        histogram_docs_after, histogram_toplevel_docs_after] =
-      yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
+      await grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
   is(histogram_page_after, histogram_page_before + 1,
      "page counts for " + use_counter_middlefix + " after are correct");
   is(histogram_document_after, histogram_document_before + 1,
      "document counts for " + use_counter_middlefix + " after are correct");
   ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
      "top level document counts are correct");
   // 2 documents: one for the outer html page containing the <img> element, and
   // one for the SVG image itself.
   ok(histogram_docs_after >= histogram_docs_before + 2,
      "document counts are correct");
-});
+};
 
-var check_use_counter_direct = Task.async(function* (file, use_counter_middlefix, xfail=false) {
+var check_use_counter_direct = async function(file, use_counter_middlefix, xfail=false) {
   info("checking " + file + " with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab( "about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
   let [histogram_page_before, histogram_document_before,
        histogram_docs_before, histogram_toplevel_docs_before] =
-      yield grabHistogramsFromContent(use_counter_middlefix);
+      await grabHistogramsFromContent(use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + file);
-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
     yield new Promise(resolve => {
       let listener = () => {
         removeEventListener("load", listener, true);
 
         let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
         wu.forceUseCounterFlush(content.document);
 
@@ -287,23 +287,23 @@ var check_use_counter_direct = Task.asyn
   });
   
   // Tear down the page.
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
-  yield waitForDestroyedDocuments();
+  await waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
   let [histogram_page_after, histogram_document_after,
        histogram_docs_after, histogram_toplevel_docs_after] =
-      yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
+      await grabHistogramsFromContent(use_counter_middlefix, histogram_page_before);
   (xfail ? todo_is : is)(histogram_page_after, histogram_page_before + 1,
                          "page counts for " + use_counter_middlefix + " after are correct");
   (xfail ? todo_is : is)(histogram_document_after, histogram_document_before + 1,
                          "document counts for " + use_counter_middlefix + " after are correct");
   ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
      "top level document counts are correct");
   ok(histogram_docs_after >= histogram_docs_before + 1,
      "document counts are correct");
-});
+};
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -49,17 +49,16 @@ support-files =
   bug696301-script-1.js
   bug696301-script-1.js^headers^
   bug696301-script-2.js
   bug704320.sjs
   bug704320_counter.sjs
   bug819051.sjs
   chrome/bug418986-1.js
   copypaste.js
-  create_file_objects.js
   delayedServerEvents.sjs
   eventsource.resource
   eventsource.resource^headers^
   eventsource_redirect.resource
   eventsource_redirect.resource^headers^
   eventsource_redirect_to.resource
   eventsource_redirect_to.resource^headers^
   file_base_xbl.xml
@@ -146,21 +145,17 @@ support-files =
   file_htmlserializer_2.html
   file_htmlserializer_2_basic.html
   file_htmlserializer_2_enthtml.html
   file_htmlserializer_2_entw3c.html
   file_htmlserializer_2_latin1.html
   file_htmlserializer_ipv6.html
   file_htmlserializer_ipv6_out.html
   file_lock_orientation.html
-  file_mozfiledataurl_audio.ogg
-  file_mozfiledataurl_doc.html
   file_mozfiledataurl_img.jpg
-  file_mozfiledataurl_inner.html
-  file_mozfiledataurl_text.txt
   file_record_orientation.html
   file_restrictedEventSource.sjs
   file_simplecontentpolicy.js
   file_timer_flood.html
   file_websocket_basic_wsh.py
   file_websocket_hello_wsh.py
   file_websocket_http_resource.txt
   file_websocket_permessage_deflate_wsh.py
@@ -184,18 +179,16 @@ support-files =
   file_xhtmlserializer_1_sibling_body_only_body.xhtml
   file_xhtmlserializer_1_wrap.xhtml
   file_xhtmlserializer_2.xhtml
   file_xhtmlserializer_2_basic.xhtml
   file_xhtmlserializer_2_enthtml.xhtml
   file_xhtmlserializer_2_entw3c.xhtml
   file_xhtmlserializer_2_latin1.xhtml
   file_youtube_flash_embed.html
-  fileapi_chromeScript.js
-  fileutils.js
   forRemoval.resource
   forRemoval.resource^headers^
   formReset.html
   invalid_accesscontrol.resource
   invalid_accesscontrol.resource^headers^
   mutationobserver_dialog.html
   orientationcommon.js
   script-1_bug597345.sjs
@@ -203,17 +196,16 @@ support-files =
   script_bug602838.sjs
   send_gzip_content.sjs
   somedatas.resource
   somedatas.resource^headers^
   variable_style_sheet.sjs
   viewport_helpers.js
   w3element_traversal.svg
   wholeTexty-helper.xml
-  file_nonascii_blob_url.html
   referrerHelper.js
   img_referrer_testserver.sjs
   file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
   file_pluginAudio.html
   file_pluginAudioNonAutoStart.html
   noaudio.webm
@@ -226,22 +218,19 @@ support-files =
   referrer_change_server.sjs
   file_change_policy_redirect.html
   file_bug1198095.js
   file_bug1250148.sjs
   file_bug1268962.sjs
   mozbrowser_api_utils.js
   websocket_helpers.js
   websocket_tests.js
-  !/dom/html/test/form_submit_server.sjs
   !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
   !/image/test/mochitest/blue.png
-  !/dom/xhr/tests/file_XHRSendData.sjs
   script_bug1238440.js
-  file_blobURL_expiring.html
   intersectionobserver_iframe.html
   intersectionobserver_window.html
 
 [test_anchor_area_referrer.html]
 [test_anchor_area_referrer_changing.html]
 [test_anchor_area_referrer_invalid.html]
 [test_anchor_area_referrer_rel.html]
 [test_anonymousContent_api.html]
@@ -263,19 +252,16 @@ tags = audiochannel
 tags = audiochannel
 [test_audioNotificationStream.html]
 tags = audiochannel
 [test_audioNotificationStopOnNavigation.html]
 tags = audiochannel
 [test_audioNotificationWithEarlyPlay.html]
 tags = audiochannel
 [test_base.xhtml]
-[test_blob_fragment_and_query.html]
-[test_blobconstructor.html]
-[test_blobURL_expiring.html]
 [test_bug5141.html]
 [test_bug28293.html]
 [test_bug28293.xhtml]
 [test_bug51034.html]
 [test_bug116083.html]
 subsuite = clipboard
 [test_bug166235.html]
 subsuite = clipboard
@@ -642,20 +628,16 @@ skip-if = toolkit == 'android' || e10s #
 [test_e4x_for_each.html]
 [test_element.matches.html]
 [test_element_closest.html]
 [test_elementTraversal.html]
 [test_encodeToStringWithMaxLength.html]
 [test_error.html]
 [test_EventSource_redirects.html]
 [test_explicit_user_agent.html]
-[test_file_from_blob.html]
-[test_file_negative_date.html]
-[test_fileapi.html]
-[test_fileapi_slice.html]
 skip-if = (toolkit == 'android') # Android: Bug 775227
 [test_getAttribute_after_createAttribute.html]
 [test_getElementById.html]
 [test_getTranslationNodes.html]
 [test_getTranslationNodes_limit.html]
 [test_gsp-qualified.html]
 [test_gsp-quirks.html]
 [test_gsp-standards.html]
@@ -669,17 +651,16 @@ skip-if = (toolkit == 'android') # Andro
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
 [test_Image_constructor.html]
 [test_img_referrer.html]
 [test_innersize_scrollport.html]
 [test_integer_attr_with_leading_zero.html]
 [test_intersectionobservers.html]
 skip-if = true # Track Bug 1320704
-[test_ipc_messagemanager_blob.html]
 [test_link_prefetch.html]
 skip-if = !e10s # Track Bug 1281415
 [test_link_stylesheet.html]
 [test_messagemanager_targetchain.html]
 [test_meta_viewport0.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport1.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
@@ -691,18 +672,16 @@ skip-if = (os != 'android')    # meta-vi
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport5.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport6.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport7.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_mozbrowser_apis_blocked.html]
-[test_mozfiledataurl.html]
-skip-if = toolkit == 'android' #TIMED_OUT
 [test_mozMatchesSelector.html]
 [test_mutationobserver_anonymous.html]
 [test_mutationobservers.html]
 [test_named_frames.html]
 [test_navigator_hardwareConcurrency.html]
 [test_navigator_language.html]
 [test_navigatorPrefOverride.html]
 [test_noAudioNotification.html]
@@ -713,17 +692,16 @@ tags = audiochannel
 tags = audiochannel
 [test_noAudioNotificationOnVolume0Element.html]
 tags = audiochannel
 [test_NodeIterator_basics_filters.xhtml]
 [test_NodeIterator_mutations_1.xhtml]
 [test_NodeIterator_mutations_2.html]
 [test_NodeIterator_mutations_3.html]
 [test_nodelist_holes.html]
-[test_nonascii_blob_url.html]
 [test_noWebAudioNotification.html]
 tags = audiochannel
 [test_open_null_features.html]
 [test_openDialogChromeOnly.html]
 tags = openwindow
 [test_orientation_alternate.html]
 skip-if = true # bug 1312417
 [test_orientation_frame.html]
--- a/dom/base/test/referrer_helper.js
+++ b/dom/base/test/referrer_helper.js
@@ -61,17 +61,17 @@ function resetState() {
       ok(false, "error in reset state");
       SimpleTest.finish();
     });
 }
 
 /**
  * testing if anchor and area referrer attributes are honoured (1174913)
  */
-var tests = (function() {
+var tests = (function*() {
 
   // enable referrer attribute
   yield SpecialPowers.pushPrefEnv({"set": [['network.http.enablePerElementReferrer', true]]}, advance);
   yield SpecialPowers.pushPrefEnv({"set": [['security.mixed_content.block_active_content', false]]}, advance);
   yield SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], advance);
 
   var iframe = document.getElementById("testframe");
 
@@ -92,11 +92,11 @@ var tests = (function() {
         }
         var schemeFrom = tests[i].SCHEME_FROM || "http";
         yield iframe.src = schemeFrom + SJS + searchParams.toString();
         yield checkIndividualResults(tests[i].DESC, tests[i].RESULT, tests[i].NAME);
       };
     };
   };
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
-})();
\ No newline at end of file
+  // complete.
+  SimpleTest.finish();
+})();
--- a/dom/base/test/test_audioNotificationSilent_audioFile.html
+++ b/dom/base/test/test_audioNotificationSilent_audioFile.html
@@ -23,21 +23,17 @@ var observer = {
   observe: function(subject, topic, data) {
     is(topic, "audio-playback", "audio-playback received");
     is(data, expectedPlaybackActive, "Corrrect audible state");
     continueTest();
   }
 };
 
 function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
+  generator.next();
 }
 
 function audioPlayingStart() {
   observerService.addObserver(observer, "audio-playback", false);
   ok(true, "Observer set");
 
   expectedPlaybackActive = 'active';
 
@@ -53,21 +49,21 @@ function audioBecomeSilentDuringPlaying(
 
 function finish() {
   observerService.removeObserver(observer, "audio-playback");
   ok(true, "Observer removed");
 
   SimpleTest.finish();
 }
 
-function runTest() {
+function* runTest() {
   yield audioPlayingStart();
 
   yield audioBecomeSilentDuringPlaying();
 
-  yield finish();
+  finish();
 }
 
 continueTest();
 
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/base/test/test_audioNotificationSilent_webAudio.html
+++ b/dom/base/test/test_audioNotificationSilent_webAudio.html
@@ -25,21 +25,17 @@ var observer = {
     is(topic, "audio-playback", "audio-playback received");
     is(data, expectedPlaybackActive, "Corrrect audible state");
     is(ac.state, expectedPlaying, "Corrrect playing state");
     continueTest();
   }
 };
 
 function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
+  generator.next();
 }
 
 function playOscillatorNode() {
   var dest = ac.destination;
   var osc = ac.createOscillator();
   osc.connect(dest);
   osc.start(0);
   osc.stop(ac.currentTime + audibleDuration);
@@ -81,23 +77,23 @@ function startAudioContext() {
       }
     }
   } else {
     ok(true, "AudioContext is running!");
     continueTest();
   }
 }
 
-function runTest() {
+function* runTest() {
   yield startAudioContext();
 
   yield audioPlayingStart();
 
   yield audioBecomeSilentDuringPlaying();
 
-  yield finish();
+  finish();
 }
 
 continueTest();
 
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/base/test/test_bug1163743.html
+++ b/dom/base/test/test_bug1163743.html
@@ -15,27 +15,27 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript;version=1.7">
 
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * testing legacy support for origin-when-crossorigin (1163743)
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
   const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
 
   // origin when crossorigin (trimming whitespace)
   yield resetCounter();
   yield iframe.src = sjs + "&policy=" + escape(' origin-when-crossorigin');
   yield checkIndividualResults("origin-when-cross-origin", ["origin", "full"]);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
--- a/dom/base/test/test_bug1165501.html
+++ b/dom/base/test/test_bug1165501.html
@@ -16,33 +16,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * testing if policy is overwritten if there are two meta statements (1165501)
  * XXX: would be nice to test this with CSP and meta as well
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
   const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
 
   // setting first unsafe-url and then origin - origin shall prevail
   yield resetCounter();
   yield iframe.src = sjs + "&policy=" + escape('origin')+ "&wrongPolicy=" + escape('unsafe-url');
   yield checkIndividualResults("unsafe-url then origin", ["origin"]);
 
   // setting first no-referrer and then default - default shall prevail
   yield resetCounter();
   yield iframe.src = sjs + "&policy=" + escape('default')+ "&wrongPolicy=" + escape('no-referrer');
   yield checkIndividualResults("no-referrer then default", ["full"]);
 
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
--- a/dom/base/test/test_bug544642.html
+++ b/dom/base/test/test_bug544642.html
@@ -12,33 +12,31 @@
 <iframe id=iframe></iframe>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 SimpleTest.waitForExplicitFinish();
 var gen = runTest();
 
 addLoadEvent(function() { gen.next(); });
 
-function runTest() {
+function* runTest() {
   var iframe = $('iframe');
-  iframe.onerror = function() { gen.send("error"); };
-  iframe.onload = function() { gen.send("load"); };
+  iframe.onerror = function() { gen.next("error"); };
+  iframe.onload = function() { gen.next("load"); };
 
   iframe.src = "data:text/plain,hello";
   is((yield), "load", "plaintext data");
 
   iframe.src = "file://foo/bar";
   is((yield), "error", "file");
 
   // We should do this test too, however it brings up a modal dialog which
   // we can't dismiss.
   //iframe.src = "http:////";
   //is((yield), "error", "invalid http");
 
   SimpleTest.finish();
-  
-  yield undefined;
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/test/test_bug590870.html
+++ b/dom/base/test/test_bug590870.html
@@ -10,28 +10,27 @@
 <body onload="gen.next()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590870">Mozilla Bug 590870</a>
 <p id="display"></p>
 <iframe id=iframe></iframe>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
 SimpleTest.waitForExplicitFinish();
-window.addEventListener("message", function(e) { gen.send(e.data) }, false);
+window.addEventListener("message", function(e) { gen.next(e.data) }, false);
 
 var gen = runTest();
 
-function runTest() {
+function* runTest() {
   var iframe = $('iframe');
   iframe.src = "http://noxul.example.com/tests/dom/base/test/file_bug590870.html";
   is((yield), true, "shouldn't be able to create XUL elements");
 
   iframe.src = "file_bug590870.html";
   is((yield), false, "should be able to create XUL elements");
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/test/test_bug682592.html
+++ b/dom/base/test/test_bug682592.html
@@ -46,24 +46,24 @@ if (navigator.platform.startsWith("Linux
 
 var page = `data:text/html;charset=UTF-8,<!DOCTYPE html>
 <html><body><p id="content"></p></body></html>`;
 var refFrame = document.getElementById("iframe-ref")
 var testFrame = document.getElementById("iframe-test");
 
 refFrame.addEventListener("load", function() {
   testFrame.addEventListener("load", function() {
-    try {
-      tests.next();
+    let {done} = tests.next();
+    if (!done) {
       ok(compareSnapshots(snapshotWindow(testFrame.contentWindow), 
                           snapshotWindow(refFrame.contentWindow), true)[0], 
          "bidi is not detected correctly");
 
       testFrame.contentWindow.location.reload();
-    } catch (err if err instanceof StopIteration) {
+    } else {
       SimpleTest.finish();
     }
   }, false);
   testFrame.src = page;
 }, false);
 refFrame.src = page;
 
 var rtl = "עִבְרִית";
@@ -73,17 +73,17 @@ var is8bit = "a";
 // concats aStr aNumber of times
 function strMult(aStr, aNumber) {
   if (aNumber === 0) {
     return "";
   }
   return strMult(aStr, aNumber - 1) + aStr;
 }
 
-function runTests () {
+function* runTests () {
   var ltr = "", prefix = null;
   var refContainer = refFrame.contentDocument.getElementById('content');
   var testContainer, textNode;
   var i = 0;
 
   // 8bit chars + bidi
   for (i = 0; i <= 16; i++) {
     ltr = strMult(is8bit, i);
--- a/dom/base/test/test_bug704320.html
+++ b/dom/base/test/test_bug704320.html
@@ -36,34 +36,34 @@ testIframeUrls = testIframeUrls.concat(g
 
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * This is the main test routine -- serialized by use of a generator.
  * It performs all tests in sequence using in the same iframe.
  */
-var tests = (function() {
+var tests = (function*() {
   SimpleTest.requestLongerTimeout(4);
   var iframe = document.getElementById("testframe");
   iframe.onload = function() {
      advance();
   }
 
   // load the test frame from testIframeUrls[url]
   // it will call back into this function via postMessage when it finishes loading.
   // and continue beyond the yield.
   for(url in testIframeUrls) {
     yield iframe.src = testIframeUrls[url];
     // run test and check result for loaded test URL
     yield checkExpectedGlobalResults();
   }
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320">Mozilla Bug 704320 - HTTP/HTTPS to HTTPS/HTTP</a>
 <p id="display"></p>
--- a/dom/base/test/test_bug704320_policyset.html
+++ b/dom/base/test/test_bug704320_policyset.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * This is the main test routine -- serialized by use of a generator.
  * It resets the counter, then performs two tests in sequence using
  * the same iframe.
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
   const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
 
 
   // basic calibration check
   // reset the counter
   yield resetCounter();
 
@@ -84,18 +84,18 @@ var tests = (function() {
   yield iframe.src = sjs + "&policy=" + escape('no-referrer');
   yield checkIndividualResults("no-referrer", ["none"]);
 
   // Case insensitive
   yield resetCounter();
   yield iframe.src = sjs + "&policy=" + escape('\f OrigIn');
   yield checkIndividualResults("origin case insensitive", ["origin"]);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
--- a/dom/base/test/test_bug704320_policyset2.html
+++ b/dom/base/test/test_bug704320_policyset2.html
@@ -17,26 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * This is the main test routine -- serialized by use of a generator.
  * It resets the counter, then performs two tests in sequence using
  * the same iframe.
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
   const sjs = "/tests/dom/base/test/bug704320.sjs?action=generate-policy-test";
 
   yield resetCounter();
   yield iframe.src = sjs + "&policy=" + escape(' \f\r\n\t  ');
   yield checkIndividualResults("whitespace only policy", ["full"]);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
--- a/dom/base/test/test_bug704320_preload.html
+++ b/dom/base/test/test_bug704320_preload.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 var advance = function() { tests.next(); };
 
 /**
  * This is the main test routine -- serialized by use of a generator.
  * It resets the counter, then performs two tests in sequence using
  * the same iframe.
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
 
   // reset the counter
   yield resetCounter();
 
   // load the first test frame
   // it will call back into this function via postMessage when it finishes loading.
   // and continue beyond the yield.
@@ -42,18 +42,18 @@ var tests = (function() {
   // load the second test frame
   // it will call back into this function via postMessage when it finishes loading.
   // and continue beyond the yield.
   yield iframe.src = 'file_bug704320_preload_reuse.html';
 
   // check the second test
   yield checkResults(finalizePreloadReuse);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 // Helper functions below.
 
 /**
  * This checks the first test: a test where the preloads should not
  * be reused.  * we expect two requests for each image, script, js request
  * since the referrer policy changed after speculative loads were started.
--- a/dom/base/test/test_change_policy.html
+++ b/dom/base/test/test_change_policy.html
@@ -73,17 +73,17 @@ function resetState() {
 }
 
 
 /**
  * This is the main test routine -- serialized by use of a generator.
  * It resets the counter, then performs two tests in sequence using
  * the same iframe.
  */
-var tests = (function() {
+var tests = (function*() {
   var iframe = document.getElementById("testframe");
   var sjs = "/tests/dom/base/test/referrer_change_server.sjs?action=generate-policy-test";
 
   yield resetState();
   var name = "no-referrer-unsafe-url";
   yield iframe.src = sjs + "&policy=" + escape('no-referrer') + "&name=" + name + "&newPolicy=" + escape('unsafe-url');
   yield checkIndividualResults("unsafe-url (changed) with no-referrer first", ["full"], [name+'unsafe-url']);
 
@@ -109,18 +109,18 @@ var tests = (function() {
   yield iframe.src = sjs + "&policy=" + escape('origin') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
   yield checkIndividualResults("no-referrer (changed) with origin first", ["none"], [name+'no-referrer']);
 
   yield resetState();
   var name = "unsafe-url-no-referrer";
   yield iframe.src = sjs + "&policy=" + escape('unsafe-url') + "&name=" + name + "&newPolicy=" + escape('no-referrer');
   yield checkIndividualResults("no-referrer (changed) with unsafe-url first", ["none"], [name+'no-referrer']);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
--- a/dom/base/test/test_img_referrer.html
+++ b/dom/base/test/test_img_referrer.html
@@ -77,17 +77,17 @@ function resetState() {
       ok(false, "error in reset state");
       SimpleTest.finish();
     });
 }
 
 /**
  * testing if img referrer attribute is honoured (1165501)
  */
-var tests = (function() {
+var tests = (function*() {
 
   // enable referrer attribute
   yield SpecialPowers.pushPrefEnv({"set": [['network.http.enablePerElementReferrer', true]]}, advance);
 
   var iframe = document.getElementById("testframe");
   var sjs = "/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-img-policy-test";
 
   // setting img unsafe-url and meta origin - unsafe-url shall prevail (should use speculative load)
@@ -169,18 +169,18 @@ var tests = (function() {
 
   yield resetState();
   sjs =
     "/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-setAttribute-test2";
   name = 'set-invalid-referrer-policy-attribute-before-src-invalid';
   yield iframe.src = sjs + "&imgPolicy=" + escape('default') + "&policy=" + escape('unsafe-url') + "&name=" + name;
   yield checkIndividualResults("unsafe-url in meta, default in img", ["full"], [name]);
 
-  // complete.  Be sure to yield so we don't call this twice.
-  yield SimpleTest.finish();
+  // complete.
+  SimpleTest.finish();
 })();
 
 </script>
 </head>
 
 <body onload="tests.next();">
   <iframe id="testframe"></iframe>
 
deleted file mode 100644
--- a/dom/base/test/test_mozfiledataurl.html
+++ /dev/null
@@ -1,225 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
-  <title>Test for File urls</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="start()">
-<p id="display">
-<iframe id=inner></iframe>
-<iframe id=iframe></iframe>
-<img id=img onload="gen.send(event);">
-<audio id=audio onloadeddata="gen.send(event);">
-</p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.8">
-
-try {
-  URL.createObjectURL(undefined);
-} catch(e) { }
-
-window.addEventListener("message", function(e) {
-  gen.send(JSON.parse(e.data));
-}, false);
-
-const innerSameSiteURI = "file_mozfiledataurl_inner.html";
-const innerCrossSiteURI = "http://example.com/tests/dom/base/test/file_mozfiledataurl_inner.html"
-
-var fileNames = ["file_mozfiledataurl_img.jpg",
-                 "file_mozfiledataurl_audio.ogg",
-                 "file_mozfiledataurl_doc.html",
-                 "file_mozfiledataurl_text.txt"];
-
-function start() {
-  let xhr = new XMLHttpRequest;
-  xhr.open("GET", "/dynamic/getMyDirectory.sjs", false);
-  xhr.send();
-  let basePath = xhr.responseText;
-
-  let fullFileNames = [];
-  for (let name of fileNames) {
-    fullFileNames.push(basePath + name);
-  }
-
-  var script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("create_file_objects.js"));
-
-  script.addMessageListener("created-file-objects", function handler(files) {
-    script.removeMessageListener("created-file-objects", handler);
-    gen = runTest(files);
-    gen.next();
-  });
-
-  script.sendAsyncMessage("create-file-objects", {fileNames: fullFileNames});
-};
-
-SimpleTest.waitForExplicitFinish();
-
-function runTest([imgFile, audioFile, docFile, xhrFile]) {
-  inner = document.getElementById('inner');
-  img = document.getElementById('img');
-  audio = document.getElementById('audio');
-  iframe = document.getElementById('iframe');
-  inner.onload = function() { gen.send("inner loaded"); };
-
-  // Attempt to load a image in this document
-  var fileurl = URL.createObjectURL(imgFile);
-  img.src = fileurl;
-  var e = (yield);
-  is(e.type, "load", "loaded successfully");
-  is(img.width, 120, "correct width");
-  is(img.height, 90, "correct height");
-
-  // Revoke url and attempt to load a image in this document
-  img.src = "file_mozfiledataurl_img.jpg";
-  is((yield).type, "load", "successfull reset image");
-  URL.revokeObjectURL(fileurl);
-  todo(false, "urls need to act like 404s, not fail to parse");
-/*  img.src = fileurl;
-  var e = (yield);
-  is(e.type, "error", "failed successfully");
-  isnot(img.width, 120, "correct error width");
-  isnot(img.height, 90, "correct error height");
-*/
-  // Generate new fileurl and make sure it's different from the old
-  var oldFileurl = fileurl;
-  fileurl = URL.createObjectURL(imgFile);
-  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
-
-  // Attempt to load an image in a different same-origin document
-  inner.src = innerSameSiteURI;
-  yield undefined;
-  inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "load", "loaded successfully");
-  is(res.width, 120, "correct width");
-  is(res.height, 90, "correct height");
-
-  // Attempt to load an image in a different cross-origin document
-  inner.src = innerCrossSiteURI;
-  yield undefined;
-  inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "error", "failed successfully");
-  isnot(res.width, 120, "correct error width");
-  isnot(res.height, 90, "correct error height");
-
-  // Attempt to load an audio in this document
-  fileurl = URL.createObjectURL(audioFile);
-  audio.src = fileurl;
-  var e = (yield);
-  is(e.type, "loadeddata", "loaded successfully");
-
-  // Revoke url and attempt to load a audio in this document
-  audio.src = "file_mozfiledataurl_audio.ogg";
-  is((yield).type, "loadeddata", "successfully reset audio");
-  URL.revokeObjectURL(fileurl);
-  todo(false, "urls need to act like 404s, not fail to parse");
-/*  img.src = fileurl;
-  var e = (yield);
-  is(e.type, "error", "failed successfully");
-  isnot(img.width, 120, "correct error width");
-  isnot(img.height, 90, "correct error height");
-*/
-  // Generate new fileurl and make sure it's different from the old
-  var oldFileurl = fileurl;
-  fileurl = URL.createObjectURL(audioFile);
-  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
-
-  // Attempt to load an audio in a different same-origin document
-  inner.src = innerSameSiteURI;
-  yield undefined;
-  inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "loadeddata", "loaded successfully");
-
-  // Attempt to load an audio in a different cross-origin document
-  inner.src = innerCrossSiteURI;
-  yield undefined;
-  inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "error", "failed successfully");
-
-  // Attempt to load a HTML document in an iframe in this document
-  iframe.onload = function() { gen.next(); };
-  iframe.src = "file_mozfiledataurl_doc.html";
-  yield undefined;
-  is(iframe.contentDocument.getElementsByTagName("p")[0].textContent,
-     "This here is a document!",
-     "iframe loaded successfully");
-  is(iframe.contentDocument.getElementById("img").width, 120,
-     "image in iframe width");
-  is(iframe.contentDocument.getElementById("img").height, 90,
-     "image in iframe height");
-
-  // Attempt to load a HTML document in an iframe in this document, using file url
-  fileurl = URL.createObjectURL(docFile);
-  iframe.src = fileurl;
-  yield undefined;
-  is(iframe.contentDocument.getElementsByTagName("p")[0].textContent,
-     "This here is a document!",
-     "iframe loaded successfully");
-  isnot(iframe.contentDocument.getElementById("img").width, 120,
-        "failed image in iframe width");
-  isnot(iframe.contentDocument.getElementById("img").height, 90,
-        "failed image in iframe height");
-
-  // Attempt to load a HTML document in an iframe in inner document
-  inner.src = innerSameSiteURI;
-  is((yield), "inner loaded", "correct gen.next()");
-  inner.contentWindow.postMessage(JSON.stringify({iframe:"file_mozfiledataurl_doc.html"}), "*");
-  var res = (yield);
-  is(res.type, "load", "loaded successfully");
-  is(res.text, "This here is a document!", "loaded successfully");
-  is(res.imgWidth, 120, "correct width");
-
-  // Attempt to load a HTML document in an iframe in inner document, using file url
-  inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "load", "loaded successfully");
-  is(res.text, "This here is a document!", "loaded successfully");
-  isnot(res.imgWidth, 120, "correct width");
-
-  // Attempt to load a HTML document in an iframe in inner cross-site document, using file url
-  inner.src = innerCrossSiteURI;
-  is((yield), "inner loaded", "correct gen.next()");
-  inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*");
-  var res = (yield);
-  is(res.type, "error", "load failed successfully");
-
-  // Attempt to load file url using XHR
-  fileurl = URL.createObjectURL(xhrFile);
-  xhr = new XMLHttpRequest;
-  xhr.onload = function() { gen.send("XHR finished"); };
-  xhr.open("GET", fileurl);
-  xhr.send();
-  is((yield), "XHR finished", "correct gen.next()");
-  xhr.responseText == "Yarr, here be plaintext file, ya landlubber\n";
-
-  // Attempt to load file url using XHR in inner document
-  inner.src = innerSameSiteURI;
-  is((yield), "inner loaded", "correct gen.next()");
-  inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*");
-  var res = (yield);
-  is(res.didThrow, undefined, "load successful");
-  is(res.text, "Yarr, here be plaintext file, ya landlubber\n", "load successful");
-
-  // Attempt to load file url using XHR
-  inner.src = innerCrossSiteURI;
-  is((yield), "inner loaded", "correct gen.next()");
-  inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*");
-  var res = (yield);
-  is(res.didError, true, "load failed successfully");
-
-  SimpleTest.finish();
-
-  yield undefined;
-}
-</script>
-</pre>
-</body>
-</html>
--- a/dom/browser-element/mochitest/browserElement_ActiveStateChange.js
+++ b/dom/browser-element/mochitest/browserElement_ActiveStateChange.js
@@ -1,105 +1,94 @@
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 browserElementTestHelpers.setEnabledPref(true);
 
 var fileURL = 'chrome://mochitests/content/chrome/dom/browser-element/mochitest/file_browserElement_ActiveStateChange.html';
-var generator = runTests();
 var testFrame;
 var ac;
 
 function assert(aVal, aMessage) {
   return (!aVal) ? error(aMessage) : 0;
 }
 
 function error(aMessage) {
   ok(false, "Error : " + aMessage);
   finish();
 }
 
-function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
-}
-
 function finish() {
   document.body.removeChild(testFrame);
   SimpleTest.finish();
 }
 
 function setCommand(aArg) {
-  assert(!!ac, "Audio channel doesn't exist!");
-  info("# Command = " + aArg);
+  return new Promise(resolve => {
+    assert(!!ac, "Audio channel doesn't exist!");
+    info("# Command = " + aArg);
 
-  testFrame.src = fileURL + '#' + aArg;
-  var expectedActive = false;
-  switch (aArg) {
-    case 'play':
-      expectedActive = true;
-      break;
-    case 'pause':
-      expectedActive = false;
-      break;
-    default :
-      error("Undefined command!");
-  }
+    testFrame.src = fileURL + '#' + aArg;
+    var expectedActive = false;
+    switch (aArg) {
+      case 'play':
+        expectedActive = true;
+        break;
+      case 'pause':
+        expectedActive = false;
+        break;
+      default :
+        error("Undefined command!");
+    }
 
-  ac.onactivestatechanged = () => {
-    ac.onactivestatechanged = null;
-    ac.isActive().onsuccess = (e) => {
-      is(expectedActive, e.target.result,
-         "Correct active state = " + expectedActive);
-      continueTest();
-    }
-  };
+    ac.onactivestatechanged = () => {
+      ac.onactivestatechanged = null;
+      ac.isActive().onsuccess = (e) => {
+        is(expectedActive, e.target.result,
+           "Correct active state = " + expectedActive);
+        resolve();
+      };
+    };
+  });
 }
 
-function runTests() {
-  setCommand('play');
-  yield undefined;
+async function runTests() {
+  await setCommand('play');
 
-  setCommand('pause');
-  yield undefined;
+  await setCommand('pause');
 
   finish();
-  yield undefined;
 }
 
 function setupTestFrame() {
   testFrame = document.createElement('iframe');
   testFrame.setAttribute('mozbrowser', 'true');
   testFrame.src = fileURL;
 
   function loadend() {
-    testFrame.removeEventListener('mozbrowserloadend', loadend);
     ok("allowedAudioChannels" in testFrame, "allowedAudioChannels exist");
     var channels = testFrame.allowedAudioChannels;
     is(channels.length, 9, "9 audio channel by default");
 
     ac = channels[0];
 
     ok(ac instanceof BrowserElementAudioChannel, "Correct class");
     ok("isActive" in ac, "isActive exists");
     ok("onactivestatechanged" in ac, "onactivestatechanged exists");
 
-    generator.next();
+    runTests();
   }
 
   function alertError(e) {
     testFrame.removeEventListener('mozbrowsershowmodalprompt', alertError);
     var message = e.detail.message
     error(message);
   }
 
-  testFrame.addEventListener('mozbrowserloadend', loadend);
+  testFrame.addEventListener('mozbrowserloadend', loadend, {once: true});
   testFrame.addEventListener('mozbrowsershowmodalprompt', alertError);
   document.body.appendChild(testFrame);
 }
 
 addEventListener('testready', function() {
   SpecialPowers.pushPrefEnv({'set': [["b2g.system_startup_url", window.location.href]]},
                             function() {
     SimpleTest.executeSoon(setupTestFrame);
--- a/dom/browser-element/mochitest/browserElement_AudioChannelSeeking.js
+++ b/dom/browser-element/mochitest/browserElement_AudioChannelSeeking.js
@@ -27,21 +27,17 @@ function assert(aVal, aMessage) {
 }
 
 function error(aMessage) {
   ok(false, "Error : " + aMessage);
   finish();
 }
 
 function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
+  generator.next();
 }
 
 function finish() {
   testFrame.removeEventListener('mozbrowsershowmodalprompt', alertListener);
   ok(true, "Remove event-listener.");
   document.body.removeChild(testFrame);
   ok(true, "Remove test-frame from document.");
   SimpleTest.finish();
@@ -69,34 +65,33 @@ function setCommand(aArg) {
     case 'pause':
       ac.onactivestatechanged = null;
       break;
     default :
       error("Undefined command!");
   }
 }
 
-function runTests() {
+function* runTests() {
   setCommand('play');
   yield undefined;
 
   setCommand('seeking');
   yield undefined;
 
   setCommand('seeking');
   yield undefined;
 
   setCommand('seeking');
   yield undefined;
 
   setCommand('pause');
   yield undefined;
 
   finish();
-  yield undefined;
 }
 
 function setupTestFrame() {
   testFrame = document.createElement('iframe');
   testFrame.setAttribute('mozbrowser', 'true');
   testFrame.src = fileURL;
 
   function loadend() {
--- a/dom/browser-element/mochitest/browserElement_NoAudioTrack.js
+++ b/dom/browser-element/mochitest/browserElement_NoAudioTrack.js
@@ -20,46 +20,41 @@ function alertListener(e) {
 }
 
 function error(aMessage) {
   ok(false, "Error : " + aMessage);
   finish();
 }
 
 function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
+  generator.next();
 }
 
 function finish() {
   testFrame.removeEventListener('mozbrowsershowmodalprompt', alertListener);
   ok(true, "Remove event-listener.");
   document.body.removeChild(testFrame);
   ok(true, "Remove test-frame from document.");
   SimpleTest.finish();
 }
 
 function setCommand(aArg) {
   info("# Command = " + aArg);
   testFrame.src = fileURL + '#' + aArg;
 }
 
-function runTests() {
+function* runTests() {
   setCommand('play');
   yield undefined;
 
   // wait a second to make sure that onactivestatechanged isn't dispatched.
   setCommand('idle');
   yield undefined;
 
   finish();
-  yield undefined;
 }
 
 function setupTestFrame() {
   testFrame = document.createElement('iframe');
   testFrame.setAttribute('mozbrowser', 'true');
   testFrame.src = fileURL;
 
   function loadend() {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1628,18 +1628,18 @@ WebGLFramebuffer::BlitFramebuffer(WebGLC
     };
 
     if (dstFB) {
         dstSampleBuffers = dstFB->mResolvedCompleteData->hasSampleBuffers;
 
         dstDepthFormat = fnGetFormat(dstFB->mResolvedCompleteData->depthBuffer);
         dstStencilFormat = fnGetFormat(dstFB->mResolvedCompleteData->stencilBuffer);
 
-        for (const auto& drawBuffer : dstFB->mColorDrawBuffers) {
-            fnCheckColorFormat(drawBuffer->Format()->format);
+        for (const auto& drawBufferEntry : dstFB->mResolvedCompleteData->drawSet) {
+            fnCheckColorFormat(drawBufferEntry.mRef.Format()->format);
         }
     } else {
         dstSampleBuffers = bool(gl->Screen()->Samples());
 
         const webgl::FormatInfo* dstColorFormat;
         GetBackbufferFormats(webgl, &dstColorFormat, &dstDepthFormat, &dstStencilFormat);
 
         fnCheckColorFormat(dstColorFormat);
--- a/dom/devicestorage/test/test_app_permissions.html
+++ b/dom/devicestorage/test/test_app_permissions.html
@@ -427,17 +427,17 @@ function testComplete(iframe, data) {
 
   if (gData.length == 0) {
     SimpleTest.finish();
   } else {
     gTestRunner.next();
   }
 }
 
-function runTest() {
+function* runTest() {
   while (gData.length > 0) {
     var iframe = document.createElement('iframe');
     var data = gData.pop();
 
     iframe.setAttribute('mozbrowser', '');
 
     iframe.src = gTestUri;
 
--- a/dom/devicestorage/test/test_fs_app_permissions.html
+++ b/dom/devicestorage/test/test_fs_app_permissions.html
@@ -545,17 +545,17 @@ function testComplete(iframe, data) {
 
   if (gData.length == 0) {
     SimpleTest.finish();
   } else {
     gTestRunner.next();
   }
 }
 
-function runTest() {
+function* runTest() {
   while (gData.length > 0) {
     let iframe = document.createElement('iframe');
     let data = gData.shift();
 
     iframe.setAttribute('mozbrowser', '');
 
     iframe.src = gTestUri;
 
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -1250,16 +1250,31 @@ Event::GetShadowRelatedTarget(nsIContent
     }
 
     relatedTarget = ancestorShadow->GetHost();
   }
 
   return nullptr;
 }
 
+NS_IMETHODIMP
+Event::GetCancelBubble(bool* aCancelBubble)
+{
+  NS_ENSURE_ARG_POINTER(aCancelBubble);
+  *aCancelBubble = CancelBubble();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Event::SetCancelBubble(bool aCancelBubble)
+{
+  mEvent->mFlags.mPropagationStopped = aCancelBubble;
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<Event>
 NS_NewDOMEvent(EventTarget* aOwner,
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -171,16 +171,21 @@ public:
     return mEvent->mFlags.mCancelable;
   }
 
   bool Composed() const
   {
     return mEvent->mFlags.mComposed;
   }
 
+  bool CancelBubble() const
+  {
+    return mEvent->PropagationStopped();
+  }
+
   // xpidl implementation
   // void PreventDefault();
 
   // You MUST NOT call PreventDefault(JSContext*, CallerType) from C++ code.  A
   // call of this method always sets Event.defaultPrevented true for web
   // contents.  If default action handler calls this, web applications see wrong
   // defaultPrevented value.
   virtual void PreventDefault(JSContext* aCx, CallerType aCallerType);
@@ -378,17 +383,19 @@ private:
   NS_IMETHOD GetExplicitOriginalTarget(nsIDOMEventTarget** aExplicitOriginalTarget) override { return _to GetExplicitOriginalTarget(aExplicitOriginalTarget); } \
   NS_IMETHOD GetPreventDefault(bool* aRetval) override { return _to GetPreventDefault(aRetval); } \
   NS_IMETHOD GetIsTrusted(bool* aIsTrusted) override { return _to GetIsTrusted(aIsTrusted); } \
   NS_IMETHOD SetTarget(nsIDOMEventTarget* aTarget) override { return _to SetTarget(aTarget); } \
   NS_IMETHOD_(bool) IsDispatchStopped(void) override { return _to IsDispatchStopped(); } \
   NS_IMETHOD_(WidgetEvent*) WidgetEventPtr(void) override { return _to WidgetEventPtr(); } \
   NS_IMETHOD_(void) SetTrusted(bool aTrusted) override { _to SetTrusted(aTrusted); } \
   NS_IMETHOD_(void) SetOwner(EventTarget* aOwner) override { _to SetOwner(aOwner); } \
-  NS_IMETHOD_(Event*) InternalDOMEvent() override { return _to InternalDOMEvent(); }
+  NS_IMETHOD_(Event*) InternalDOMEvent() override { return _to InternalDOMEvent(); } \
+  NS_IMETHOD GetCancelBubble(bool* aCancelBubble) override { return _to GetCancelBubble(aCancelBubble); } \
+  NS_IMETHOD SetCancelBubble(bool aCancelBubble) override { return _to SetCancelBubble(aCancelBubble); }
 
 #define NS_FORWARD_TO_EVENT_NO_SERIALIZATION_NO_DUPLICATION \
   NS_FORWARD_NSIDOMEVENT_NO_SERIALIZATION_NO_DUPLICATION(Event::) \
   virtual void PreventDefault(JSContext* aCx, CallerType aCallerType) override { Event::PreventDefault(aCx, aCallerType); }
 
 inline nsISupports*
 ToSupports(mozilla::dom::Event* e)
 {
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -283,31 +283,16 @@ UIEvent::RangeOffset() const
     return 0;
   }
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
                                                             targetFrame);
   return targetFrame->GetContentOffsetsFromPoint(pt).offset;
 }
 
-NS_IMETHODIMP
-UIEvent::GetCancelBubble(bool* aCancelBubble)
-{
-  NS_ENSURE_ARG_POINTER(aCancelBubble);
-  *aCancelBubble = CancelBubble();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-UIEvent::SetCancelBubble(bool aCancelBubble)
-{
-  mEvent->mFlags.mPropagationStopped = aCancelBubble;
-  return NS_OK;
-}
-
 nsIntPoint
 UIEvent::GetLayerPoint() const
 {
   if (!mEvent ||
       (mEvent->mClass != eMouseEventClass &&
        mEvent->mClass != eMouseScrollEventClass &&
        mEvent->mClass != eWheelEventClass &&
        mEvent->mClass != ePointerEventClass &&
--- a/dom/events/UIEvent.h
+++ b/dom/events/UIEvent.h
@@ -88,21 +88,16 @@ public:
                "Mouse events should override Which()");
     return 0;
   }
 
   already_AddRefed<nsINode> GetRangeParent();
 
   int32_t RangeOffset() const;
 
-  bool CancelBubble() const
-  {
-    return mEvent->PropagationStopped();
-  }
-
   bool IsChar() const;
 
 protected:
   ~UIEvent() {}
 
   // Internal helper functions
   nsIntPoint GetMovementPoint();
   nsIntPoint GetLayerPoint() const;
--- a/dom/events/test/bug426082.html
+++ b/dom/events/test/bug426082.html
@@ -56,29 +56,30 @@ function paintListener(e) {
     currentSnapshot = takeSnapshot();
   }
 }
 
 var gNeedsPaint = false;
 function executeTests() {
   var testYielder = tests();
   function execNext() {
-    try {
-      if (!gNeedsPaint) {
-        testYielder.next();
-        button.getBoundingClientRect(); // Flush.
-        gNeedsPaint = true;
+    if (!gNeedsPaint) {
+      let {done} = testYielder.next();
+      if (done) {
+        return;
       }
-      SimpleTest.executeSoon(execNext);
-    } catch (e) {}
+      button.getBoundingClientRect(); // Flush.
+      gNeedsPaint = true;
+    }
+    SimpleTest.executeSoon(execNext);
   }
   execNext();
 }
 
-function tests() {
+function* tests() {
   window.addEventListener("MozAfterPaint", paintListener, false);
   normalButtonCanvas = takeSnapshot();
   // Press the button.
   sendMouseEvent("mousemove", button);
   sendMouseEvent("mousedown", button);
   yield undefined;
   pressedFocusedButtonCanvas = takeSnapshot();
   compareSnapshots_(normalButtonCanvas, pressedFocusedButtonCanvas, false, "Pressed focused buttons should look different from normal buttons.");
--- a/dom/events/test/bug656379-1.html
+++ b/dom/events/test/bug656379-1.html
@@ -72,29 +72,30 @@ function paintListener(e) {
     currentSnapshot = takeSnapshot();
   }
 }
 
 var gNeedsPaint = false;
 function executeTests() {
   var testYielder = tests();
   function execNext() {
-    try {
-      if (!gNeedsPaint) {
-        testYielder.next();
-        button.getBoundingClientRect(); // Flush.
-        gNeedsPaint = true;
+    if (!gNeedsPaint) {
+      let {done} = testYielder.next();
+      if (done) {
+        return;
       }
-      SimpleTest.executeSoon(execNext);
-    } catch (e) {}
+      button.getBoundingClientRect(); // Flush.
+      gNeedsPaint = true;
+    }
+    SimpleTest.executeSoon(execNext);
   }
   execNext();
 }
 
-function tests() {
+function* tests() {
   window.addEventListener("MozAfterPaint", paintListener, false);
   normalButtonCanvas = takeSnapshot();
   // Press the button.
   sendMouseEvent("mousemove", button);
   sendMouseEvent("mousedown", button);
   yield undefined;
   pressedFocusedButtonCanvas = takeSnapshot();
   compareSnapshots_(normalButtonCanvas, pressedFocusedButtonCanvas, false, "Pressed focused buttons should look different from normal buttons.");
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -179,8 +179,9 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_moz_mouse_pixel_scroll_event.html]
 [test_offsetxy.html]
 [test_onerror_handler_args.html]
 [test_passive_listeners.html]
 [test_paste_image.html]
 [test_wheel_default_action.html]
 [test_bug687787.html]
 [test_bug1305458.html]
+[test_bug1298970.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1298970.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1298970
+-->
+<head>
+  <title>Test for Bug 1298970</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1298970">Mozilla Bug 1298970</a>
+<p id="display"></p>
+<div id="inner"></div>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1298970 **/
+var target = document.getElementById("inner");
+var event = new Event("test", { bubbles: true, cancelable: true });
+
+is(event.cancelBubble, false, "Event.cancelBubble should be false by default");
+
+target.addEventListener("test", (e) => {
+  e.stopPropagation();
+  is(e.cancelBubble, true, "Event.cancelBubble should be true after stopPropagation");
+}, true);
+
+target.dispatchEvent(event);
+
+</script>
+</body>
+</html>
+
--- a/dom/events/test/test_bug415498.xul
+++ b/dom/events/test/test_bug415498.xul
@@ -47,30 +47,26 @@ https://bugzilla.mozilla.org/show_bug.cg
         }
       };
       gConsole.registerListener(gConsoleListener);
 
       nextTest();
     }
 
     function nextTest() {
-      try {
-        gTestsIterator.next();
-      } catch (err) {
-          ok(err instanceof StopIteration,
-             "Some other exception was thrown than what we expected!");
-
+      let {done} = gTestsIterator.next();
+      if (done) {
         if (gConsole && gConsoleListener) {
           gConsole.unregisterListener(gConsoleListener);
         }
         SimpleTest.finish();
       }
     }
 
-    function testsIterator() {
+    function* testsIterator() {
 
       var browser = $("browser");
       browser.addEventListener("load", function() {
         setTimeout(nextTest, 0)
       }, false);
 
       // 1) This document uses addEventListener to register a method throwing an exception
       var chromeDir = getRootDirectory(window.location.href);
--- a/dom/events/test/test_bug656379-2.html
+++ b/dom/events/test/test_bug656379-2.html
@@ -25,17 +25,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript;version=1.8">
 
 /** Test for Bug 656379 **/
 SimpleTest.waitForExplicitFinish();
-function tests() {
+function* tests() {
   synthesizeMouseAtCenter($("label1"), { type: "mousemove" });
   yield undefined;
   is($("button1").matches(":hover"), true,
      "Button 1 should be hovered after mousemove over label1");
   is($("label1").matches(":hover"), true,
      "Label 1 should be hovered after mousemove over label1");
   is($("button2").matches(":hover"), false,
      "Button 2 should not be hovered after mousemove over label1");
@@ -62,20 +62,21 @@ function tests() {
   is($("label2").matches(":hover"), true,
      "Label 2 should be hovered after mousemove over label2");
   SimpleTest.finish();
 }
 
 function executeTests() {
   var testYielder = tests();
   function execNext() {
-    try {
-      testYielder.next();
-      SimpleTest.executeSoon(execNext);
-    } catch(e) {}
+    let {done} = testYielder.next();
+    if (done) {
+      return;
+    }
+    SimpleTest.executeSoon(execNext);
   }
   execNext();
 }
 
 SimpleTest.waitForFocus(executeTests);
 
 </script>
 </pre>
rename from dom/base/BlobSet.cpp
rename to dom/file/BlobSet.cpp
rename from dom/base/BlobSet.h
rename to dom/file/BlobSet.h
--- a/dom/base/BlobSet.h
+++ b/dom/file/BlobSet.h
@@ -2,17 +2,20 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef mozilla_dom_BlobSet_h
 #define mozilla_dom_BlobSet_h
 
+#include "jsapi.h"
 #include "mozilla/RefPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
 class BlobSet final
 {
rename from dom/base/File.cpp
rename to dom/file/File.cpp
--- a/dom/base/File.cpp
+++ b/dom/file/File.cpp
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozilla/dom/File.h"
 
+#include "ipc/nsIRemoteBlob.h"
 #include "MultipartBlobImpl.h"
 #include "nsCExternalHandlerService.h"
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsICharsetDetector.h"
 #include "nsIConverterInputStream.h"
 #include "nsIDocument.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsISeekableStream.h"
 #include "nsIUnicharInputStream.h"
 #include "nsIUnicodeDecoder.h"
-#include "nsIRemoteBlob.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIUUIDGenerator.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsStringStream.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
 #include "mozilla/SHA1.h"
@@ -550,17 +550,21 @@ ParseSize(int64_t aSize, int64_t& aStart
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   const Sequence<BlobPart>& aData,
                   const nsAString& aName,
                   const FilePropertyBag& aBag,
                   ErrorResult& aRv)
 {
-  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(aName);
+  // Normalizing the filename
+  nsString name(aName);
+  name.ReplaceChar('/', ':');
+
+  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(name);
 
   nsAutoString type(aBag.mType);
   MakeValidBlobType(type);
   impl->InitializeBlob(aGlobal.Context(), aData, type, false, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
rename from dom/base/File.h
rename to dom/file/File.h
rename from dom/base/FileList.cpp
rename to dom/file/FileList.cpp
rename from dom/base/FileList.h
rename to dom/file/FileList.h
rename from dom/base/FileReader.cpp
rename to dom/file/FileReader.cpp
rename from dom/base/FileReader.h
rename to dom/file/FileReader.h
rename from dom/base/MultipartBlobImpl.cpp
rename to dom/file/MultipartBlobImpl.cpp
rename from dom/base/MultipartBlobImpl.h
rename to dom/file/MultipartBlobImpl.h
rename from dom/base/MutableBlobStorage.cpp
rename to dom/file/MutableBlobStorage.cpp
--- a/dom/base/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/File.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsNetCID.h"
+#include "nsProxyRelease.h"
 #include "WorkerPrivate.h"
 
 #define BLOB_MEMORY_TEMPORARY_FILE 1048576
 
 namespace mozilla {
 namespace dom {
 
 namespace {
rename from dom/base/MutableBlobStorage.h
rename to dom/file/MutableBlobStorage.h
rename from dom/base/MutableBlobStreamListener.cpp
rename to dom/file/MutableBlobStreamListener.cpp
rename from dom/base/MutableBlobStreamListener.h
rename to dom/file/MutableBlobStreamListener.h
rename from dom/ipc/Blob.cpp
rename to dom/file/ipc/Blob.cpp
--- a/dom/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -384,30 +384,16 @@ OptionalFileDescriptorSetToFDs(OptionalF
 
     default:
       MOZ_CRASH("Unknown type!");
   }
 
   MOZ_CRASH("Should never get here!");
 }
 
-class NS_NO_VTABLE IPrivateRemoteInputStream
-  : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
-
-  // This will return the underlying stream.
-  virtual nsIInputStream*
-  BlockAndGetInternalStream() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream,
-                              PRIVATE_REMOTE_INPUT_STREAM_IID)
-
 // This class exists to keep a blob alive at least as long as its internal
 // stream.
 class BlobInputStreamTether final
   : public nsIMultiplexInputStream
   , public nsISeekableStream
   , public nsIIPCSerializableInputStream
   , public nsIFileMetadata
 {
@@ -479,16 +465,32 @@ NS_INTERFACE_MAP_BEGIN(BlobInputStreamTe
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mWeakSeekableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakSerializableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata,
                                      mWeakFileMetadata)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
+} // namespace
+
+class NS_NO_VTABLE IPrivateRemoteInputStream
+  : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
+
+  // This will return the underlying stream.
+  virtual nsIInputStream*
+  BlockAndGetInternalStream() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream,
+                              PRIVATE_REMOTE_INPUT_STREAM_IID)
+
 class RemoteInputStream final
   : public nsIInputStream
   , public nsISeekableStream
   , public nsIIPCSerializableInputStream
   , public nsIFileMetadata
   , public IPrivateRemoteInputStream
 {
   Monitor mMonitor;
@@ -553,16 +555,18 @@ private:
   NS_DECL_NSISEEKABLESTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIFILEMETADATA
 
   nsIInputStream*
   BlockAndGetInternalStream() override;
 };
 
+namespace {
+
 class InputStreamChild final
   : public PBlobStreamChild
 {
   RefPtr<RemoteInputStream> mRemoteStream;
 
 public:
   explicit
   InputStreamChild(RemoteInputStream* aRemoteStream)
@@ -579,111 +583,16 @@ public:
 
 private:
   // This method is only called by the IPDL message machinery.
   mozilla::ipc::IPCResult
   Recv__delete__(const InputStreamParams& aParams,
                  const OptionalFileDescriptorSet& aFDs) override;
 };
 
-class InputStreamParent final
-  : public PBlobStreamParent
-{
-  typedef mozilla::ipc::InputStreamParams InputStreamParams;
-  typedef mozilla::dom::OptionalFileDescriptorSet OptionalFileDescriptorSet;
-
-  bool* mSyncLoopGuard;
-  InputStreamParams* mParams;
-  OptionalFileDescriptorSet* mFDs;
-
-#ifdef DEBUG
-  PRThread* mOwningThread;
-#endif
-
-public:
-  InputStreamParent()
-    : mSyncLoopGuard(nullptr)
-    , mParams(nullptr)
-    , mFDs(nullptr)
-  {
-#ifdef DEBUG
-    mOwningThread = PR_GetCurrentThread();
-#endif
-
-    AssertIsOnOwningThread();
-
-    MOZ_COUNT_CTOR(InputStreamParent);
-  }
-
-  InputStreamParent(bool* aSyncLoopGuard,
-                    InputStreamParams* aParams,
-                    OptionalFileDescriptorSet* aFDs)
-    : mSyncLoopGuard(aSyncLoopGuard)
-    , mParams(aParams)
-    , mFDs(aFDs)
-  {
-#ifdef DEBUG
-    mOwningThread = PR_GetCurrentThread();
-#endif
-
-    AssertIsOnOwningThread();
-    MOZ_ASSERT(aSyncLoopGuard);
-    MOZ_ASSERT(!*aSyncLoopGuard);
-    MOZ_ASSERT(aParams);
-    MOZ_ASSERT(aFDs);
-
-    MOZ_COUNT_CTOR(InputStreamParent);
-  }
-
-  ~InputStreamParent() override
-  {
-    AssertIsOnOwningThread();
-
-    MOZ_COUNT_DTOR(InputStreamParent);
-  }
-
-  void
-  AssertIsOnOwningThread() const
-  {
-#ifdef DEBUG
-    MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
-#endif
-  }
-
-  bool
-  Destroy(const InputStreamParams& aParams,
-          const OptionalFileDescriptorSet& aFDs)
-  {
-    AssertIsOnOwningThread();
-
-    if (mSyncLoopGuard) {
-      MOZ_ASSERT(!*mSyncLoopGuard);
-
-      *mSyncLoopGuard = true;
-      *mParams = aParams;
-      *mFDs = aFDs;
-
-      // We're not a live actor so manage the memory ourselves.
-      delete this;
-      return true;
-    }
-
-    // This will be destroyed by BlobParent::DeallocPBlobStreamParent.
-    return PBlobStreamParent::Send__delete__(this, aParams, aFDs);
-  }
-
-private:
-  // This method is only called by the IPDL message machinery.
-  void
-  ActorDestroy(ActorDestroyReason aWhy) override
-  {
-    // Nothing needs to be done here.
-  }
-};
-
 struct MOZ_STACK_CLASS CreateBlobImplMetadata final
 {
   nsString mContentType;
   nsString mName;
   uint64_t mLength;
   int64_t mLastModifiedDate;
   bool mHasRecursed;
 
@@ -704,16 +613,18 @@ struct MOZ_STACK_CLASS CreateBlobImplMet
 
   bool
   IsFile() const
   {
     return !mName.IsVoid();
   }
 };
 
+} // namespace
+
 already_AddRefed<BlobImpl>
 CreateBlobImpl(const nsID& aKnownBlobIDData,
                const CreateBlobImplMetadata& aMetadata)
 {
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   MOZ_ASSERT(aMetadata.mHasRecursed);
 
   RefPtr<BlobImpl> blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData);
@@ -1430,18 +1341,110 @@ RemoteInputStream::BlockAndGetInternalSt
   MOZ_ASSERT(!IsOnOwningThread());
 
   nsresult rv = BlockAndWaitForStream();
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return mStream;
 }
 
-} // namespace
-
+class InputStreamParent final
+  : public PBlobStreamParent
+{
+  typedef mozilla::ipc::InputStreamParams InputStreamParams;
+  typedef mozilla::dom::OptionalFileDescriptorSet OptionalFileDescriptorSet;
+
+  bool* mSyncLoopGuard;
+  InputStreamParams* mParams;
+  OptionalFileDescriptorSet* mFDs;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
+public:
+  InputStreamParent()
+    : mSyncLoopGuard(nullptr)
+    , mParams(nullptr)
+    , mFDs(nullptr)
+  {
+#ifdef DEBUG
+    mOwningThread = PR_GetCurrentThread();
+#endif
+
+    AssertIsOnOwningThread();
+
+    MOZ_COUNT_CTOR(InputStreamParent);
+  }
+
+  InputStreamParent(bool* aSyncLoopGuard,
+                    InputStreamParams* aParams,
+                    OptionalFileDescriptorSet* aFDs)
+    : mSyncLoopGuard(aSyncLoopGuard)
+    , mParams(aParams)
+    , mFDs(aFDs)
+  {
+#ifdef DEBUG
+    mOwningThread = PR_GetCurrentThread();
+#endif
+
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(aSyncLoopGuard);
+    MOZ_ASSERT(!*aSyncLoopGuard);
+    MOZ_ASSERT(aParams);
+    MOZ_ASSERT(aFDs);
+
+    MOZ_COUNT_CTOR(InputStreamParent);
+  }
+
+  ~InputStreamParent() override
+  {
+    AssertIsOnOwningThread();
+
+    MOZ_COUNT_DTOR(InputStreamParent);
+  }
+
+  void
+  AssertIsOnOwningThread() const
+  {
+#ifdef DEBUG
+    MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+#endif
+  }
+
+  bool
+  Destroy(const InputStreamParams& aParams,
+          const OptionalFileDescriptorSet& aFDs)
+  {
+    AssertIsOnOwningThread();
+
+    if (mSyncLoopGuard) {
+      MOZ_ASSERT(!*mSyncLoopGuard);
+
+      *mSyncLoopGuard = true;
+      *mParams = aParams;
+      *mFDs = aFDs;
+
+      // We're not a live actor so manage the memory ourselves.
+      delete this;
+      return true;
+    }
+
+    // This will be destroyed by BlobParent::DeallocPBlobStreamParent.
+    return PBlobStreamParent::Send__delete__(this, aParams, aFDs);
+  }
+
+private:
+  // This method is only called by the IPDL message machinery.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override
+  {
+    // Nothing needs to be done here.
+  }
+};
 StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
 StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
 
 /*******************************************************************************
  * BlobParent::IDTableEntry Declaration
  ******************************************************************************/
 
 class BlobParent::IDTableEntry final
rename from dom/ipc/BlobChild.h
rename to dom/file/ipc/BlobChild.h
rename from dom/ipc/BlobParent.h
rename to dom/file/ipc/BlobParent.h
rename from dom/ipc/BlobTypes.ipdlh
rename to dom/file/ipc/BlobTypes.ipdlh
rename from dom/ipc/PBlob.ipdl
rename to dom/file/ipc/PBlob.ipdl
rename from dom/ipc/PBlobStream.ipdl
rename to dom/file/ipc/PBlobStream.ipdl
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/moz.build
@@ -0,0 +1,36 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom.ipc += [
+    'BlobChild.h',
+    'BlobParent.h',
+    'nsIRemoteBlob.h',
+]
+
+UNIFIED_SOURCES += [
+    'Blob.cpp',
+]
+
+IPDL_SOURCES += [
+    'BlobTypes.ipdlh',
+    'PBlob.ipdl',
+    'PBlobStream.ipdl',
+]
+
+LOCAL_INCLUDES += [
+    '/dom/file',
+    '/dom/ipc',
+    '/dom/workers',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wno-error=shadow']
rename from dom/ipc/nsIRemoteBlob.h
rename to dom/file/ipc/nsIRemoteBlob.h
new file mode 100644
--- /dev/null
+++ b/dom/file/moz.build
@@ -0,0 +1,50 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ['ipc']
+
+XPIDL_SOURCES += [
+    'nsIDOMBlob.idl',
+    'nsIDOMFileList.idl',
+]
+
+XPIDL_MODULE = 'dom_file'
+
+EXPORTS += [
+    'nsHostObjectProtocolHandler.h',
+    'nsHostObjectURI.h',
+]
+
+EXPORTS.mozilla.dom += [
+    'BlobSet.h',
+    'File.h',
+    'FileList.h',
+    'FileReader.h',
+    'MutableBlobStorage.h',
+    'MutableBlobStreamListener.h',
+]
+
+UNIFIED_SOURCES += [
+    'BlobSet.cpp',
+    'File.cpp',
+    'FileList.cpp',
+    'FileReader.cpp',
+    'MultipartBlobImpl.cpp',
+    'MutableBlobStorage.cpp',
+    'MutableBlobStreamListener.cpp',
+    'nsHostObjectProtocolHandler.cpp',
+    'nsHostObjectURI.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/dom/workers',
+]
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
rename from dom/base/nsHostObjectProtocolHandler.cpp
rename to dom/file/nsHostObjectProtocolHandler.cpp
rename from dom/base/nsHostObjectProtocolHandler.h
rename to dom/file/nsHostObjectProtocolHandler.h
rename from dom/base/nsHostObjectURI.cpp
rename to dom/file/nsHostObjectURI.cpp
rename from dom/base/nsHostObjectURI.h
rename to dom/file/nsHostObjectURI.h
rename from dom/base/nsIDOMBlob.idl
rename to dom/file/nsIDOMBlob.idl
rename from dom/base/nsIDOMFileList.idl
rename to dom/file/nsIDOMFileList.idl
rename from dom/base/test/create_file_objects.js
rename to dom/file/tests/create_file_objects.js
rename from dom/base/test/file_blobURL_expiring.html
rename to dom/file/tests/file_blobURL_expiring.html
rename from dom/base/test/file_mozfiledataurl_audio.ogg
rename to dom/file/tests/file_mozfiledataurl_audio.ogg
rename from dom/base/test/file_mozfiledataurl_doc.html
rename to dom/file/tests/file_mozfiledataurl_doc.html
copy from dom/base/test/file_mozfiledataurl_img.jpg
copy to dom/file/tests/file_mozfiledataurl_img.jpg
rename from dom/base/test/file_mozfiledataurl_inner.html
rename to dom/file/tests/file_mozfiledataurl_inner.html
rename from dom/base/test/file_mozfiledataurl_text.txt
rename to dom/file/tests/file_mozfiledataurl_text.txt
rename from dom/base/test/file_nonascii_blob_url.html
rename to dom/file/tests/file_nonascii_blob_url.html
rename from dom/base/test/fileapi_chromeScript.js
rename to dom/file/tests/fileapi_chromeScript.js
rename from dom/base/test/fileutils.js
rename to dom/file/tests/fileutils.js
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/mochitest.ini
@@ -0,0 +1,27 @@
+[DEFAULT]
+support-files =
+  create_file_objects.js
+  file_blobURL_expiring.html
+  file_mozfiledataurl_img.jpg
+  file_mozfiledataurl_audio.ogg
+  file_mozfiledataurl_doc.html
+  file_mozfiledataurl_text.txt
+  file_mozfiledataurl_inner.html
+  file_nonascii_blob_url.html
+  fileutils.js
+  fileapi_chromeScript.js
+  !/dom/html/test/form_submit_server.sjs
+  !/dom/xhr/tests/file_XHRSendData.sjs
+
+[test_blob_fragment_and_query.html]
+[test_blobconstructor.html]
+[test_blobURL_expiring.html]
+[test_file_from_blob.html]
+[test_ipc_messagemanager_blob.html]
+[test_nonascii_blob_url.html]
+[test_file_negative_date.html]
+[test_fileapi.html]
+[test_fileapi_slice.html]
+skip-if = (toolkit == 'android') # Android: Bug 775227
+[test_mozfiledataurl.html]
+skip-if = toolkit == 'android' #TIMED_OUT
rename from dom/base/test/test_blobURL_expiring.html
rename to dom/file/tests/test_blobURL_expiring.html
rename from dom/base/test/test_blob_fragment_and_query.html
rename to dom/file/tests/test_blob_fragment_and_query.html
rename from dom/base/test/test_blobconstructor.html
rename to dom/file/tests/test_blobconstructor.html
rename from dom/base/test/test_file_from_blob.html
rename to dom/file/tests/test_file_from_blob.html
rename from dom/base/test/test_file_negative_date.html
rename to dom/file/tests/test_file_negative_date.html
rename from dom/base/test/test_fileapi.html
rename to dom/file/tests/test_fileapi.html
rename from dom/base/test/test_fileapi_slice.html
rename to dom/file/tests/test_fileapi_slice.html
rename from dom/base/test/test_ipc_messagemanager_blob.html
rename to dom/file/tests/test_ipc_messagemanager_blob.html
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/test_mozfiledataurl.html
@@ -0,0 +1,223 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+  <title>Test for File urls</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="start()">
+<p id="display">
+<iframe id=inner></iframe>
+<iframe id=iframe></iframe>
+<img id=img onload="gen.next(event);">
+<audio id=audio onloadeddata="gen.next(event);">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+try {
+  URL.createObjectURL(undefined);
+} catch(e) { }
+
+window.addEventListener("message", function(e) {
+  gen.next(JSON.parse(e.data));
+}, false);
+
+const innerSameSiteURI = "file_mozfiledataurl_inner.html";
+const innerCrossSiteURI = "http://example.com/tests/dom/file/tests/file_mozfiledataurl_inner.html"
+
+var fileNames = ["file_mozfiledataurl_img.jpg",
+                 "file_mozfiledataurl_audio.ogg",
+                 "file_mozfiledataurl_doc.html",
+                 "file_mozfiledataurl_text.txt"];
+
+function start() {
+  let xhr = new XMLHttpRequest;
+  xhr.open("GET", "/dynamic/getMyDirectory.sjs", false);
+  xhr.send();
+  let basePath = xhr.responseText;
+
+  let fullFileNames = [];
+  for (let name of fileNames) {
+    fullFileNames.push(basePath + name);
+  }
+
+  var script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("create_file_objects.js"));
+
+  script.addMessageListener("created-file-objects", function handler(files) {
+    script.removeMessageListener("created-file-objects", handler);
+    gen = runTest(files);
+    gen.next();
+  });
+
+  script.sendAsyncMessage("create-file-objects", {fileNames: fullFileNames});
+};
+
+SimpleTest.waitForExplicitFinish();
+
+function* runTest([imgFile, audioFile, docFile, xhrFile]) {
+  inner = document.getElementById('inner');
+  img = document.getElementById('img');
+  audio = document.getElementById('audio');
+  iframe = document.getElementById('iframe');
+  inner.onload = function() { gen.next("inner loaded"); };
+
+  // Attempt to load a image in this document
+  var fileurl = URL.createObjectURL(imgFile);
+  img.src = fileurl;
+  var e = (yield);
+  is(e.type, "load", "loaded successfully");
+  is(img.width, 120, "correct width");
+  is(img.height, 90, "correct height");
+
+  // Revoke url and attempt to load a image in this document
+  img.src = "file_mozfiledataurl_img.jpg";
+  is((yield).type, "load", "successfull reset image");
+  URL.revokeObjectURL(fileurl);
+  todo(false, "urls need to act like 404s, not fail to parse");
+/*  img.src = fileurl;
+  var e = (yield);
+  is(e.type, "error", "failed successfully");
+  isnot(img.width, 120, "correct error width");
+  isnot(img.height, 90, "correct error height");
+*/
+  // Generate new fileurl and make sure it's different from the old
+  var oldFileurl = fileurl;
+  fileurl = URL.createObjectURL(imgFile);
+  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
+
+  // Attempt to load an image in a different same-origin document
+  inner.src = innerSameSiteURI;
+  yield undefined;
+  inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "load", "loaded successfully");
+  is(res.width, 120, "correct width");
+  is(res.height, 90, "correct height");
+
+  // Attempt to load an image in a different cross-origin document
+  inner.src = innerCrossSiteURI;
+  yield undefined;
+  inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "error", "failed successfully");
+  isnot(res.width, 120, "correct error width");
+  isnot(res.height, 90, "correct error height");
+
+  // Attempt to load an audio in this document
+  fileurl = URL.createObjectURL(audioFile);
+  audio.src = fileurl;
+  var e = (yield);
+  is(e.type, "loadeddata", "loaded successfully");
+
+  // Revoke url and attempt to load a audio in this document
+  audio.src = "file_mozfiledataurl_audio.ogg";
+  is((yield).type, "loadeddata", "successfully reset audio");
+  URL.revokeObjectURL(fileurl);
+  todo(false, "urls need to act like 404s, not fail to parse");
+/*  img.src = fileurl;
+  var e = (yield);
+  is(e.type, "error", "failed successfully");
+  isnot(img.width, 120, "correct error width");
+  isnot(img.height, 90, "correct error height");
+*/
+  // Generate new fileurl and make sure it's different from the old
+  var oldFileurl = fileurl;
+  fileurl = URL.createObjectURL(audioFile);
+  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
+
+  // Attempt to load an audio in a different same-origin document
+  inner.src = innerSameSiteURI;
+  yield undefined;
+  inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "loadeddata", "loaded successfully");
+
+  // Attempt to load an audio in a different cross-origin document
+  inner.src = innerCrossSiteURI;
+  yield undefined;
+  inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "error", "failed successfully");
+
+  // Attempt to load a HTML document in an iframe in this document
+  iframe.onload = function() { gen.next(); };
+  iframe.src = "file_mozfiledataurl_doc.html";
+  yield undefined;
+  is(iframe.contentDocument.getElementsByTagName("p")[0].textContent,
+     "This here is a document!",
+     "iframe loaded successfully");
+  is(iframe.contentDocument.getElementById("img").width, 120,
+     "image in iframe width");
+  is(iframe.contentDocument.getElementById("img").height, 90,
+     "image in iframe height");
+
+  // Attempt to load a HTML document in an iframe in this document, using file url
+  fileurl = URL.createObjectURL(docFile);
+  iframe.src = fileurl;
+  yield undefined;
+  is(iframe.contentDocument.getElementsByTagName("p")[0].textContent,
+     "This here is a document!",
+     "iframe loaded successfully");
+  isnot(iframe.contentDocument.getElementById("img").width, 120,
+        "failed image in iframe width");
+  isnot(iframe.contentDocument.getElementById("img").height, 90,
+        "failed image in iframe height");
+
+  // Attempt to load a HTML document in an iframe in inner document
+  inner.src = innerSameSiteURI;
+  is((yield), "inner loaded", "correct gen.next()");
+  inner.contentWindow.postMessage(JSON.stringify({iframe:"file_mozfiledataurl_doc.html"}), "*");
+  var res = (yield);
+  is(res.type, "load", "loaded successfully");
+  is(res.text, "This here is a document!", "loaded successfully");
+  is(res.imgWidth, 120, "correct width");
+
+  // Attempt to load a HTML document in an iframe in inner document, using file url
+  inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "load", "loaded successfully");
+  is(res.text, "This here is a document!", "loaded successfully");
+  isnot(res.imgWidth, 120, "correct width");
+
+  // Attempt to load a HTML document in an iframe in inner cross-site document, using file url
+  inner.src = innerCrossSiteURI;
+  is((yield), "inner loaded", "correct gen.next()");
+  inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*");
+  var res = (yield);
+  is(res.type, "error", "load failed successfully");
+
+  // Attempt to load file url using XHR
+  fileurl = URL.createObjectURL(xhrFile);
+  xhr = new XMLHttpRequest;
+  xhr.onload = function() { gen.next("XHR finished"); };
+  xhr.open("GET", fileurl);
+  xhr.send();
+  is((yield), "XHR finished", "correct gen.next()");
+  xhr.responseText == "Yarr, here be plaintext file, ya landlubber\n";
+
+  // Attempt to load file url using XHR in inner document
+  inner.src = innerSameSiteURI;
+  is((yield), "inner loaded", "correct gen.next()");
+  inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*");
+  var res = (yield);
+  is(res.didThrow, undefined, "load successful");
+  is(res.text, "Yarr, here be plaintext file, ya landlubber\n", "load successful");
+
+  // Attempt to load file url using XHR
+  inner.src = innerCrossSiteURI;
+  is((yield), "inner loaded", "correct gen.next()");
+  inner.contentWindow.postMessage(JSON.stringify({xhr:fileurl}), "*");
+  var res = (yield);
+  is(res.didError, true, "load failed successfully");
+
+  SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
rename from dom/base/test/test_nonascii_blob_url.html
rename to dom/file/tests/test_nonascii_blob_url.html
--- a/dom/base/test/test_nonascii_blob_url.html
+++ b/dom/file/tests/test_nonascii_blob_url.html
@@ -7,17 +7,17 @@
   <script type="text/javascript" src="fileutils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <div id="content"></div>
 <script class="testbody" type="text/javascript;version=1.7">
 
 var iframe = document.createElement('iframe');
-iframe.src = 'http://xn--exmple-cua.test/tests/dom/base/test/file_nonascii_blob_url.html';
+iframe.src = 'http://xn--exmple-cua.test/tests/dom/file/tests/file_nonascii_blob_url.html';
 iframe.onload = function() {
   iframe.contentWindow.postMessage('hello world', '*');
   onmessage = function(e) {
     is(e.data, 'hello world', "Blob URL for non-ascii domain works");
     SimpleTest.finish();
   }
 }
 
--- a/dom/filesystem/tests/mochitest.ini
+++ b/dom/filesystem/tests/mochitest.ini
@@ -2,8 +2,9 @@
 support-files =
   filesystem_commons.js
   script_fileList.js
   worker_basic.js
 
 [test_basic.html]
 [test_webkitdirectory.html]
 [test_worker_basic.html]
+[test_bug1319088.html]
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/tests/test_bug1319088.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for bug 1319088</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="input" type="file"></input>
+
+<script type="application/javascript;version=1.7">
+
+function testSetup() {
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["dom.webkitBlink.dirPicker.enabled", true]]}, next);
+}
+
+function populateInputFile() {
+  var url = SimpleTest.getTestFileURL("script_fileList.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  function onOpened(message) {
+    var input = document.getElementById("input");
+    SpecialPowers.wrap(input).mozSetFileArray([message.file]);
+
+    script.destroy();
+    next();
+  }
+
+  script.addMessageListener("file.opened", onOpened);
+  script.sendAsyncMessage("file.open");
+}
+
+function checkBug() {
+  var input = document.getElementById("input");
+  is(input.files[0].webkitRelativePath, "", "No relative path!");
+
+  let form = document.createElement('form');
+  form.appendChild(input);
+
+  is(input.files[0].webkitRelativePath, "", "No relative path!");
+  SimpleTest.finish();
+}
+
+var tests = [
+  testSetup,
+  populateInputFile,
+  checkBug,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
--- a/dom/gamepad/GamepadManager.cpp
+++ b/dom/gamepad/GamepadManager.cpp
@@ -679,17 +679,16 @@ GamepadManager::ActorCreated(PBackground
   MOZ_ASSERT(initedChild == child);
   child->SendGamepadListenerAdded();
   mChannelChildren.AppendElement(child);
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
   // Construct VRManagerChannel and ask adding the connected
   // VR controllers to GamepadManager
   mVRChannelChild = gfx::VRManagerChild::Get();
-  mVRChannelChild->SetGamepadManager(this);
   mVRChannelChild->SendControllerListenerAdded();
 #endif
 }
 
 //Override nsIIPCBackgroundChildCreateCallback
 void
 GamepadManager::ActorFailed()
 {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -355,28 +355,17 @@ class HTMLInputElementState final : publ
 
     void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
     {
       mBlobImplsOrDirectoryPaths.Clear();
       for (uint32_t i = 0; i < aArray.Length(); ++i) {
         if (aArray[i].IsFile()) {
           BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement();
 
-          RefPtr<File> file = aArray[i].GetAsFile();
-
-          nsAutoString name;
-          file->GetName(name);
-
-          nsAutoString path;
-          path.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
-          path.Append(name);
-
-          file->SetPath(path);
-
-          data->mBlobImpl = file->Impl();
+          data->mBlobImpl = aArray[i].GetAsFile()->Impl();
           data->mType = BlobImplOrDirectoryPath::eBlobImpl;
         } else {
           MOZ_ASSERT(aArray[i].IsDirectory());
           nsAutoString fullPath;
           nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             continue;
           }
--- a/dom/html/test/forms/test_input_color_input_change_events.html
+++ b/dom/html/test/forms/test_input_color_input_change_events.html
@@ -20,17 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   var MockColorPicker = SpecialPowers.MockColorPicker;
 
   var test = runTest();
 
   SimpleTest.waitForFocus(function() {
     test.next();
   });
 
-  function runTest() {
+  function* runTest() {
     MockColorPicker.init(window);
     var element = null;
 
     MockColorPicker.showCallback = function(picker, update) {
       is(picker.initialColor, element.value);
 
       var inputEvent = false;
       var changeEvent = false;
@@ -95,17 +95,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       element = document.getElementsByTagName('input')[i];
       element.focus();
       synthesizeMouseAtCenter(element, {});
       yield undefined;
     };
 
     MockColorPicker.cleanup();
     SimpleTest.finish();
-    yield undefined;
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
 <p id="display"></p>
 <div id="content">
--- a/dom/html/test/forms/test_input_color_picker_initial.html
+++ b/dom/html/test/forms/test_input_color_picker_initial.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   var MockColorPicker = SpecialPowers.MockColorPicker;
 
   var test = runTest();
 
   SimpleTest.waitForFocus(function() {
     test.next();
   });
 
-  function runTest() {
+  function* runTest() {
     MockColorPicker.init(window);
     var element = null;
 
     MockColorPicker.showCallback = function(picker) {
       is(picker.initialColor, element.value);
       SimpleTest.executeSoon(function() {
         test.next();
       });
@@ -42,17 +42,16 @@ https://bugzilla.mozilla.org/show_bug.cg
         element.value = '#deadbe';
       }
       synthesizeMouseAtCenter(element, {});
       yield undefined;
     };
 
     MockColorPicker.cleanup();
     SimpleTest.finish();
-    yield undefined;
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
 <p id="display"></p>
 <div id="content">
--- a/dom/html/test/forms/test_input_color_picker_popup.html
+++ b/dom/html/test/forms/test_input_color_picker_popup.html
@@ -40,17 +40,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     { id: 'div-click-on-demand', result: true },
   ];
   var currentTest = null;
 
   SimpleTest.waitForFocus(function() {
     test.next();
   });
 
-  function runTest() {
+  function* runTest() {
     MockColorPicker.init(window);
     var element = null;
 
     MockColorPicker.showCallback = function(picker) {
       ok(currentTest.result);
       SimpleTest.executeSoon(function() {
         test.next();
       });
@@ -107,17 +107,16 @@ https://bugzilla.mozilla.org/show_bug.cg
           });
         });
       }
       yield undefined;
     };
 
     MockColorPicker.cleanup();
     SimpleTest.finish();
-    yield undefined;
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
 <p id="display"></p>
 <div id="content">
--- a/dom/html/test/forms/test_input_color_picker_update.html
+++ b/dom/html/test/forms/test_input_color_picker_update.html
@@ -21,17 +21,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   var MockColorPicker = SpecialPowers.MockColorPicker;
 
   var test = runTest();
 
   SimpleTest.waitForFocus(function() {
     test.next();
   });
 
-  function runTest() {
+  function* runTest() {
     MockColorPicker.init(window);
     var element = null;
 
     MockColorPicker.showCallback = function(picker, update) {
       is(picker.initialColor, element.value);
 
       if (element.dataset.type == 'update') {
         update('#f00ba4');
@@ -63,17 +63,16 @@ https://bugzilla.mozilla.org/show_bug.cg
     for (var i = 0; i < document.getElementsByTagName('input').length; ++i) {
       element = document.getElementsByTagName('input')[i];
       synthesizeMouseAtCenter(element, {});
       yield undefined;
     };
 
     MockColorPicker.cleanup();
     SimpleTest.finish();
-    yield undefined;
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
 <p id="display"></p>
 <div id="content">
--- a/dom/html/test/forms/test_input_textarea_set_value_no_scroll.html
+++ b/dom/html/test/forms/test_input_textarea_set_value_no_scroll.html
@@ -84,26 +84,25 @@ https://bugzilla.mozilla.org/show_bug.cg
     results = compareSnapshots(baseSnapshot, tmpSnapshot, true);
     ok(results[0], "textarea view should look like the beginning");
 
     setTimeout(function() {
       gTestRunner.next();
     }, 0);
   }
 
-  function runTest()
+  function* runTest()
   {
     test('input');
     yield undefined;
     test('textarea');
     yield undefined;
     testCorrectUpdateOnScroll();
     yield undefined;
     SimpleTest.finish();
-    yield undefined;
   }
 
   gTestRunner = runTest();
 
   SimpleTest.waitForFocus(function() {
     gTestRunner.next();
   });;
 
--- a/dom/html/test/forms/test_input_typing_sanitization.html
+++ b/dom/html/test/forms/test_input_typing_sanitization.html
@@ -44,24 +44,19 @@ function sendKeyEventToSubmitForm() {
 }
 
 function urlify(aStr) {
   return aStr.replace(/:/g, '%3A');
 }
 
 function runTestsForNextInputType()
 {
-  try {
-    testRunner.next();
-  } catch (e) {
-    if (e.toString() == '[object StopIteration]') {
-      SimpleTest.finish();
-    } else {
-      throw StopIteration;
-    }
+  let {done} = testRunner.next();
+  if (done) {
+    SimpleTest.finish();
   }
 }
 
 function checkValueSubmittedIsValid()
 {
   is(frames['submit_frame'].location.href,
      'http://mochi.test:8888/tests/dom/html/test/forms/foo?i='
      + urlify(gValidData[valueIndex++]),
@@ -111,17 +106,17 @@ function testSubmissions() {
   submitMethod();
 }
 
 var valueIndex = 0;
 var submitMethod = submitForm;
 
 SimpleTest.waitForExplicitFinish();
 
-function runTest()
+function* runTest()
 {
   SimpleTest.requestLongerTimeout(4);
 
   var data = [
     {
       type: 'number',
       canHaveBadInputValidityState: true,
       validData: [
--- a/dom/html/test/test_anchor_ping.html
+++ b/dom/html/test/test_anchor_ping.html
@@ -18,21 +18,21 @@ var Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 addLoadEvent(function () {
-  Task.spawn(function run_tests() {
+  Task.spawn(function* run_tests() {
     while (tests.length) {
       let test = tests.shift();
       info("-- running " + test.name);
-      yield Task.spawn(test);
+      yield* test();
     }
 
     SimpleTest.finish();
   });
 });
 
 let tests = [
 
--- a/dom/html/test/test_bug209275.xhtml
+++ b/dom/html/test/test_bug209275.xhtml
@@ -95,17 +95,17 @@ function setXlinkHref(elem, href) {
 
 function continueTest() {
   gGen.next();
 }
 
 var iframe = document.getElementById("iframe");
 var iframeCw = iframe.contentWindow;
 
-function run() {
+function* run() {
   var iframe = document.getElementById("iframe");
   var iframeCw = iframe.contentWindow;
 
   // First, set the visited/unvisited link/ellipse colors.
   const unvisitedColor = "rgb(0, 0, 238)";
   const visitedColor = "rgb(85, 26, 139)";
   const unvisitedFill = "rgb(0, 0, 255)";
   const visitedFill = "rgb(128, 0, 128)";
@@ -240,17 +240,16 @@ function run() {
   // tags it has.
   iframeCw.location = "file_bug209275_3.html";
   yield undefined; // wait for callback from child
   is(iframeCw.document.getElementById("link").href,
      "http://mochi.test:8888/",
      "Wrong href after document.open().");
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 window.addEventListener("load", function() {
   gGen = run();
   gGen.next();
 }, false);
 
 ]]>
--- a/dom/html/test/test_bug481335.xhtml
+++ b/dom/html/test/test_bug481335.xhtml
@@ -57,17 +57,17 @@ function checkLinkColor(aElmId, aExpecte
   while (getColor() != aExpectedColor) {
     setTimeout(continueTest, 0);
     return false;
   }
   is(getColor(), aExpectedColor, aMessage);
   return true;
 }
 
-function testIterator() {
+function* testIterator() {
   // After first load
   $("newparent").appendChild($("t"));
   is($("t").href, "http://www.example.com/" + rand,
      "Unexpected href after move");
   is($("t").href, "http://www.example.com/" + rand,
      "Unexpected cached href after move");
   while (!checkLinkColor("t", unvisitedColor, "Should be unvisited now"))
     yield undefined;
@@ -101,17 +101,16 @@ function testIterator() {
   yield undefined;
 
   // After third load
   while (!checkLinkColor("t", visitedColor,
                          "Should be visited now after third load")) {
     yield undefined;
   }
   SimpleTest.finish();
-  yield undefined;
 }
 
 addLoadEvent(function() {
   $("i").onload = continueTest;
   $("i").src = $("t").href;
 });
 ]]>
 </script>
--- a/dom/html/test/test_formSubmission.html
+++ b/dom/html/test/test_formSubmission.html
@@ -681,17 +681,17 @@ function setDisabled(list, state) {
 var gen;
 function onFilesSet() {
   gen = runTest();
   addLoadEvent(function() {
     gen.next();
   });
 }
 
-function runTest() {
+function* runTest() {
   // Set up the expectedSub array
   fileReader1 = new FileReader;
   fileReader1.readAsBinaryString(myFile1);
   fileReader2 = new FileReader;
   fileReader2.readAsBinaryString(myFile2);
   fileReader1.onload = fileReader2.onload = function() { gen.next(); };
   yield undefined; // Wait for both FileReaders. We don't care which order they finish.
   yield undefined;
@@ -811,15 +811,14 @@ function runTest() {
   addToFormData(fd);
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(fd);
   yield undefined;
   checkMPSubmission(JSON.parse(xhr.responseText),
                     expectedSub.concat(expectedAugment), "send augmented FormData");
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/test_formSubmission2.html
+++ b/dom/html/test/test_formSubmission2.html
@@ -157,17 +157,17 @@ function clickImage(aTarget, aX, aY)
   wu.sendMouseEvent('mousedown',  aX, aY, 0, 1, 0);
   wu.sendMouseEvent('mouseup',  aX, aY, 0, 0, 0);
 
   aTarget.style.position = "";
   aTarget.style.top = "";
   aTarget.style.left = "";
 }
 
-function runTest() {
+function* runTest() {
   // Make normal submission
   var form = $("form");
   var iframe = $("target_iframe");
   iframe.onload = function() { gen.next(); };
 
   var elements = form.querySelectorAll("input, button");
 
   is(elements.length, expectedSub.length,
@@ -207,15 +207,14 @@ function runTest() {
     elem.disabled = false;
     form.onsubmit = undefined;
 
     var submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
     checkSubmission(submission, []);
   }
 
   SimpleTest.finish();
-  yield undefined;
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/indexedDB/test/browserHelpers.js
+++ b/dom/indexedDB/test/browserHelpers.js
@@ -11,17 +11,17 @@ var testException;
 function runTest()
 {
   testGenerator.next();
 }
 
 function finishTestNow()
 {
   if (testGenerator) {
-    testGenerator.close();
+    testGenerator.return();
     testGenerator = undefined;
   }
 }
 
 function finishTest()
 {
   setTimeout(finishTestNow, 0);
   setTimeout(() => {
@@ -35,17 +35,17 @@ function finishTest()
         message = testException;
       window.parent.postMessage(message, "*");
     }
   }, 0);
 }
 
 function grabEventAndContinueHandler(event)
 {
-  testGenerator.send(event);
+  testGenerator.next(event);
 }
 
 function errorHandler(event)
 {
   throw new Error("indexedDB error, code " + event.target.error.name);
 }
 
 function continueToNextStep()
--- a/dom/indexedDB/test/browser_forgetThisSiteAdd.html
+++ b/dom/indexedDB/test/browser_forgetThisSiteAdd.html
@@ -2,17 +2,17 @@
   Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html>
   <head>
     <title>Indexed Database Test</title>
 
     <script type="text/javascript;version=1.7">
-      function testSteps()
+      function* testSteps()
       {
         let request = indexedDB.open("browser_forgetThisSite.js", 11);
         request.onerror = grabEventAndContinueHandler;
         request.onupgradeneeded = grabEventAndContinueHandler;
         let event = yield undefined;
 
         if (event.type == "error") {
           testException = event.target.error.name;
--- a/dom/indexedDB/test/browser_forgetThisSiteGet.html
+++ b/dom/indexedDB/test/browser_forgetThisSiteGet.html
@@ -2,17 +2,17 @@
   Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html>
   <head>
     <title>Indexed Database Test</title>
 
     <script type="text/javascript;version=1.7">
-      function testSteps()
+      function* testSteps()
       {
         let request = indexedDB.open("browser_forgetThisSite.js");
         request.onerror = grabEventAndContinueHandler;
         request.onsuccess = grabEventAndContinueHandler;
         let event = yield undefined;
 
         if (event.type == "error") {
           testException = event.target.error.name;
--- a/dom/indexedDB/test/browser_permissionsPrompt.html
+++ b/dom/indexedDB/test/browser_permissionsPrompt.html
@@ -3,17 +3,17 @@
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html>
   <head>
     <meta charset=UTF-8>
     <title>Indexed Database Test</title>
 
     <script type="text/javascript;version=1.7">
-      function testSteps()
+      function* testSteps()
       {
         const name = window.location.pathname;
 
         let request = indexedDB.open(name, { version: 1,
                                              storage: "persistent" });
         request.onerror = grabEventAndContinueHandler;
         request.onsuccess = grabEventAndContinueHandler;
         let event = yield undefined;
--- a/dom/indexedDB/test/browser_permissionsPromptDeny.js
+++ b/dom/indexedDB/test/browser_permissionsPromptDeny.js
@@ -17,17 +17,17 @@ function promiseMessage(aMessage, browse
           resolve();
         else
           reject();
       });
     });
   });
 }
 
-add_task(function test1() {
+add_task(function* test1() {
   removePermission(testPageURL, "indexedDB");
 
   info("creating tab");
   gBrowser.selectedTab = gBrowser.addTab();
 
   info("loading test page: " + testPageURL);
   gBrowser.selectedBrowser.loadURI(testPageURL);
   yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
@@ -46,17 +46,17 @@ add_task(function test1() {
   yield promiseMessage("InvalidStateError", gBrowser);
 
   is(getPermission(testPageURL, "indexedDB"),
      Components.interfaces.nsIPermissionManager.DENY_ACTION,
      "Correct permission set");
   gBrowser.removeCurrentTab();
 });
 
-add_task(function test2() {
+add_task(function* test2() {
   info("creating private window");
   let win = yield BrowserTestUtils.openNewBrowserWindow({ private : true });
   
   info("creating private tab");
   win.gBrowser.selectedTab = win.gBrowser.addTab();
 
   info("loading test page: " + testPageURL);
   win.gBrowser.selectedBrowser.loadURI(testPageURL);
@@ -76,17 +76,17 @@ add_task(function test2() {
   is(getPermission(testPageURL, "indexedDB"),
      Components.interfaces.nsIPermissionManager.DENY_ACTION,
      "Correct permission set");
   unregisterAllPopupEventHandlers();
   win.gBrowser.removeCurrentTab();
   win.close();
 });
 
-add_task(function test3() {
+add_task(function* test3() {
   info("creating tab");
   gBrowser.selectedTab = gBrowser.addTab();
 
   info("loading test page: " + testPageURL);
   gBrowser.selectedBrowser.loadURI(testPageURL);
   yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
   registerPopupEventHandler("popupshowing", function () {
--- a/dom/indexedDB/test/chromeHelpers.js
+++ b/dom/indexedDB/test/chromeHelpers.js
@@ -14,24 +14,24 @@ if (!window.runTest) {
 
     testGenerator.next();
   }
 }
 
 function finishTest()
 {
   SimpleTest.executeSoon(function() {
-    testGenerator.close();
+    testGenerator.return();
     SimpleTest.finish();
   });
 }
 
 function grabEventAndContinueHandler(event)
 {
-  testGenerator.send(event);
+  testGenerator.next(event);
 }
 
 function continueToNextStep()
 {
   SimpleTest.executeSoon(function() {
     testGenerator.next();
   });
 }
--- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html
+++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html
@@ -16,44 +16,43 @@
                                 "');", "*");
     }
 
     function is(a, b, message) {
       ok(a == b, message);
     }
 
     function grabEventAndContinueHandler(event) {
-      testGenerator.send(event);
+      testGenerator.next(event);
     }
 
     function errorHandler(event) {
       ok(false, "indexedDB error, code " + event.target.errorCcode);
       finishTest();
     }
 
     function unexpectedSuccessHandler(event) {
       ok(false, "got success when it was not expected!");
       finishTest();
     }
 
     function finishTest() {
       // Let window.onerror have a chance to fire
       setTimeout(function() {
         setTimeout(function() {
-          testGenerator.close();
           window.parent.postMessage("SimpleTest.finish();", "*");
         }, 0);
       }, 0);
     }
 
     window.onerror = function(message, filename, lineno) {
       is(message, "ConstraintError", "Expect a constraint error");
     };
 
-    function testSteps() {
+    function* testSteps() {
       window.parent.SpecialPowers.addPermission("indexedDB", true, document);
 
       let request = indexedDB.open(window.location.pathname, 1);
       request.onsuccess = unexpectedSuccessHandler;
       request.onerror = grabEventAndContinueHandler;
       request.onupgradeneeded = grabEventAndContinueHandler;
       let event = yield undefined;
 
@@ -225,17 +224,16 @@
 
       event = yield undefined;
       is(event.type, "error", "Got request error event");
       is(event.target.error.name, "AbortError", "Right error");
       // Skip the verification of ConstraintError in window.onerror.
       event.preventDefault();
 
       finishTest();
-      yield undefined;
     }
   </script>
 
 </head>
 
 <body onload="testGenerator.next();"></body>
 
 </html>
--- a/dom/indexedDB/test/event_propagation_iframe.html
+++ b/dom/indexedDB/test/event_propagation_iframe.html
@@ -12,29 +12,29 @@
 
     function ok(val, message) {
       val = val ? "true" : "false";
       window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message +
                                 "');", "*");
     }
 
     function grabEventAndContinueHandler(event) {
-      testGenerator.send(event);
+      testGenerator.next(event);
     }
 
     function errorHandler(event) {
       ok(false, "indexedDB error, code " + event.target.error.name);
       finishTest();
     }
 
     function finishTest() {
       // Let window.onerror have a chance to fire
       setTimeout(function() {
         setTimeout(function() {
-          testGenerator.close();
+          testGenerator.return();
           ok(windowErrorCount == 1, "Good window.onerror count");
           window.parent.postMessage("SimpleTest.finish();", "*");
         }, 0);
       }, 0);
     }
 
     const eventChain = [
       "IDBRequest",
@@ -81,17 +81,17 @@
 
         captureCount = bubbleCount = atTargetCount = 0;
         if (windowErrorCount) {
           finishTest();
         }
       }
     }
 
-    function testSteps() {
+    function* testSteps() {
       window.parent.SpecialPowers.addPermission("indexedDB", true, document);
 
       let request = indexedDB.open(window.location.pathname, 1);
       request.onerror = errorHandler;
       request.onupgradeneeded = grabEventAndContinueHandler;
       let event = yield undefined;
 
       let db = event.target.result;
--- a/dom/indexedDB/test/exceptions_in_events_iframe.html
+++ b/dom/indexedDB/test/exceptions_in_events_iframe.html
@@ -18,44 +18,44 @@
                                 "');", "*");
     }
 
     function is(a, b, message) {
       ok(a == b, message);
     }
 
     function grabEventAndContinueHandler(event) {
-      testGenerator.send(event);
+      testGenerator.next(event);
     }
 
     function errorHandler(event) {
       ok(false, "indexedDB error, code " + event.target.error.name);
       finishTest();
     }
 
     function unexpectedSuccessHandler(event) {
       ok(false, "got success when it was not expected!");
       finishTest();
     }
 
     function finishTest() {
       // Let window.onerror have a chance to fire
       setTimeout(function() {
         setTimeout(function() {
-          testGenerator.close();
+          testGenerator.return();
           window.parent.postMessage("SimpleTest.finish();", "*");
         }, 0);
       }, 0);
     }
 
     window.onerror = function() {
       return false;
     };
 
-    function testSteps() {
+    function* testSteps() {
       window.parent.SpecialPowers.addPermission("indexedDB", true, document);
 
       // Test 1: Throwing an exception in an upgradeneeded handler should
       // abort the versionchange transaction and fire an error at the request.
       let request = indexedDB.open(window.location.pathname, 1);
       request.onerror = errorHandler;
       request.onsuccess = unexpectedSuccessHandler;
       request.onupgradeneeded = function () {
--- a/dom/indexedDB/test/file.js
+++ b/dom/indexedDB/test/file.js
@@ -85,17 +85,17 @@ function getNullFile(name, size)
   return getFile(name, "binary/null", getView(size));
 }
 
 // This needs to be async to make it available on workers too.
 function getWasmBinary(text)
 {
   let binary = getWasmBinarySync(text);
   SimpleTest.executeSoon(function() {
-    testGenerator.send(binary);
+    testGenerator.next(binary);
   });
 }
 
 function getWasmModule(binary)
 {
   let module = new WebAssembly.Module(binary);
   return module;
 }
@@ -215,17 +215,17 @@ function verifyWasmModule(module1, modul
   ok(code2 instanceof getGlobalForObject(code1).Uint8Array, "Instance of Uint8Array");
   ok(code1.length == code2.length, "Correct length");
   verifyBuffers(code1, code2);
   continueToNextStep();
 }
 
 function grabFileUsageAndContinueHandler(request)
 {
-  testGenerator.send(request.fileUsage);
+  testGenerator.next(request.fileUsage);
 }
 
 function getUsage(usageHandler)
 {
   let qms = SpecialPowers.Services.qms;
   let principal = SpecialPowers.wrap(document).nodePrincipal;
   let cb = SpecialPowers.wrapCallback(usageHandler);
   qms.getUsageForPrincipal(principal, cb);
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -41,17 +41,17 @@ function clearAllDatabases(callback) {
   request.callback = cb;
 }
 
 var testHarnessGenerator = testHarnessSteps();
 testHarnessGenerator.next();
 
 function testHarnessSteps() {
   function nextTestHarnessStep(val) {
-    testHarnessGenerator.send(val);
+    testHarnessGenerator.next(val);
   }
 
   let testScriptPath;
   let testScriptFilename;
 
   let scripts = document.getElementsByTagName("script");
   for (let i = 0; i < scripts.length; i++) {
     let src = scripts[i].src;
@@ -217,35 +217,32 @@ if (!window.runTest) {
 
 function finishTest()
 {
   SpecialPowers.notifyObserversInParentProcess(null,
                                                "disk-space-watcher",
                                                "free");
 
   SimpleTest.executeSoon(function() {
-    testGenerator.close();
-    testHarnessGenerator.close();
     clearAllDatabases(function() { SimpleTest.finish(); });
   });
 }
 
 function browserRunTest()
 {
   testGenerator.next();
 }
 
 function browserFinishTest()
 {
-  setTimeout(function() { testGenerator.close(); }, 0);
 }
 
 function grabEventAndContinueHandler(event)
 {
-  testGenerator.send(event);
+  testGenerator.next(event);
 }
 
 function continueToNextStep()
 {
   SimpleTest.executeSoon(function() {
     testGenerator.next();
   });
 }
@@ -437,17 +434,17 @@ function workerScript() {
     if (self._expectingUncaughtException) {
       self.ok(false, "expectUncaughtException was called but no uncaught "
                      + "exception was detected!");
     }
     self.postMessage({ op: "done" });
   };
 
   self.grabEventAndContinueHandler = function(_event_) {
-    testGenerator.send(_event_);
+    testGenerator.next(_event_);
   };
 
   self.continueToNextStep = function() {
     executeSoon(function() {
       testGenerator.next();
     });
   };
 
@@ -603,17 +600,17 @@ function workerScript() {
         info("Worker: all databases are cleared");
         if (self._clearAllDatabasesCallback) {
           self._clearAllDatabasesCallback();
         }
         break;
 
       case "getWasmBinaryDone":
         info("Worker: get wasm binary done");
-        testGenerator.send(message.wasmBinary);
+        testGenerator.next(message.wasmBinary);
         break;
 
       default:
         throw new Error("Received a bad message from parent: " +
                         JSON.stringify(message));
     }
   };
 
--- a/dom/indexedDB/test/test_bfcache.html
+++ b/dom/indexedDB/test/test_bfcache.html
@@ -28,17 +28,17 @@
                                  gOrigMaxTotalViewers);
         gOrigMaxTotalViewers = undefined;
         try {
           SpecialPowers.clearUserPref("browser.sessionhistory.cache_subframes");
         } catch (e) { /* Pref didn't exist, meh */ }
       }
     }
 
-    function testSteps()
+    function* testSteps()
     {
       var iframe = $("iframe");
       setCachePref(true);
       window.onmessage = grabEventAndContinueHandler;
 
       iframe.src = "bfcache_iframe1.html";
       var event = yield undefined;
       is(event.data, "go", "set up database successfully");
@@ -48,17 +48,16 @@
       is(res.version, 2, "version was set correctly");
       is(res.storeCount, 1, "correct set of stores");
       ok(!("blockedFired" in res), "blocked shouldn't fire");
       is(res.value, JSON.stringify({ hello: "world" }),
          "correct value found in store");
 
       setCachePref(false);
       finishTest();
-      yield undefined;
     }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();">
   <iframe id="iframe"></iframe>
--- a/dom/indexedDB/test/test_blob_archive.html
+++ b/dom/indexedDB/test/test_blob_archive.html
@@ -6,17 +6,17 @@
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
 
-  function testSteps()
+  function* testSteps()
   {
     const BLOB_DATA =
       "504B03040A00000000002E6BF14000000000000000000000000005001C00746573742F" +
       "555409000337CA055039CA055075780B000104E803000004E8030000504B0304140000" +
       "0008002D6BF1401780E15015000000580200000A001C00746573742F612E7478745554" +
       "09000336CA05503ACA055075780B000104E803000004E8030000CB48CDC9C95728CF2F" +
       "CA49E1CA18658FB2A9C40600504B03040A00000000002F88EC40662E84701000000010" +
       "0000000A001C00746573742F622E74787455540900035A65FF4F42C5055075780B0001" +
@@ -110,17 +110,16 @@
     fileReader.onload = grabEventAndContinueHandler;
     fileReader.onerror = errorHandler;
     event = yield undefined;
 
     // Don't use is() because it prints out a newline...
     ok(event.target.result == TEST_FILE_2_CONTENTS, "Correct text");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_blob_simple.html
+++ b/dom/indexedDB/test/test_blob_simple.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     info("Setting up test fixtures: create an IndexedDB database and object store.");
 
     let request = indexedDB.open(window.location.pathname, 1);
     request.onerror = errorHandler;
     request.onupgradeneeded = grabEventAndContinueHandler;
     request.onsuccess = unexpectedSuccessHandler;
     let event = yield undefined;
@@ -264,17 +264,16 @@
     yield undefined;
 
     URL.revokeObjectURL(blobURL);
 
     is(xhr.responseText, BLOB_DATA.join(""), "Correct responseText");
 
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_blob_worker_crash.html
+++ b/dom/indexedDB/test/test_blob_worker_crash.html
@@ -15,17 +15,17 @@
    * worker and the database has already been shutdown, that there is no crash
    * when the owning page gets cleaned up which causes the termination of the
    * worker which in turn garbage collects during its shutdown.
    *
    * We do the IndexedDB stuff in the iframe so we can kill it as part of our
    * test.  Doing it out here is no good.
    */
 
-  function testSteps()
+  function* testSteps()
   {
     info("Open iframe, wait for it to do its IndexedDB stuff.");
 
     let iframe = document.getElementById("iframe1");
     window.addEventListener("message", grabEventAndContinueHandler, false);
     // Put it in a different origin to be safe
     iframe.src = //"http://example.org" +
                  window.location.pathname.replace(
@@ -44,17 +44,16 @@
     iframe = null;
     scheduleGC();
     yield undefined;
 
     info("If we are still alive, then we win!");
     ok('Did not crash / trigger an assert!');
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
   <iframe id="iframe1"></iframe>
--- a/dom/indexedDB/test/test_blob_worker_xhr_post.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_post.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
 
     let request = indexedDB.open(window.location.pathname, 1);
@@ -96,17 +96,16 @@
     event = yield undefined;
 
     is(event.data.status, 404, "XHR generated the expected 404");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
@@ -11,17 +11,17 @@
 
   <script type="text/javascript;version=1.7">
   /**
    * Create a composite/multi-file Blob on the worker, then post it as an XHR
    * payload and ensure that we don't hang/generate an assertion/etc. but
    * instead generate the expected 404.  This test is basically the same as
    * test_blob_worker_xhr_post.html except for the composite Blob.
    */
-  function testSteps()
+  function* testSteps()
   {
     const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
 
     let request = indexedDB.open(window.location.pathname, 1);
@@ -96,17 +96,16 @@
     event = yield undefined;
 
     is(event.data.status, 404, "XHR generated the expected 404");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_blob_worker_xhr_read.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_read.html
@@ -10,17 +10,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   /**
    * Create an IndexedDB-backed Blob, send it to the worker, try and read the
    * contents of the Blob from the worker using an XHR.  Ideally, we don't
    * deadlock the main thread.
    */
-  function testSteps()
+  function* testSteps()
   {
     const BLOB_DATA = ["Green"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
 
     let request = indexedDB.open(window.location.pathname, 1);
@@ -97,17 +97,16 @@
     event = yield undefined;
 
     is(event.data.data, "Green", "XHR returned expected payload.");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
@@ -11,17 +11,17 @@
 
   <script type="text/javascript;version=1.7">
   /**
    * Create an IndexedDB-backed Blob, send it to the worker, try and read the
    * *SLICED* contents of the Blob from the worker using an XHR.  This is
    * (as of the time of writing this) basically the same as
    * test_blob_worker_xhr_read.html but with slicing added.
    */
-  function testSteps()
+  function* testSteps()
   {
     const BLOB_DATA = ["Green"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
 
     let request = indexedDB.open(window.location.pathname, 1);
@@ -99,17 +99,16 @@
     event = yield undefined;
 
     is(event.data.data, "Gre", "XHR returned expected sliced payload.");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_file_array.html
+++ b/dom/indexedDB/test/test_file_array.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const b1 = getRandomBlob(10000);
 
     const b2 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(12000),
@@ -69,17 +69,16 @@
 
       verifyBlobArray(event.target.result, data.blobs, data.expectedFileIds);
       yield undefined;
     }
 
     is(bufferCache.length, 11, "Correct length");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_cross_database_copying.html
+++ b/dom/indexedDB/test/test_file_cross_database_copying.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const databaseInfo = [
       { name: window.location.pathname + "1" },
       { name: window.location.pathname + "2" }
     ];
 
@@ -90,17 +90,16 @@
       yield undefined;
 
       isnot(getFilePath(result), getFilePath(refResult), "Different os files");
     }
 
     is(bufferCache.length, 2, "Correct length");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_delete.html
+++ b/dom/indexedDB/test/test_file_delete.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const fileData1 = { key: 1, file: getRandomFile("random1.bin", 110000) };
@@ -119,17 +119,16 @@
     scheduleGC();
     yield undefined;
 
     ok(!hasFileInfo(name, 1), "Correct ref count");
     ok(!hasFileInfo(name, 2), "Correct ref count");
     ok(!hasFileInfo(name, 3), "Correct ref count");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_os_delete.html
+++ b/dom/indexedDB/test/test_file_os_delete.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     getUsage(grabFileUsageAndContinueHandler);
@@ -91,17 +91,16 @@
     flushPendingFileDeletions();
 
     getUsage(grabFileUsageAndContinueHandler);
     let endUsage = yield undefined;
 
     is(endUsage, startUsage, "OS files deleted");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_put_get_object.html
+++ b/dom/indexedDB/test/test_file_put_get_object.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const blob = getRandomBlob(1000);
@@ -72,17 +72,16 @@
 
     verifyBlob(result.foo, file, 2);
     yield undefined;
 
     verifyBlob(result.bar, file, 2);
     yield undefined;
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_put_get_values.html
+++ b/dom/indexedDB/test/test_file_put_get_values.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const blobData = { key: 1, blob: getRandomBlob(10000) };
@@ -86,17 +86,16 @@
     event = yield undefined;
 
     verifyBlob(event.target.result, fileData.file, 2);
     yield undefined;
 
     is(bufferCache.length, 2, "Correct length");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_replace.html
+++ b/dom/indexedDB/test/test_file_replace.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const blobData = { key: 42, blobs: [] };
 
     for (let i = 0; i < 100; i++) {
@@ -52,17 +52,16 @@
       let dbRefs = {};
       let hasFileInfo = utils.getFileReferences(name, id, null, refs, dbRefs);
       ok(hasFileInfo, "Has file info");
       is(refs.value, 1, "Correct ref count");
       is(dbRefs.value, id / 100 >> 0, "Correct db ref count");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_resurrection_delete.html
+++ b/dom/indexedDB/test/test_file_resurrection_delete.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
@@ -115,17 +115,16 @@
     }
 
     scheduleGC();
     yield undefined;
 
     is(getFileRefCount(name, 1), 0, "Correct ref count");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_resurrection_transaction_abort.html
+++ b/dom/indexedDB/test/test_file_resurrection_transaction_abort.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
@@ -74,17 +74,16 @@
     }
 
     scheduleGC();
     yield undefined;
 
     is(getFileRefCount(name, 1), 0, "Correct ref count");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_sharing.html
+++ b/dom/indexedDB/test/test_file_sharing.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreInfo = [
       { name: "Blobs", options: { } },
       { name: "Other Blobs", options: { } }
@@ -85,17 +85,16 @@
       yield undefined;
 
       is(getFilePath(result), getFilePath(refResult), "The same os file");
     }
 
     is(bufferCache.length, 2, "Correct length");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_file_transaction_abort.html
+++ b/dom/indexedDB/test/test_file_transaction_abort.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
@@ -59,17 +59,16 @@
     }
 
     scheduleGC();
     yield undefined;
 
     ok(!hasFileInfo(name, 1), "Correct ref count");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_compat.html
+++ b/dom/indexedDB/test/test_filehandle_compat.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -35,17 +35,16 @@
 
     request = lockedFile.getMetadata({ size: true });
     ok(request.lockedFile === lockedFile, "Correct property");
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_disabled_pref.html
+++ b/dom/indexedDB/test/test_filehandle_disabled_pref.html
@@ -8,17 +8,17 @@
 
     <script type="text/javascript"
             src="/tests/SimpleTest/SimpleTest.js">
     </script>
 
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
     <script type="text/javascript;version=1.7">
-      function testSteps()
+      function* testSteps()
       {
         const databaseName = window.location.pathname;
         const databaseVersion = 1;
         const objectStoreName = "foo";
         const mutableFileName = "bar";
         const mutableFileKey = 42;
 
         info("opening database");
@@ -185,17 +185,16 @@
         event = yield undefined;
 
         info("resetting file handle pref");
 
         SpecialPowers.popPrefEnv(continueToNextStep);
         yield undefined;
 
         finishTest();
-        yield undefined;
       }
     </script>
 
     <script type="text/javascript;version=1.7" src="helpers.js"></script>
     <script type="text/javascript;version=1.7" src="file.js"></script>
 
   </head>
 
--- a/dom/indexedDB/test/test_filehandle_getFile.html
+++ b/dom/indexedDB/test/test_filehandle_getFile.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -36,17 +36,16 @@
     ok(!('fileHandle' in request), "Property should not exist");
     ok(request.fileHandle === undefined, "Property should not exist");
     ok(!('lockedFile' in request), "Property should not exist");
     ok(request.lockedFile === undefined, "Property should not exist");
     ok(!('onprogress' in request), "Property should not exist");
     ok(request.onprogress === undefined, "Property should not exist");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_iteration.html
+++ b/dom/indexedDB/test/test_filehandle_iteration.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const dbName = window.location.pathname;
     const dbVersion = 1;
     const objectStoreName = "foo";
     const entryCount = 10;
 
     let request = indexedDB.open(dbName, dbVersion);
     request.onerror = errorHandler;
@@ -60,17 +60,16 @@
         continueToNextStep();
       }
     }
     yield undefined;
 
     is(seenEntryCount, entryCount, "Correct entry count");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_lifetimes.html
+++ b/dom/indexedDB/test/test_filehandle_lifetimes.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -40,17 +40,16 @@
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
       is(e.name, "FileHandleInactiveError", "Good error.");
       is(e.code, 0, "Good error code.");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html
+++ b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -52,17 +52,16 @@
     }
 
     ok(fileHandle2, "Non-null fileHandle2");
 
     continueToNextStep();
     yield undefined;
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_location.html
+++ b/dom/indexedDB/test/test_filehandle_location.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -87,17 +87,16 @@
 
     request = fileHandle.truncate(0);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(fileHandle.location, 0, "Correct location");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_ordering.html
+++ b/dom/indexedDB/test/test_filehandle_ordering.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -45,17 +45,16 @@
     let fileHandle3 = mutableFile.open("readonly");
     let request3 = fileHandle3.readAsText(1);
     request3.onsuccess = grabEventAndContinueHandler;
 
     event = yield undefined;
     is(event.target.result, "2", "File handles were ordered properly.");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_overlapping.html
+++ b/dom/indexedDB/test/test_filehandle_overlapping.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -56,17 +56,16 @@
 
       stepNumber++;
       yield undefined; yield undefined; yield undefined;;
 
       is(stepNumber, 4, "All callbacks received");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_progress_events.html
+++ b/dom/indexedDB/test/test_filehandle_progress_events.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     var testBuffer = getRandomBuffer(100000);
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
@@ -61,17 +61,16 @@
       sum += event.loaded - sum;
     }
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(sum, testBuffer.byteLength, "Correct loaded progress sum");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html
+++ b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -64,17 +64,16 @@
       mutableFile.open().flush({});
       ok(false, "Flushing a readonly file handle should fail!");
     }
     catch (e) {
       ok(true, "Flushing a readonly file handle failed");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_request_readyState.html
+++ b/dom/indexedDB/test/test_filehandle_request_readyState.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     is(request.readyState, "pending", "Correct readyState");
 
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
@@ -52,17 +52,16 @@
     request.onsuccess = grabEventAndContinueHandler;
     is(request.readyState, "pending", "Correct readyState");
     event = yield undefined;
 
     ok(event.target.result, "Got something");
     is(request.readyState, "done", "Correct readyState");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_serialization.html
+++ b/dom/indexedDB/test/test_filehandle_serialization.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const databaseInfo = [
       { name: window.location.pathname + "1" },
       { name: window.location.pathname + "2" }
     ];
 
@@ -83,17 +83,16 @@
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
       is(e.name, "DataCloneError", "Good error.");
       is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_store_snapshot.html
+++ b/dom/indexedDB/test/test_filehandle_store_snapshot.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const READ_WRITE = "readwrite";
 
     const name = window.location.pathname;
 
     const objectStoreName = "Blobs";
 
     const testFile = getRandomFile("random.bin", 100000);
@@ -80,17 +80,16 @@
     request = objectStore.get(42);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     verifyBlob(event.target.result, testFile, 2);
     yield undefined;
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_stream_tracking.html
+++ b/dom/indexedDB/test/test_filehandle_stream_tracking.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     var testBuffer = getRandomBuffer(100000);
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
@@ -94,17 +94,16 @@
     }
 
     yield undefined;
 
     ok(compareBuffers(resultBuffer1, testBuffer), "Correct data");
     ok(compareBuffers(resultBuffer2, testBuffer), "Correct data");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html
+++ b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
@@ -57,17 +57,16 @@
     var thread = comp.classes["@mozilla.org/thread-manager;1"]
                      .getService(comp.interfaces.nsIThreadManager)
                      .currentThread;
     while (thread.hasPendingEvents()) {
       thread.processNextEvent(false);
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_filehandle_truncate.html
+++ b/dom/indexedDB/test/test_filehandle_truncate.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     var testBuffer = getRandomBuffer(100000);
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
@@ -73,17 +73,16 @@
       request = fileHandle.getMetadata({ size: true });
       request.onsuccess = grabEventAndContinueHandler;
       event = yield undefined;
 
       is(event.target.result.size, location, "Correct size");
     }
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_workers.html
+++ b/dom/indexedDB/test/test_filehandle_workers.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     let testBuffer = getRandomBuffer(100000);
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onupgradeneeded = grabEventAndContinueHandler;
@@ -131,17 +131,16 @@
     worker.postMessage(name);
     worker.onmessage = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(event.data, "success", "Good response.");
     worker.terminate();
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_filehandle_write_read_data.html
+++ b/dom/indexedDB/test/test_filehandle_write_read_data.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>File Handle Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
     for (let i = 0; i < 5; i++) {
       testString += testString;
     }
 
@@ -92,17 +92,16 @@
     request = fileHandle.getMetadata({ size: true });
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     let result = event.target.result;
     is(result.size, location, "Correct size");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
--- a/dom/indexedDB/test/test_globalObjects_chrome.xul
+++ b/dom/indexedDB/test/test_globalObjects_chrome.xul
@@ -8,17 +8,17 @@
 <window title="Mozilla Bug 832883"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTest();">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript;version=1.7">
   <![CDATA[
-  function testSteps() {
+  function* testSteps() {
     const name = window.location.pathname;
 
     // Test for IDBKeyRange and indexedDB availability in chrome windows.
     var keyRange = IDBKeyRange.only(42);
     ok(keyRange, "Got keyRange");
 
     var request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_globalObjects_content.html
+++ b/dom/indexedDB/test/test_globalObjects_content.html
@@ -5,34 +5,33 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     const name = window.location.pathname;
 
     // Test for IDBKeyRange and indexedDB availability in content windows.
     let keyRange = IDBKeyRange.only(42);
     ok(keyRange, "Got keyRange");
 
     let request = indexedDB.open(name, 1);
     request.onerror = errorHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     let db = event.target.result;
     ok(db, "Got database");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 </head>
 
 <body onload="runTest();"></body>
 
 </html>
--- a/dom/indexedDB/test/test_globalObjects_other.xul
+++ b/dom/indexedDB/test/test_globalObjects_other.xul
@@ -8,17 +8,17 @@
 <window title="Mozilla Bug 832883"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTest();">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript;version=1.7">
   <![CDATA[
-  function testSteps() {
+  function* testSteps() {
     // Test for IDBKeyRange and indexedDB availability in bootstrap files.
     let test = Cc["@mozilla.org/dom/indexeddb/GlobalObjectsComponent;1"].
                createInstance(Ci.nsISupports).wrappedJSObject;
     test.ok = ok;
     test.finishTest = continueToNextStep;
     test.runTest();
     yield undefined;
 
--- a/dom/indexedDB/test/test_leaving_page.html
+++ b/dom/indexedDB/test/test_leaving_page.html
@@ -14,17 +14,17 @@
   <iframe id="inner"></iframe>
   <a id="a" href="leaving_page_iframe.html"></a>
 
   <script type="text/javascript;version=1.7">
     onmessage = function(e) {
       ok(false, "gotmessage: " + e.data);
     }
 
-    function testSteps()
+    function* testSteps()
     {
       var iframe = $("inner");
       iframe.src = "leaving_page_iframe.html";
       iframe.onload = continueToNextStep;
       yield undefined;
       is(iframe.contentWindow.location.href, $("a").href,
          "should navigate to iframe page");
       yield undefined;
@@ -37,13 +37,12 @@
 
       let db = event.target.result;
       db.transaction(["mystore"]).objectStore("mystore").get(42).onsuccess =
         grabEventAndContinueHandler;
       event = yield undefined;
       is(event.target.result.hello, "world", "second modification rolled back");
 
       finishTest();
-      yield undefined;
     }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 </html>
--- a/dom/indexedDB/test/test_message_manager_ipc.html
+++ b/dom/indexedDB/test/test_message_manager_ipc.html
@@ -46,27 +46,27 @@ function childFrameScript() {
     ok(pass, name, diag);
   }
 
   function finish(result) {
     sendAsyncMessage(mmName, { op: "done", result: result });
   }
 
   function grabAndContinue(arg) {
-    testGenerator.send(arg);
+    testGenerator.next(arg);
   }
 
   function errorHandler(event) {
     ok(false,
        event.target + " received error event: '" + event.target.error.name +
        "'");
     finish();
   }
 
-  function testSteps() {
+  function* testSteps() {
     addMessageListener(mmName, grabAndContinue);
     let message = yield undefined;
 
     let blob = message.data;
 
     ok(blob instanceof Ci.nsIDOMBlob, "Message manager sent a blob");
     is(blob.size, blobText.length, "Blob has correct length");
     is(blob.type, blobType, "Blob has correct type");
@@ -190,36 +190,34 @@ function childFrameScript() {
     reader.readAsText(slice);
 
     yield undefined;
 
     is(reader.result, blobData[0], "Slice has correct data");
 
     info("Sending blob and slice from database to message manager");
     finish([blob, slice]);
-
-    yield undefined;
   }
 
   let testGenerator = testSteps();
   testGenerator.next();
 }
 
 function parentFrameScript(mm) {
   const messageName = "test:idb-and-mm";
   const blobData = ["So", " ", "many", " ", "blobs!"];
   const blobText = blobData.join("");
   const blobType = "text/plain";
   const blob = new Blob(blobData, { type: blobType });
 
   function grabAndContinue(arg) {
-    testGenerator.send(arg);
+    testGenerator.next(arg);
   }
 
-  function testSteps() {
+  function* testSteps() {
     let result = yield undefined;
 
     is(Array.isArray(result), true, "Child delivered an array of results");
     is(result.length, 2, "Child delivered two results");
 
     let blob = result[0];
     is(blob instanceof Blob, true, "Child delivered a blob");
     is(blob.size, blobText.length, "Blob has correct size");
@@ -259,17 +257,16 @@ function parentFrameScript(mm) {
     reader = new FileReader();
     reader.onload = grabAndContinue;
     reader.readAsText(slice);
     yield undefined;
 
     is(reader.result, blobData[0], "Second slice has correct data");
 
     SimpleTest.finish();
-    yield undefined;
   }
 
   let testGenerator = testSteps();
   testGenerator.next();
 
   mm.addMessageListener(messageName, function(message) {
     let data = message.data;
     switch (data.op) {
@@ -279,17 +276,17 @@ function parentFrameScript(mm) {
       }
 
       case "ok": {
         ok(data.condition, data.name, data.diag);
         break;
       }
 
       case "done": {
-        testGenerator.send(data.result);
+        testGenerator.next(data.result);
         break;
       }
 
       default: {
         ok(false, "Unknown op: " + data.op);
         SimpleTest.finish();
       }
     }
--- a/dom/indexedDB/test/test_open_for_principal.html
+++ b/dom/indexedDB/test/test_open_for_principal.html
@@ -5,26 +5,25 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-  function testSteps()
+  function* testSteps()
   {
     is("open" in indexedDB, true, "open() defined");
     is("openForPrincipal" in indexedDB, false, "openForPrincipal() not defined");
 
     is("deleteDatabase" in indexedDB, true, "deleteDatabase() defined");
     is("deleteForPrincipal" in indexedDB, false, "deleteForPrincipal() not defined");
 
     finishTest();
-    yield undefined;
   }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/test_persistenceType.html
+++ b/dom/indexedDB/test/test_persistenceType.html
@@ -5,17 +5,17 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
-    function testSteps()
+    function* testSteps()
     {
       const name = window.location.pathname;
       const version = 1;
       const storages = ["persistent", "temporary", "default"];
 
       const objectStoreName = "Foo";
       const data = { key: 1, value: "bar" };
 
@@ -76,17 +76,16 @@
         request = objectStore.add(data.value, data.key);
         request.onsuccess = grabEventAndContinueHandler;
         event = yield undefined;
 
         is(event.target.result, data.key, "Got correct key");
       }
 
       finishTest();
-      yield undefined;
     }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
 
 <body onload="runTest();"></body>
 
--- a/dom/indexedDB/test/unit/test_abort_deleted_index.js
+++ b/dom/indexedDB/test/unit/test_abort_deleted_index.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const storeName = "test store";
   const indexName_ToBeDeleted = "test index to be deleted";
 
   info("Create index in v1.");
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
@@ -69,10 +69,9 @@ function testSteps()
     index.get('foo');
     ok(false, "TransactionInactiveError shall be thrown after the transaction is inactive.");
   } catch (e) {
     ok(e instanceof DOMException, "got a database exception");
     is(e.name, "TransactionInactiveError", "TransactionInactiveError shall be thrown after the transaction is inactive.");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js
+++ b/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const storeName_ToBeDeleted = "test store to be deleted";
 
   info("Create objectStore in v1.");
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
@@ -65,10 +65,9 @@ function testSteps()
     objectStore.get('foo');
     ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive.");
   } catch (e) {
     ok(e instanceof DOMException, "got a database exception");
     is(e.name, "TransactionInactiveError", "correct error");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_add_put.js
+++ b/dom/indexedDB/test/unit/test_add_put.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   let openRequest = indexedDB.open(name, 1);
   openRequest.onerror = errorHandler;
   openRequest.onupgradeneeded = grabEventAndContinueHandler;
   openRequest.onsuccess = unexpectedSuccessHandler;
   let event = yield undefined;
   let db = event.target.result;
@@ -156,10 +156,9 @@ function testSteps()
 
     return "success";
   }
 
   openRequest.onsuccess = grabEventAndContinueHandler;
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_add_twice_failure.js
+++ b/dom/indexedDB/test/unit/test_add_twice_failure.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield undefined;
@@ -33,11 +33,10 @@ function testSteps()
   request.addEventListener("error", new ExpectError("ConstraintError", true));
   request.onsuccess = unexpectedSuccessHandler;
   yield undefined;
 
   // Wait for success.
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
 
--- a/dom/indexedDB/test/unit/test_advance.js
+++ b/dom/indexedDB/test/unit/test_advance.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const dataCount = 30;
 
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
 
@@ -183,10 +183,9 @@ function testSteps()
       continueToNextStep();
     }
   };
   yield undefined;
 
   is(count, dataCount - 1, "Saw all data");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_autoIncrement.js
+++ b/dom/indexedDB/test/unit/test_autoIncrement.js
@@ -28,17 +28,17 @@ function genCheck(key, value, test, opti
     event.target.source.get(key).onsuccess = function(event) {
       is(JSON.stringify(event.target.result), JSON.stringify(value),
          "correct stored value in " + test);
       continueToNextStepSync();
     }
   }
 }
 
-function testSteps()
+function* testSteps()
 {
   const dbname = this.window ? window.location.pathname : "Splendid Test";
   const RW = "readwrite";
   let c1 = 1;
   let c2 = 1;
 
   let openRequest = indexedDB.open(dbname, 1);
   openRequest.onerror = errorHandler;
@@ -391,10 +391,9 @@ function testSteps()
     genCheck(c2, { reopen: 2, id: c2 }, "second" + test);
   c2++;
   yield undefined; yield undefined;
 
   openRequest.onsuccess = grabEventAndContinueHandler;
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_autoIncrement_indexes.js
+++ b/dom/indexedDB/test/unit/test_autoIncrement_indexes.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
 
   let db = request.result;
   db.onerror = errorHandler;
@@ -47,10 +47,9 @@ function testSteps()
   is (event.target.result.id, 1, "Entry in second");
 
   third.get("foo").onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   is (event.target.result.id, 1, "Entry in third");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_blob_file_backed.js
+++ b/dom/indexedDB/test/unit/test_blob_file_backed.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var disableWorkerTest = "This test uses SpecialPowers";
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const fileData = "abcdefghijklmnopqrstuvwxyz";
   const fileType = "text/plain";
 
   const databaseName =
     ("window" in this) ? window.location.pathname : "Test";
   const objectStoreName = "foo";
   const objectStoreKey = "10";
@@ -69,10 +69,9 @@ function testSteps()
   fileReader.onload = grabEventAndContinueHandler;
   fileReader.readAsText(file);
 
   event = yield undefined;
 
   is(fileReader.result, fileData, "Correct data");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_blocked_order.js
+++ b/dom/indexedDB/test/unit/test_blocked_order.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const databaseName =
     ("window" in this) ? window.location.pathname : "Test";
   const databaseCount = 10;
 
   // Test 1: Make sure basic versionchange events work and that they don't
   //         trigger blocked events.
   info("Opening " + databaseCount + " databases with version 1");
@@ -170,10 +170,9 @@ function testSteps()
 
   request.onblocked = grabEventAndContinueHandler;
 
   event = yield undefined;
   ok(true, "Got blocked");
   // Just allow this to remain blocked ...
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_cleanup_transaction.js
+++ b/dom/indexedDB/test/unit/test_cleanup_transaction.js
@@ -72,21 +72,21 @@ function testSteps()
       request.onerror = function(event)
       {
         event.stopPropagation();
       }
 
       trans.oncomplete = function(event) {
         i++;
         j++;
-        testGenerator.send(true);
+        testGenerator.next(true);
       }
       trans.onabort = function(event) {
         is(trans.error.name, "QuotaExceededError", "Reached quota limit");
-        testGenerator.send(false);
+        testGenerator.next(false);
       }
 
       let completeFired = yield undefined;
       if (completeFired) {
         ok(true, "Got complete event");
       } else {
         ok(true, "Got abort event");
 
--- a/dom/indexedDB/test/unit/test_clear.js
+++ b/dom/indexedDB/test/unit/test_clear.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const entryCount = 1000;
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
@@ -88,10 +88,9 @@ function testSteps()
               .add({});
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   isnot(event.target.result, firstKey, "Got a different key");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_complex_keyPaths.js
+++ b/dom/indexedDB/test/unit/test_complex_keyPaths.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   // Test object stores
 
   const name = "test_complex_keyPaths";
   const keyPaths = [
     { keyPath: "id",      value: { id: 5 },                      key: 5 },
     { keyPath: "id",      value: { id: "14", iid: 12 },          key: "14" },
     { keyPath: "id",      value: { iid: "14", id: 12 },          key: 12 },
@@ -257,10 +257,9 @@ function testSteps()
     is(JSON.stringify(e.target.result), JSON.stringify(info.res || info.v),
        "expected value stored" + test);
   }
 
   openRequest.onsuccess = grabEventAndContinueHandler;
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_count.js
+++ b/dom/indexedDB/test/unit/test_count.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
     { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
@@ -345,10 +345,9 @@ function testSteps()
                                true, true);
   index.count(keyRange).onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   is(event.target.result, weightSort.length - 2,
      "Correct number of index entries for bound keyRange");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_create_index.js
+++ b/dom/indexedDB/test/unit/test_create_index.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreInfo = [
     { name: "a", options: { keyPath: "id", autoIncrement: true } },
     { name: "b", options: { keyPath: "id", autoIncrement: false } },
   ];
   const indexInfo = [
     { name: "1", keyPath: "unique_value", options: { unique: true } },
@@ -112,10 +112,9 @@ function testSteps()
   }
 
   request.onsuccess = grabEventAndContinueHandler;
   request.onupgradeneeded = unexpectedSuccessHandler;
 
   event = yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_create_index_with_integer_keys.js
+++ b/dom/indexedDB/test/unit/test_create_index_with_integer_keys.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const data = { id: new Date().getTime(),
                  num: parseInt(Math.random() * 1000) };
 
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   let event = yield undefined;
@@ -57,10 +57,9 @@ function testSteps()
       continueToNextStep();
     }
   };
   yield undefined;
 
   is(seenCount, 1, "Saw our entry");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_create_locale_aware_index.js
+++ b/dom/indexedDB/test/unit/test_create_locale_aware_index.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreInfo = [
     { name: "a", options: { keyPath: "id", autoIncrement: true } },
     { name: "b", options: { keyPath: "id", autoIncrement: false } },
   ];
   const indexInfo = [
     { name: "1", keyPath: "unique_value", options: { unique: true, locale: "es-ES" } },
@@ -114,10 +114,9 @@ function testSteps()
   }
 
   request.onsuccess = grabEventAndContinueHandler;
   request.onupgradeneeded = unexpectedSuccessHandler;
 
   event = yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_create_objectStore.js
+++ b/dom/indexedDB/test/unit/test_create_objectStore.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreInfo = [
     { name: "1", options: { keyPath: null } },
     { name: "2", options: { keyPath: null, autoIncrement: true } },
     { name: "3", options: { keyPath: null, autoIncrement: false } },
     { name: "4", options: { keyPath: null } },
     { name: "5", options: { keyPath: "foo" } },
@@ -125,10 +125,9 @@ function testSteps()
   is(ex.code, DOMException.INVALID_ACCESS_ERR, "should throw right exception");
 
   request.onsuccess = grabEventAndContinueHandler;
   request.onupgradeneeded = unexpectedSuccessHandler;
 
   event = yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_cursor_mutation.js
+++ b/dom/indexedDB/test/unit/test_cursor_mutation.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const objectStoreData = [
     // This one will be removed.
     { ss: "237-23-7732", name: "Bob" },
 
     // These will always be included.
     { ss: "237-23-7733", name: "Ann" },
     { ss: "237-23-7734", name: "Ron" },
@@ -108,11 +108,9 @@ function testSteps()
 
   is(count, objectStoreData.length - 1, "Good final count");
   is(sawAdded, true, "Saw item that was added");
   is(sawRemoved, false, "Didn't see item that was removed");
 
   finishTest();
 
   objectStore = null; // Bug 943409 workaround.
-
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js
+++ b/dom/indexedDB/test/unit/test_cursor_update_updates_indexes.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const START_DATA = "hi";
   const END_DATA = "bye";
   const objectStoreInfo = [
     { name: "1", options: { keyPath: null }, key: 1,
       entry: { data: START_DATA } },
     { name: "2", options: { keyPath: "foo" },
@@ -89,11 +89,10 @@ function testSteps()
 
     // Wait for success
     yield undefined;
 
     db.close();
   }
 
   finishTest();
-  yield undefined;
 }
 
--- a/dom/indexedDB/test/unit/test_cursors.js
+++ b/dom/indexedDB/test/unit/test_cursors.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const keys = [1, -1, 0, 10, 2000, "q", "z", "two", "b", "a"];
   const sortedKeys = [-1, 0, 1, 10, 2000, "a", "b", "q", "two", "z"];
 
   is(keys.length, sortedKeys.length, "Good key setup");
 
   let request = indexedDB.open(name, 1);
@@ -374,10 +374,9 @@ function testSteps()
   is(keyIndex, -1, "Saw all added items");
 
   // Wait for success
   yield undefined;
 
   db.close();
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_database_onclose.js
+++ b/dom/indexedDB/test/unit/test_database_onclose.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   function testInvalidStateError(aDb, aTxn) {
     try {
       info("The db shall become invalid after closed.");
       aDb.transaction("store");
       ok(false, "InvalidStateError shall be thrown.");
     } catch (e) {
       ok(e instanceof DOMException, "got a database exception");
@@ -100,20 +100,20 @@ function testSteps()
   objectStore = txn.objectStore("store");
 
   let objectId = 0;
   while(true) {
     let addRequest = objectStore.add({foo: "foo"}, objectId);
     addRequest.onerror = function(event) {
       info("addRequest.onerror, objectId: " + objectId);
       txn.onerror = grabEventAndContinueHandler;
-      testGenerator.send(true);
+      testGenerator.next(true);
     }
     addRequest.onsuccess = function() {
-      testGenerator.send(false);
+      testGenerator.next(false);
     }
 
     if (objectId == 0) {
       clearAllDatabases(() => {
         info("clearAllDatabases is done.");
         continueToNextStep();
       });
     }
@@ -165,17 +165,17 @@ function testSteps()
   // The number of read records varies between 1~2000 before the db is cleared
   // during testing.
   let numberOfObjects = 3000;
   objectId = 0;
   while(true) {
     let addRequest = objectStore.add({foo: "foo"});
     addRequest.onsuccess = function() {
       objectId++;
-      testGenerator.send(objectId == numberOfObjects);
+      testGenerator.next(objectId == numberOfObjects);
     }
     addRequest.onerror = errorHandler;
 
     let done = yield undefined;
     if (done) {
       break;
     }
   }
@@ -191,27 +191,27 @@ function testSteps()
 
   txn = db.transaction("store");
   objectStore = txn.objectStore("store");
 
   let numberOfReadObjects = 0;
   let readRequest = objectStore.openCursor();
   readRequest.onerror = function(event) {
     info("readRequest.onerror, numberOfReadObjects: " + numberOfReadObjects);
-    testGenerator.send(true);
+    testGenerator.next(true);
   }
   readRequest.onsuccess = function(event) {
     let cursor = event.target.result;
     if (cursor) {
       numberOfReadObjects++;
       event.target.result.continue();
     } else {
       info("Cursor is invalid, numberOfReadObjects: " + numberOfReadObjects);
       todo(false, "All records are iterated before database is cleared!");
-      testGenerator.send(false);
+      testGenerator.next(false);
     }
   }
 
   clearAllDatabases(() => {
     info("clearAllDatabases is done.");
     continueToNextStep();
   });
 
@@ -236,10 +236,9 @@ function testSteps()
 
     testInvalidStateError(db, txn);
   }
 
   info("Wait for the callback of clearAllDatabases().");
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_deleteDatabase.js
+++ b/dom/indexedDB/test/unit/test_deleteDatabase.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   ok(indexedDB.deleteDatabase, "deleteDatabase function should exist!");
 
   let request = indexedDB.open(name, 10);
   request.onerror = errorHandler;
   request.onsuccess = unexpectedSuccessHandler;
@@ -97,10 +97,9 @@ function testSteps()
   request = indexedDB.open("thisDatabaseHadBetterNotExist");
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   event = yield undefined;
   ok(true, "after deleting a non-existent database, open should work");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_deleteDatabase_interactions.js
+++ b/dom/indexedDB/test/unit/test_deleteDatabase_interactions.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   let request = indexedDB.open(name, 10);
   request.onerror = errorHandler;
   request.onsuccess = unexpectedSuccessHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
 
@@ -53,10 +53,9 @@ function testSteps()
 
   openRequest.onsuccess = grabEventAndContinueHandler;
 
   event = yield undefined;
   is(event.target.result.version, 1, "DB has proper version");
   is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js
+++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const dbVersion = 10;
 
   let openRequest = indexedDB.open(name, dbVersion);
   openRequest.onerror = errorHandler;
   openRequest.onblocked = errorHandler;
   openRequest.onsuccess = unexpectedSuccessHandler;
@@ -74,10 +74,9 @@ function testSteps()
   event = yield undefined;
   db = event.target.result;
   is(db.version, 1, "DB has proper version");
   is(db.objectStoreNames.length, 0, "DB should have no object stores");
 
   db.close();
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js
+++ b/dom/indexedDB/test/unit/test_deleteDatabase_onblocked_duringVersionChange.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const dbVersion = 10;
 
   let openRequest = indexedDB.open(name, dbVersion);
   openRequest.onerror = errorHandler;
   openRequest.onblocked = errorHandler;
   openRequest.onsuccess = unexpectedSuccessHandler;
@@ -75,10 +75,9 @@ function testSteps()
   event = yield undefined;
   db = event.target.result;
   is(db.version, 1, "DB has proper version");
   is(db.objectStoreNames.length, 0, "DB should have no object stores");
 
   db.close();
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_event_source.js
+++ b/dom/indexedDB/test/unit/test_event_source.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "Objects";
 
   var request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
@@ -27,10 +27,9 @@ function testSteps()
   event = yield undefined;
 
   ok(event.target.source === objectStore, "correct event.source");
 
   // Wait for success
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_filehandle_append_read_data.js
+++ b/dom/indexedDB/test/unit/test_filehandle_append_read_data.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var disableWorkerTest = "FileHandle doesn't work in workers yet";
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
   for (let i = 0; i < 5; i++) {
     testString += testString;
   }
 
@@ -89,10 +89,9 @@ function testSteps()
   request = fileHandle.getMetadata({ size: true });
   request.onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   let result = event.target.result;
   is(result.size, location, "Correct size");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_getAll.js
+++ b/dom/indexedDB/test/unit/test_getAll.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const values = [ "a", "1", 1, "foo", 300, true, false, 4.5, null ];
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
@@ -186,10 +186,9 @@ function testSteps()
   is(event.target.result instanceof Array, true, "Got an array object");
   is(event.target.result.length, 2, "Correct length");
 
   for (let i in event.target.result) {
     is(event.target.result[i], values[parseInt(i) + 4], "Same value");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_global_data.js
+++ b/dom/indexedDB/test/unit/test_global_data.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStore =  { name: "Objects",
                          options: { keyPath: "id", autoIncrement: true } };
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
@@ -48,10 +48,9 @@ function testSteps()
   let objectStore2 = db2.transaction(objectStore.name)
                         .objectStore(objectStore.name);
 
   ok(objectStore1 !== objectStore2, "Different objectStores");
   is(objectStore1.name, objectStore2.name, "Same name");
   is(objectStore1.keyPath, objectStore2.keyPath, "Same keyPath");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_index_empty_keyPath.js
+++ b/dom/indexedDB/test/unit/test_index_empty_keyPath.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const objectStoreData = [
     { key: "1", value: "foo" },
     { key: "2", value: "bar" },
     { key: "3", value: "baz" }
   ];
@@ -28,17 +28,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined; // testGenerator.send
 
   // Now create the index.
   objectStore.createIndex("set", "", { unique: true });
   yield undefined; // success
@@ -74,10 +74,9 @@ function testSteps()
   request.onsuccess = unexpectedSuccessHandler;
 
   trans.oncomplete = grabEventAndContinueHandler;
 
   yield undefined;
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_index_getAll.js
+++ b/dom/indexedDB/test/unit/test_index_getAll.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
     { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
@@ -64,17 +64,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   yield undefined;
   ok(true, "1");
 
   // Now create the indexes.
   for (let i in indexData) {
@@ -182,10 +182,9 @@ function testSteps()
   is(event.target.result.length, 1, "Correct length");
 
   for (let i in event.target.result) {
     is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key,
        "Correct key");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_index_getAllObjects.js
+++ b/dom/indexedDB/test/unit/test_index_getAllObjects.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
     { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
@@ -64,17 +64,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined;
 
   // Now create the indexes.
   for (let i in indexData) {
     objectStore.createIndex(indexData[i].name, indexData[i].keyPath,
@@ -224,10 +224,9 @@ function testSteps()
     is(result.height, testObj.height, "Correct height");
 
     if (testObj.hasOwnProperty("weight")) {
       is(result.weight, testObj.weight, "Correct weight");
     }
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_index_object_cursors.js
+++ b/dom/indexedDB/test/unit/test_index_object_cursors.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const objectStoreData = [
     { name: "", options: { keyPath: "id", autoIncrement: true } },
     { name: null, options: { keyPath: "ss" } },
     { name: undefined, options: { } },
     { name: "4", options: { autoIncrement: true } },
   ];
 
@@ -138,10 +138,9 @@ function testSteps()
         .onsuccess = continueToNextStep;
       yield undefined;
 
       objectStore = index = null; // Bug 943409 workaround.
     }
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_index_update_delete.js
+++ b/dom/indexedDB/test/unit/test_index_update_delete.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   let name = this.window ? window.location.pathname : "Splendid Test";
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   let event = yield undefined;
@@ -162,10 +162,9 @@ function testSteps()
 
       index = event = null; // Bug 943409 workaround.
     }
     objectStore = event = null; // Bug 943409 workaround.
   }
 
   finishTest();
   event = db = request = null; // Bug 943409 workaround.
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_indexes.js
+++ b/dom/indexedDB/test/unit/test_indexes.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
@@ -64,17 +64,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined;
   // Now create the indexes.
   for (let i in indexData) {
     objectStore.createIndex(indexData[i].name, indexData[i].keyPath,
                             indexData[i].options);
@@ -1252,10 +1252,9 @@ function testSteps()
       testGenerator.next();
     }
   }
   yield undefined;
 
   is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_indexes_bad_values.js
+++ b/dom/indexedDB/test/unit/test_indexes_bad_values.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
@@ -50,17 +50,17 @@ function testSteps()
 
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined;
 
   for (let i in indexData) {
     objectStore.createIndex(indexData[i].name, indexData[i].keyPath,
                             indexData[i].options);
@@ -121,10 +121,9 @@ function testSteps()
     }
   }
   yield undefined;
 
   is(keyIndex, objectStoreData.length + badObjectStoreData.length,
      "Saw all people");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_indexes_funny_things.js
+++ b/dom/indexedDB/test/unit/test_indexes_funny_things.js
@@ -1,21 +1,21 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   // Blob constructor is not implemented outside of windows yet (Bug 827723).
   if (!this.window) {
     finishTest();
-    yield undefined;
+    return;
   }
 
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   const objectStoreName = "Things";
 
   const blob1 = new Blob(["foo", "bar"], { type: "text/plain" });
   const blob2 = new Blob(["foobazybar"], { type: "text/plain" });
@@ -60,17 +60,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined;
   // Now create the indexes.
   for (let i in indexData) {
     objectStore.createIndex(indexData[i].name, indexData[i].keyPath,
                             indexData[i].options);
@@ -159,10 +159,9 @@ function testSteps()
       testGenerator.next();
     }
   }
   yield undefined;
 
   is(keyIndex, objectStoreDataLengthSort.length, "Saw all the expected keys");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_invalid_cursor.js
+++ b/dom/indexedDB/test/unit/test_invalid_cursor.js
@@ -2,17 +2,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var disableWorkerTest = "Need to implement a gc() function for worker tests";
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const dbName = ("window" in this) ? window.location.pathname : "test";
   const dbVersion = 1;
   const objectStoreName = "foo";
   const data = 0;
 
   let req = indexedDB.open(dbName, dbVersion);
   req.onerror = errorHandler;
@@ -55,10 +55,9 @@ function testSteps()
   info("Done collecting garbage");
 
   cursor.continue();
   event = yield undefined;
 
   is(event.target.result, null, "No more entries");
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_invalid_version.js
+++ b/dom/indexedDB/test/unit/test_invalid_version.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   try {
     indexedDB.open(name, 0);
     ok(false, "Should have thrown!");
   }
   catch (e) {
@@ -41,10 +41,9 @@ function testSteps()
     ok(false, "Should have thrown!");
   }
   catch (e) {
     ok(e instanceof TypeError, "Got TypeError.");
     is(e.name, "TypeError", "Good error name.");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_key_requirements.js
+++ b/dom/indexedDB/test/unit/test_key_requirements.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps(); 
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
 
   let request = indexedDB.open(name, 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield undefined;
@@ -276,10 +276,9 @@ function testSteps()
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   event = yield undefined;
 
   // Wait for success
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_keys.js
+++ b/dom/indexedDB/test/unit/test_keys.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const dbname = this.window ? window.location.pathname : "Splendid Test";
   const RW = "readwrite"
   let c1 = 1;
   let c2 = 1;
 
   let openRequest = indexedDB.open(dbname, 1);
   openRequest.onerror = errorHandler;
@@ -260,10 +260,9 @@ function testSteps()
       is(ex.code, 0, "Threw with right code3");
     }
   }
 
   openRequest.onsuccess = grabEventAndContinueHandler;
   yield undefined;
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_locale_aware_index_getAll.js
+++ b/dom/indexedDB/test/unit/test_locale_aware_index_getAll.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "\u00E1na",   height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "ana",        height: 52, weight: 110 } },
     { key: "237-23-7734", value: { name: "fabio",      height: 73, weight: 180 } },
@@ -64,17 +64,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   yield undefined;
   ok(true, "1");
 
   // Now create the indexes.
   for (let i in indexData) {
@@ -182,10 +182,9 @@ function testSteps()
   is(event.target.result.length, 1, "Correct length");
 
   for (let i in event.target.result) {
     is(event.target.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key,
        "Correct key");
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js
+++ b/dom/indexedDB/test/unit/test_locale_aware_index_getAllObjects.js
@@ -1,16 +1,16 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var testGenerator = testSteps();
 
-function testSteps()
+function* testSteps()
 {
   const name = this.window ? window.location.pathname : "Splendid Test";
   const objectStoreName = "People";
 
   const objectStoreData = [
     { key: "237-23-7732", value: { name: "\u00E1na",   height: 60, weight: 120 } },
     { key: "237-23-7733", value: { name: "ana",        height: 52, weight: 110 } },
     { key: "237-23-7734", value: { name: "fabio",      height: 73, weight: 180 } },
@@ -64,17 +64,17 @@ function testSteps()
   // First, add all our data to the object store.
   let addedData = 0;
   for (let i in objectStoreData) {
     request = objectStore.add(objectStoreData[i].value,
                               objectStoreData[i].key);
     request.onerror = errorHandler;
     request.onsuccess = function(event) {
       if (++addedData == objectStoreData.length) {
-        testGenerator.send(event);
+        testGenerator.next(event);
       }
     }
   }
   event = yield undefined;
 
   // Now create the indexes.
   for (let i in indexData) {
     objectStore.createIndex(indexData[i].name, indexData[i].keyPath,
@@ -224,10 +224,9 @@ function testSteps()
     is(result.height, testObj.height, "Correct height");
 
     if (testObj.hasOwnProperty("weight")) {
       is(result.weight, testObj.weight, "Correct weight");
     }
   }
 
   finishTest();
-  yield undefined;
 }
--- a/dom/indexedDB/test/unit/test_locale_aware_indexes.js
+++ b/dom/indexedDB/test/unit/test_locale_aware_indexes.js
@@ -1,16 +1,16 @@